Merge "add function setTvView to link the input Session and IApp Session"
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index f8aa98e..5036abc 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -8788,7 +8788,12 @@
public static final String KEY_DEFAULT_ACCOUNT = "key_default_account";
/**
- * Return the account that was set to default account for new contacts.
+ * Get the account that is set as the default account for new contacts, which should be
+ * initially selected when creating a new contact on contact management apps.
+ *
+ * @param resolver the ContentResolver to query.
+ * @return the default account for new contacts, or null if it's not set or set to NULL
+ * account.
*/
@Nullable
public static Account getDefaultAccount(@NonNull ContentResolver resolver) {
@@ -8798,8 +8803,10 @@
}
/**
- * Set the account to be the default account for new contacts.
+ * Sets the account as the default account that should be initially selected
+ * when creating a new contact on contact management apps.
*
+ * @param resolver the ContentResolver to query.
* @param account the account to be set to default.
* @hide
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index dfc4fa7..17272d3 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -13587,7 +13587,7 @@
* Whether of not to send keycode sleep for ungaze when Home is the foreground activity on
* watch type devices.
* Type: int (0 for false, 1 for true)
- * Default: 0
+ * Default: 1
* @hide
*/
@Readable
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 12bcd8b..b5fe4f5 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -62,6 +62,10 @@
* another buffer allocation and copy, and even more pressure on the gc.
* That means that if your log message is filtered out, you might be doing
* significant work and incurring significant overhead.
+ *
+ * <p>When calling the log methods that take a Throwable parameter,
+ * if any of the throwables in the cause chain is an <code>UnknownHostException</code>,
+ * then the stack trace is not logged.
*/
public final class Log {
/** @hide */
@@ -341,6 +345,9 @@
/**
* Handy function to get a loggable stack trace from a Throwable
+
+ * <p>If any of the throwables in the cause chain is an <code>UnknownHostException</code>,
+ * this returns an empty string.
* @param tr An exception to log
*/
@NonNull
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 2ee112b..fceda20 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -96,9 +96,9 @@
/**
* Tell the window that it is either gaining or losing focus. Keep it up
- * to date on the current state showing navigational focus (touch mode) too.
+ * to date on the current state showing navigational focus too.
*/
- void windowFocusChanged(boolean hasFocus, boolean inTouchMode);
+ void windowFocusChanged(boolean hasFocus);
void closeSystemDialogs(String reason);
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index 2165f55..c9abec9 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -139,11 +139,9 @@
* @param hasFocus if true, the window associated with this input channel has just received
* focus
* if false, the window associated with this input channel has just lost focus
- * @param inTouchMode if true, the device is in touch mode
- * if false, the device is not in touch mode
*/
// Called from native code.
- public void onFocusEvent(boolean hasFocus, boolean inTouchMode) {
+ public void onFocusEvent(boolean hasFocus) {
}
/**
@@ -175,7 +173,7 @@
* exited touch mode.
*
* @param inTouchMode {@code true} if the display showing the window associated with the
- * input channel entered touch mode.
+ * input channel entered touch mode or {@code false} if left touch mode
*/
public void onTouchModeChanged(boolean inTouchMode) {
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ce96eca..89b95c0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -8705,8 +8705,8 @@
}
@Override
- public void onFocusEvent(boolean hasFocus, boolean inTouchMode) {
- windowFocusChanged(hasFocus, inTouchMode);
+ public void onFocusEvent(boolean hasFocus) {
+ windowFocusChanged(hasFocus);
}
@Override
@@ -8975,9 +8975,7 @@
/**
* Notifies this {@link ViewRootImpl} object that window focus has changed.
*/
- public void windowFocusChanged(boolean hasFocus, boolean unusedInTouchMode) {
- // TODO(b/193718270): Remove inTouchMode parameter from this method and update related code
- // accordingly.
+ public void windowFocusChanged(boolean hasFocus) {
synchronized (this) {
mWindowFocusChanged = true;
mUpcomingWindowFocus = hasFocus;
@@ -9732,10 +9730,10 @@
}
@Override
- public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
+ public void windowFocusChanged(boolean hasFocus) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
- viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
+ viewAncestor.windowFocusChanged(hasFocus);
}
}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index b428970..7755b69 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -1842,7 +1842,7 @@
@Override
public void setLocalFocus(boolean hasFocus, boolean inTouchMode) {
ViewRootImpl viewRoot = getViewRootImpl();
- viewRoot.windowFocusChanged(hasFocus, inTouchMode);
+ viewRoot.windowFocusChanged(hasFocus);
viewRoot.touchModeChanged(inTouchMode);
}
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 9e741e2..f93e32f 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -90,7 +90,7 @@
}
@Override
- public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
+ public void windowFocusChanged(boolean hasFocus) {
}
@Override
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 67ab30b..1159c8a 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -390,13 +390,11 @@
case AINPUT_EVENT_TYPE_FOCUS: {
FocusEvent* focusEvent = static_cast<FocusEvent*>(inputEvent);
if (kDebugDispatchCycle) {
- ALOGD("channel '%s' ~ Received focus event: hasFocus=%s, inTouchMode=%s.",
- getInputChannelName().c_str(), toString(focusEvent->getHasFocus()),
- toString(focusEvent->getInTouchMode()));
+ ALOGD("channel '%s' ~ Received focus event: hasFocus=%s.",
+ getInputChannelName().c_str(), toString(focusEvent->getHasFocus()));
}
env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.onFocusEvent,
- jboolean(focusEvent->getHasFocus()),
- jboolean(focusEvent->getInTouchMode()));
+ jboolean(focusEvent->getHasFocus()));
finishInputEvent(seq, true /* handled */);
continue;
}
@@ -615,7 +613,7 @@
gInputEventReceiverClassInfo.clazz,
"dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
gInputEventReceiverClassInfo.onFocusEvent =
- GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onFocusEvent", "(ZZ)V");
+ GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onFocusEvent", "(Z)V");
gInputEventReceiverClassInfo.onPointerCaptureEvent =
GetMethodIDOrDie(env, gInputEventReceiverClassInfo.clazz, "onPointerCaptureEvent",
"(Z)V");
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 8ef3825..211f78e 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -112,8 +112,9 @@
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
optional bool keyguard_showing = 1;
- repeated KeyguardOccludedProto keyguard_occluded_states = 2;
+ repeated KeyguardOccludedProto keyguard_occluded_states = 2 [deprecated=true];
optional bool aod_showing = 3;
+ repeated KeyguardPerDisplayProto keyguard_per_display = 4;
}
message KeyguardOccludedProto {
@@ -123,6 +124,15 @@
optional bool keyguard_occluded = 2;
}
+message KeyguardPerDisplayProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional int32 display_id = 1;
+ optional bool keyguard_showing = 2;
+ optional bool aod_showing = 3;
+ optional bool keyguard_occluded = 4;
+}
+
/* represents PhoneWindowManager */
message WindowManagerPolicyProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 882a0da..e958a96 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -62,7 +62,7 @@
* Tests for {@link ViewRootImpl}
*
* Build/Install/Run:
- * atest FrameworksCoreTests:ViewRootImplTest
+ * atest FrameworksCoreTests:ViewRootImplTest
*/
@Presubmit
@SmallTest
@@ -300,7 +300,7 @@
@Test
public void whenWindowDoesNotHaveFocus_keysAreDropped() {
checkKeyEvent(() -> {
- mViewRootImpl.windowFocusChanged(false /*hasFocus*/, true /*inTouchMode*/);
+ mViewRootImpl.windowFocusChanged(false /*hasFocus*/);
}, false /*shouldReceiveKey*/);
}
@@ -310,7 +310,7 @@
@Test
public void whenWindowHasFocus_keysAreReceived() {
checkKeyEvent(() -> {
- mViewRootImpl.windowFocusChanged(true /*hasFocus*/, true /*inTouchMode*/);
+ mViewRootImpl.windowFocusChanged(true /*hasFocus*/);
}, true /*shouldReceiveKey*/);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index caa5327..ec59fad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -111,8 +111,7 @@
private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
- // TODO(b/173386799) keep in sync with Launcher3 and also don't do a broadcast
- public static final String TASKBAR_CHANGED_BROADCAST = "taskbarChanged";
+ // TODO(b/173386799) keep in sync with Launcher3, not hooked up to anything
public static final String EXTRA_TASKBAR_CREATED = "taskbarCreated";
public static final String EXTRA_BUBBLE_OVERFLOW_OPENED = "bubbleOverflowOpened";
public static final String EXTRA_TASKBAR_VISIBLE = "taskbarVisible";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 33ce383..c5713b4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -356,7 +356,7 @@
public void dispatchGetNewSurface() {}
@Override
- public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {}
+ public void windowFocusChanged(boolean hasFocus) {}
@Override
public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {}
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index f312f64..d285b13 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -53,6 +53,7 @@
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
+import android.view.ViewRootImpl;
import android.view.WindowManager;
import android.view.accessibility.CaptioningManager;
import android.widget.FrameLayout;
@@ -1581,7 +1582,8 @@
return TvInputManager.Session.DISPATCH_NOT_HANDLED;
}
if (!mOverlayViewContainer.hasWindowFocus()) {
- mOverlayViewContainer.getViewRootImpl().windowFocusChanged(true, true);
+ ViewRootImpl viewRoot = mOverlayViewContainer.getViewRootImpl();
+ viewRoot.windowFocusChanged(true);
}
if (isNavigationKey && mOverlayViewContainer.hasFocusable()) {
// If mOverlayView has focusable views, navigation key events should be always
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index ecd9cc1..39b560b 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -65,6 +65,7 @@
case HAL_PIXEL_FORMAT_Y8:
case HAL_PIXEL_FORMAT_Y16:
case HAL_PIXEL_FORMAT_RAW16:
+ case HAL_PIXEL_FORMAT_RAW12:
case HAL_PIXEL_FORMAT_RAW10:
case HAL_PIXEL_FORMAT_RAW_OPAQUE:
case HAL_PIXEL_FORMAT_BLOB:
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 24eea30..c4dfee4 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -3881,8 +3881,8 @@
Result r = filterClient->close();
filterClient->decStrong(filter);
- env->SetLongField(filter, gFields.sharedFilterContext, 0);
if (shared) {
+ env->SetLongField(filter, gFields.sharedFilterContext, 0);
} else {
env->SetLongField(filter, gFields.filterContext, 0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index dd44f72..022baf6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -507,15 +507,19 @@
riv.setRevealParameters(cx, cy, r);
riv.setPendingIntent(pendingIntent);
+ riv.getController().setPendingIntent(pendingIntent);
riv.setRemoteInput(inputs, input, editedSuggestionInfo);
+ riv.getController().setRemoteInput(input);
+ riv.getController().setRemoteInputs(inputs);
riv.focusAnimated();
if (userMessageContent != null) {
riv.setEditTextContent(userMessageContent);
}
if (deferBouncer) {
final ExpandableNotificationRow finalRow = row;
- riv.setBouncerChecker(() -> !authBypassCheck.canSendRemoteInputWithoutBouncer()
- && showBouncerForRemoteInput(view, pendingIntent, finalRow));
+ riv.getController().setBouncerChecker(() ->
+ !authBypassCheck.canSendRemoteInputWithoutBouncer()
+ && showBouncerForRemoteInput(view, pendingIntent, finalRow));
}
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
index 010b6f80..8182e73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
@@ -51,8 +51,10 @@
// If this notif begins a new section, first add the section's header view
if (section != currentSection) {
- section.headerController?.let { headerController ->
- root.children.add(NodeSpecImpl(root, headerController))
+ if (section.headerController != currentSection?.headerController) {
+ section.headerController?.let { headerController ->
+ root.children.add(NodeSpecImpl(root, headerController))
+ }
}
prevSections.add(currentSection)
currentSection = section
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 4568470..1b4d369 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1289,8 +1289,8 @@
result.mView = riv;
// Create a new controller for the view. The lifetime of the controller is 1:1
// with that of the view.
- RemoteInputViewSubcomponent subcomponent =
- mRemoteInputSubcomponentFactory.create(result.mView);
+ RemoteInputViewSubcomponent subcomponent = mRemoteInputSubcomponentFactory
+ .create(result.mView, mRemoteInputController);
result.mController = subcomponent.getController();
result.mView.setController(result.mController);
} else {
@@ -1311,8 +1311,9 @@
Notification.Action[] actions = entry.getSbn().getNotification().actions;
if (existingPendingIntent != null) {
result.mView.setPendingIntent(existingPendingIntent);
+ result.mController.setPendingIntent(existingPendingIntent);
}
- if (result.mView.updatePendingIntentFromActions(actions)) {
+ if (result.mController.updatePendingIntentFromActions(actions)) {
if (!result.mView.isActive()) {
result.mView.focus();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index b47e71a..06cf70d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -40,7 +40,6 @@
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
-import static com.android.wm.shell.bubbles.BubbleController.TASKBAR_CHANGED_BROADCAST;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -918,8 +917,6 @@
mBypassHeadsUpNotifier.setUp();
if (mBubblesOptional.isPresent()) {
mBubblesOptional.get().setExpandListener(mBubbleExpandListener);
- IntentFilter filter = new IntentFilter(TASKBAR_CHANGED_BROADCAST);
- mBroadcastDispatcher.registerReceiver(mTaskbarChangeReceiver, filter);
}
mKeyguardIndicationController.init();
@@ -4270,13 +4267,6 @@
}
};
- BroadcastReceiver mTaskbarChangeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- mBubblesOptional.ifPresent(bubbles -> bubbles.onTaskbarChanged(intent.getExtras()));
- }
- };
-
private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
@Override
public void onConfigChanged(Configuration newConfig) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index d25013a..46fa20d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -25,9 +25,7 @@
import android.app.PendingIntent;
import android.app.RemoteInput;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
-import android.content.pm.ShortcutManager;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.BlendMode;
@@ -35,13 +33,9 @@
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.text.Editable;
import android.text.SpannedString;
-import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.ArraySet;
import android.util.AttributeSet;
@@ -78,7 +72,6 @@
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dependency;
import com.android.systemui.R;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry.EditedSuggestionInfo;
@@ -89,7 +82,6 @@
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
import java.util.List;
import java.util.function.Consumer;
@@ -107,7 +99,7 @@
private final SendButtonTextWatcher mTextWatcher;
private final TextView.OnEditorActionListener mEditorActionHandler;
- private final ArrayList<OnSendRemoteInputListener> mOnSendListeners = new ArrayList<>();
+ private final ArrayList<Runnable> mOnSendListeners = new ArrayList<>();
private final ArrayList<Consumer<Boolean>> mOnVisibilityChangedListeners = new ArrayList<>();
private final ArrayList<OnFocusChangeListener> mEditTextFocusChangeListeners =
new ArrayList<>();
@@ -133,7 +125,6 @@
private PendingIntent mPendingIntent;
private RemoteInput mRemoteInput;
private RemoteInput[] mRemoteInputs;
- private NotificationRemoteInputManager.BouncerChecker mBouncerChecker;
private boolean mRemoved;
private NotificationViewWrapper mWrapper;
@@ -178,6 +169,12 @@
ta.recycle();
}
+ // TODO(b/193539698): move to Controller, since we're just directly accessing a system service
+ /** Hide the IME, if visible. */
+ public void hideIme() {
+ mEditText.hideIme();
+ }
+
private ColorStateList colorStateListWithDisabledAlpha(int color, int disabledAlpha) {
return new ColorStateList(new int[][]{
new int[]{-com.android.internal.R.attr.state_enabled}, // disabled
@@ -303,6 +300,11 @@
return mViewController;
}
+ /** Clear the attachment, if present. */
+ public void clearAttachment() {
+ setAttachment(null);
+ }
+
@VisibleForTesting
protected void setAttachment(ContentInfo item) {
if (mEntry.remoteInputAttachment != null && mEntry.remoteInputAttachment != item) {
@@ -339,121 +341,18 @@
updateSendButton();
}
- /**
- * Reply intent
- * @return returns intent with granted URI permissions that should be used immediately
- */
- private Intent prepareRemoteInput() {
- return mEntry.remoteInputAttachment == null
- ? prepareRemoteInputFromText()
- : prepareRemoteInputFromData(mEntry.remoteInputMimeType, mEntry.remoteInputUri);
- }
-
- private Intent prepareRemoteInputFromText() {
- Bundle results = new Bundle();
- results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
- Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent,
- results);
-
- mEntry.remoteInputText = mEditText.getText().toString();
- setAttachment(null);
- mEntry.remoteInputUri = null;
- mEntry.remoteInputMimeType = null;
-
- if (mEntry.editedSuggestionInfo == null) {
- RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_FREE_FORM_INPUT);
- } else {
- RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_CHOICE);
- }
-
- return fillInIntent;
- }
-
- private Intent prepareRemoteInputFromData(String contentType, Uri data) {
- HashMap<String, Uri> results = new HashMap<>();
- results.put(contentType, data);
- // grant for the target app.
- mController.grantInlineReplyUriPermission(mEntry.getSbn(), data);
- Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- RemoteInput.addDataResultToIntent(mRemoteInput, fillInIntent, results);
-
- Bundle bundle = new Bundle();
- bundle.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
- RemoteInput.addResultsToIntent(mRemoteInputs, fillInIntent,
- bundle);
-
- CharSequence attachmentText =
- mEntry.remoteInputAttachment.getClip().getDescription().getLabel();
-
- CharSequence attachmentLabel = TextUtils.isEmpty(attachmentText)
- ? mContext.getString(R.string.remote_input_image_insertion_text)
- : attachmentText;
- // add content description to reply text for context
- CharSequence fullText = TextUtils.isEmpty(mEditText.getText())
- ? attachmentLabel
- : "\"" + attachmentLabel + "\" " + mEditText.getText();
-
- mEntry.remoteInputText = fullText;
-
- // mirror prepareRemoteInputFromText for text input
- if (mEntry.editedSuggestionInfo == null) {
- RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_FREE_FORM_INPUT);
- } else if (mEntry.remoteInputAttachment == null) {
- RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_CHOICE);
- }
-
- return fillInIntent;
- }
-
- private void sendRemoteInput(Intent intent) {
- if (mBouncerChecker != null && mBouncerChecker.showBouncerIfNecessary()) {
- mEditText.hideIme();
- for (OnSendRemoteInputListener listener : new ArrayList<>(mOnSendListeners)) {
- listener.onSendRequestBounced();
- }
- return;
- }
-
+ /** Show the "sending in-progress" UI. */
+ public void startSending() {
mEditText.setEnabled(false);
mSendButton.setVisibility(INVISIBLE);
mProgressBar.setVisibility(VISIBLE);
- mEntry.lastRemoteInputSent = SystemClock.elapsedRealtime();
- mEntry.mRemoteEditImeAnimatingAway = true;
- mController.addSpinning(mEntry.getKey(), mToken);
- mController.removeRemoteInput(mEntry, mToken);
mEditText.mShowImeOnInputConnection = false;
- mController.remoteInputSent(mEntry);
- mEntry.setHasSentReply();
+ }
- for (OnSendRemoteInputListener listener : new ArrayList<>(mOnSendListeners)) {
- listener.onSendRemoteInput();
+ private void sendRemoteInput() {
+ for (Runnable listener : new ArrayList<>(mOnSendListeners)) {
+ listener.run();
}
-
- // Tell ShortcutManager that this package has been "activated". ShortcutManager
- // will reset the throttling for this package.
- // Strictly speaking, the intent receiver may be different from the notification publisher,
- // but that's an edge case, and also because we can't always know which package will receive
- // an intent, so we just reset for the publisher.
- getContext().getSystemService(ShortcutManager.class).onApplicationActive(
- mEntry.getSbn().getPackageName(),
- mEntry.getSbn().getUser().getIdentifier());
-
- mUiEventLogger.logWithInstanceId(
- NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_SEND,
- mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
- mEntry.getSbn().getInstanceId());
- try {
- mPendingIntent.send(mContext, 0, intent);
- } catch (PendingIntent.CanceledException e) {
- Log.i(TAG, "Unable to send remote input result", e);
- mUiEventLogger.logWithInstanceId(
- NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_FAILURE,
- mEntry.getSbn().getUid(), mEntry.getSbn().getPackageName(),
- mEntry.getSbn().getInstanceId());
- }
-
- setAttachment(null);
}
public CharSequence getText() {
@@ -478,7 +377,7 @@
@Override
public void onClick(View v) {
if (v == mSendButton) {
- sendRemoteInput(prepareRemoteInput());
+ sendRemoteInput();
}
}
@@ -687,55 +586,18 @@
return mEditText.isFocused() && mEditText.isEnabled();
}
+ // TODO(b/193539698): move this to the controller
public void stealFocusFrom(RemoteInputView other) {
other.close();
setPendingIntent(other.mPendingIntent);
setRemoteInput(other.mRemoteInputs, other.mRemoteInput, mEntry.editedSuggestionInfo);
setRevealParameters(other.mRevealCx, other.mRevealCy, other.mRevealR);
+ getController().setPendingIntent(other.mPendingIntent);
+ getController().setRemoteInput(other.mRemoteInput);
+ getController().setRemoteInputs(other.mRemoteInputs);
focus();
}
- /**
- * Tries to find an action in {@param actions} that matches the current pending intent
- * of this view and updates its state to that of the found action
- *
- * @return true if a matching action was found, false otherwise
- */
- public boolean updatePendingIntentFromActions(Notification.Action[] actions) {
- if (mPendingIntent == null || actions == null) {
- return false;
- }
- Intent current = mPendingIntent.getIntent();
- if (current == null) {
- return false;
- }
-
- for (Notification.Action a : actions) {
- RemoteInput[] inputs = a.getRemoteInputs();
- if (a.actionIntent == null || inputs == null) {
- continue;
- }
- Intent candidate = a.actionIntent.getIntent();
- if (!current.filterEquals(candidate)) {
- continue;
- }
-
- RemoteInput input = null;
- for (RemoteInput i : inputs) {
- if (i.getAllowFreeFormInput()) {
- input = i;
- }
- }
- if (input == null) {
- continue;
- }
- setPendingIntent(a.actionIntent);
- setRemoteInput(inputs, input, null /* editedSuggestionInfo*/);
- return true;
- }
- return false;
- }
-
public PendingIntent getPendingIntent() {
return mPendingIntent;
}
@@ -814,16 +676,6 @@
return getVisibility() == VISIBLE && mController.isSpinning(mEntry.getKey(), mToken);
}
- /**
- * Sets a {@link com.android.systemui.statusbar.NotificationRemoteInputManager.BouncerChecker}
- * that will be used to determine if the device needs to be unlocked before sending the
- * RemoteInput.
- */
- public void setBouncerChecker(
- @Nullable NotificationRemoteInputManager.BouncerChecker bouncerChecker) {
- mBouncerChecker = bouncerChecker;
- }
-
/** Registers a listener for focus-change events on the EditText */
public void addOnEditTextFocusChangedListener(View.OnFocusChangeListener listener) {
mEditTextFocusChangeListeners.add(listener);
@@ -846,26 +698,15 @@
}
/** Registers a listener for send events on this RemoteInputView */
- public void addOnSendRemoteInputListener(OnSendRemoteInputListener listener) {
+ public void addOnSendRemoteInputListener(Runnable listener) {
mOnSendListeners.add(listener);
}
/** Removes a previously-added listener for send events on this RemoteInputView */
- public void removeOnSendRemoteInputListener(OnSendRemoteInputListener listener) {
+ public void removeOnSendRemoteInputListener(Runnable listener) {
mOnSendListeners.remove(listener);
}
- /** Listener for send events */
- public interface OnSendRemoteInputListener {
- /** Invoked when the remote input has been sent successfully. */
- void onSendRemoteInput();
- /**
- * Invoked when the user had requested to send the remote input, but authentication was
- * required and the bouncer was shown instead.
- */
- void onSendRequestBounced();
- }
-
/** Handler for button click on send action in IME. */
private class EditorActionHandler implements TextView.OnEditorActionListener {
@@ -881,7 +722,7 @@
if (isSoftImeEvent || isKeyboardEnterKey) {
if (mEditText.length() > 0 || mEntry.remoteInputAttachment != null) {
- sendRemoteInput(prepareRemoteInput());
+ sendRemoteInput();
}
// Consume action to prevent IME from closing.
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
index 383170e..530da43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
@@ -16,28 +16,102 @@
package com.android.systemui.statusbar.policy
+import android.app.Notification
+import android.app.PendingIntent
+import android.app.RemoteInput
+import android.content.Intent
+import android.content.pm.ShortcutManager
+import android.net.Uri
+import android.os.Bundle
+import android.os.SystemClock
+import android.text.TextUtils
+import android.util.ArraySet
+import android.util.Log
import android.view.View
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.R
+import com.android.systemui.statusbar.NotificationRemoteInputManager
+import com.android.systemui.statusbar.RemoteInputController
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.policy.RemoteInputView.NotificationRemoteInputEvent
import com.android.systemui.statusbar.policy.dagger.RemoteInputViewScope
import javax.inject.Inject
interface RemoteInputViewController {
fun bind()
fun unbind()
+
+ /**
+ * A [NotificationRemoteInputManager.BouncerChecker] that will be used to determine if the
+ * device needs to be unlocked before sending the RemoteInput.
+ */
+ var bouncerChecker: NotificationRemoteInputManager.BouncerChecker?
+
+ // TODO(b/193539698): these properties probably shouldn't be nullable
+ /** A [PendingIntent] to be used to send the RemoteInput. */
+ var pendingIntent: PendingIntent?
+ /** The [RemoteInput] data backing this Controller. */
+ var remoteInput: RemoteInput?
+ /** Other [RemoteInput]s from the notification associated with this Controller. */
+ var remoteInputs: Array<RemoteInput>?
+
+ /**
+ * Tries to find an action in {@param actions} that matches the current pending intent
+ * of this view and updates its state to that of the found action
+ *
+ * @return true if a matching action was found, false otherwise
+ */
+ fun updatePendingIntentFromActions(actions: Array<Notification.Action>?): Boolean
+
+ /** Registers a listener for send events. */
+ fun addOnSendRemoteInputListener(listener: OnSendRemoteInputListener)
+
+ /** Unregisters a listener previously registered via [addOnSendRemoteInputListener] */
+ fun removeOnSendRemoteInputListener(listener: OnSendRemoteInputListener)
}
+/** Listener for send events */
+interface OnSendRemoteInputListener {
+
+ /** Invoked when the remote input has been sent successfully. */
+ fun onSendRemoteInput()
+
+ /**
+ * Invoked when the user had requested to send the remote input, but authentication was
+ * required and the bouncer was shown instead.
+ */
+ fun onSendRequestBounced()
+}
+
+private const val TAG = "RemoteInput"
+
@RemoteInputViewScope
class RemoteInputViewControllerImpl @Inject constructor(
private val view: RemoteInputView,
- private val remoteInputQuickSettingsDisabler: RemoteInputQuickSettingsDisabler
+ private val entry: NotificationEntry,
+ private val remoteInputQuickSettingsDisabler: RemoteInputQuickSettingsDisabler,
+ private val remoteInputController: RemoteInputController,
+ private val shortcutManager: ShortcutManager,
+ private val uiEventLogger: UiEventLogger
) : RemoteInputViewController {
+ private object Token
+ private val onSendListeners = ArraySet<OnSendRemoteInputListener>()
+ private val resources get() = view.resources
+
private var isBound = false
+ override var pendingIntent: PendingIntent? = null
+ override var bouncerChecker: NotificationRemoteInputManager.BouncerChecker? = null
+ override var remoteInput: RemoteInput? = null
+ override var remoteInputs: Array<RemoteInput>? = null
+
override fun bind() {
if (isBound) return
isBound = true
view.addOnEditTextFocusChangedListener(onFocusChangeListener)
+ view.addOnSendRemoteInputListener(onSendRemoteInputListener)
}
override fun unbind() {
@@ -45,9 +119,159 @@
isBound = false
view.removeOnEditTextFocusChangedListener(onFocusChangeListener)
+ view.removeOnSendRemoteInputListener(onSendRemoteInputListener)
+ }
+
+ override fun updatePendingIntentFromActions(actions: Array<Notification.Action>?): Boolean {
+ actions ?: return false
+ val current: Intent = pendingIntent?.intent ?: return false
+ for (a in actions) {
+ val actionIntent = a.actionIntent ?: continue
+ val inputs = a.remoteInputs ?: continue
+ if (!current.filterEquals(actionIntent.intent)) continue
+ val input = inputs.firstOrNull { it.allowFreeFormInput } ?: continue
+ pendingIntent = actionIntent
+ remoteInput = input
+ remoteInputs = inputs
+ view.pendingIntent = actionIntent
+ view.setRemoteInput(inputs, input, null /* editedSuggestionInfo */)
+ return true
+ }
+ return false
+ }
+
+ override fun addOnSendRemoteInputListener(listener: OnSendRemoteInputListener) {
+ onSendListeners.add(listener)
+ }
+
+ /** Removes a previously-added listener for send events on this RemoteInputView */
+ override fun removeOnSendRemoteInputListener(listener: OnSendRemoteInputListener) {
+ onSendListeners.remove(listener)
}
private val onFocusChangeListener = View.OnFocusChangeListener { _, hasFocus ->
remoteInputQuickSettingsDisabler.setRemoteInputActive(hasFocus)
}
-}
\ No newline at end of file
+
+ private val onSendRemoteInputListener = Runnable {
+ val remoteInput = remoteInput ?: run {
+ Log.e(TAG, "cannot send remote input, RemoteInput data is null")
+ return@Runnable
+ }
+ val pendingIntent = pendingIntent ?: run {
+ Log.e(TAG, "cannot send remote input, PendingIntent is null")
+ return@Runnable
+ }
+ val intent = prepareRemoteInput(remoteInput)
+ sendRemoteInput(pendingIntent, intent)
+ }
+
+ private fun sendRemoteInput(pendingIntent: PendingIntent, intent: Intent) {
+ if (bouncerChecker?.showBouncerIfNecessary() == true) {
+ view.hideIme()
+ for (listener in onSendListeners.toList()) {
+ listener.onSendRequestBounced()
+ }
+ return
+ }
+
+ view.startSending()
+
+ entry.lastRemoteInputSent = SystemClock.elapsedRealtime()
+ entry.mRemoteEditImeAnimatingAway = true
+ remoteInputController.addSpinning(entry.key, Token)
+ remoteInputController.removeRemoteInput(entry, Token)
+ remoteInputController.remoteInputSent(entry)
+ entry.setHasSentReply()
+
+ for (listener in onSendListeners.toList()) {
+ listener.onSendRemoteInput()
+ }
+
+ // Tell ShortcutManager that this package has been "activated". ShortcutManager will reset
+ // the throttling for this package.
+ // Strictly speaking, the intent receiver may be different from the notification publisher,
+ // but that's an edge case, and also because we can't always know which package will receive
+ // an intent, so we just reset for the publisher.
+ shortcutManager.onApplicationActive(entry.sbn.packageName, entry.sbn.user.identifier)
+
+ uiEventLogger.logWithInstanceId(
+ NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_SEND,
+ entry.sbn.uid, entry.sbn.packageName,
+ entry.sbn.instanceId)
+
+ try {
+ pendingIntent.send(view.context, 0, intent)
+ } catch (e: PendingIntent.CanceledException) {
+ Log.i(TAG, "Unable to send remote input result", e)
+ uiEventLogger.logWithInstanceId(
+ NotificationRemoteInputEvent.NOTIFICATION_REMOTE_INPUT_FAILURE,
+ entry.sbn.uid, entry.sbn.packageName,
+ entry.sbn.instanceId)
+ }
+
+ view.clearAttachment()
+ }
+
+ /**
+ * Reply intent
+ * @return returns intent with granted URI permissions that should be used immediately
+ */
+ private fun prepareRemoteInput(remoteInput: RemoteInput): Intent =
+ if (entry.remoteInputAttachment == null) prepareRemoteInputFromText(remoteInput)
+ else prepareRemoteInputFromData(
+ remoteInput,
+ entry.remoteInputMimeType,
+ entry.remoteInputUri)
+
+ private fun prepareRemoteInputFromText(remoteInput: RemoteInput): Intent {
+ val results = Bundle()
+ results.putString(remoteInput.resultKey, view.text.toString())
+ val fillInIntent = Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ RemoteInput.addResultsToIntent(remoteInputs, fillInIntent, results)
+ entry.remoteInputText = view.text
+ view.clearAttachment()
+ entry.remoteInputUri = null
+ entry.remoteInputMimeType = null
+ if (entry.editedSuggestionInfo == null) {
+ RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_FREE_FORM_INPUT)
+ } else {
+ RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_CHOICE)
+ }
+ return fillInIntent
+ }
+
+ private fun prepareRemoteInputFromData(
+ remoteInput: RemoteInput,
+ contentType: String,
+ data: Uri
+ ): Intent {
+ val results = HashMap<String, Uri>()
+ results[contentType] = data
+ // grant for the target app.
+ remoteInputController.grantInlineReplyUriPermission(entry.sbn, data)
+ val fillInIntent = Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ RemoteInput.addDataResultToIntent(remoteInput, fillInIntent, results)
+ val bundle = Bundle()
+ bundle.putString(remoteInput.resultKey, view.text.toString())
+ RemoteInput.addResultsToIntent(remoteInputs, fillInIntent, bundle)
+ val attachmentText: CharSequence = entry.remoteInputAttachment.clip.description.label
+ val attachmentLabel =
+ if (TextUtils.isEmpty(attachmentText))
+ resources.getString(R.string.remote_input_image_insertion_text)
+ else attachmentText
+ // add content description to reply text for context
+ val fullText =
+ if (TextUtils.isEmpty(view.text)) attachmentLabel
+ else "\"" + attachmentLabel + "\" " + view.text
+ entry.remoteInputText = fullText
+
+ // mirror prepareRemoteInputFromText for text input
+ if (entry.editedSuggestionInfo == null) {
+ RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_FREE_FORM_INPUT)
+ } else if (entry.remoteInputAttachment == null) {
+ RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_CHOICE)
+ }
+ return fillInIntent
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/RemoteInput.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/RemoteInput.kt
index aff39ef..d4abc40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/RemoteInput.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/RemoteInput.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy.dagger
+import com.android.systemui.statusbar.RemoteInputController
import com.android.systemui.statusbar.policy.RemoteInputView
import com.android.systemui.statusbar.policy.RemoteInputViewController
import com.android.systemui.statusbar.policy.RemoteInputViewControllerImpl
@@ -32,7 +33,10 @@
@Subcomponent.Factory
interface Factory {
- fun create(@BindsInstance view: RemoteInputView): RemoteInputViewSubcomponent
+ fun create(
+ @BindsInstance view: RemoteInputView,
+ @BindsInstance remoteInputController: RemoteInputController
+ ): RemoteInputViewSubcomponent
}
}
@@ -44,4 +48,4 @@
@Qualifier
@Retention(AnnotationRetention.BINARY)
-internal annotation class RemoteInputViewScope
\ No newline at end of file
+internal annotation class RemoteInputViewScope
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
index ed48452..6313d3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
@@ -58,6 +58,7 @@
private val section1 = buildSection(1, section1Bucket, headerController1)
private val section1NoHeader = buildSection(1, section1Bucket, null)
private val section2 = buildSection(2, section2Bucket, headerController2)
+ private val section3 = buildSection(3, section2Bucket, headerController2)
private val fakeViewBarn = FakeViewBarn()
@@ -75,6 +76,37 @@
}
@Test
+ fun testMultipleSectionsWithSameController() {
+ checkOutput(
+ listOf(
+ notif(0, section0),
+ notif(1, section2),
+ notif(2, section3)
+ ),
+ tree(
+ node(headerController0),
+ notifNode(0),
+ node(headerController2),
+ notifNode(1),
+ notifNode(2)
+ )
+ )
+ }
+
+ @Test(expected = RuntimeException::class)
+ fun testMultipleSectionsWithSameControllerNonConsecutive() {
+ checkOutput(
+ listOf(
+ notif(0, section0),
+ notif(1, section1),
+ notif(2, section3),
+ notif(3, section1)
+ ),
+ tree()
+ )
+ }
+
+ @Test
fun testSimpleMapping() {
checkOutput(
// GIVEN a simple flat list of notifications all in the same headerless section
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 27ddb36..ba7bbfe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -46,6 +46,7 @@
import android.widget.EditText;
import android.widget.ImageButton;
+import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.internal.logging.UiEventLogger;
@@ -55,6 +56,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.RemoteInputController;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
import com.android.systemui.statusbar.phone.LightBarController;
@@ -85,7 +87,6 @@
@Mock private LightBarController mLightBarController;
private BlockingQueueIntentReceiver mReceiver;
private final UiEventLoggerFake mUiEventLoggerFake = new UiEventLoggerFake();
- private RemoteInputView mView;
@Before
public void setUp() throws Exception {
@@ -112,13 +113,17 @@
mContext.unregisterReceiver(mReceiver);
}
- private void setTestPendingIntent(RemoteInputView view) {
+ private void setTestPendingIntent(RemoteInputView view, RemoteInputViewController controller) {
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
new Intent(TEST_ACTION), PendingIntent.FLAG_MUTABLE);
RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).build();
+ RemoteInput[] inputs = {input};
view.setPendingIntent(pendingIntent);
- view.setRemoteInput(new RemoteInput[]{input}, input, null /* editedSuggestionInfo */);
+ controller.setPendingIntent(pendingIntent);
+ view.setRemoteInput(inputs, input, null /* editedSuggestionInfo */);
+ controller.setRemoteInput(input);
+ controller.setRemoteInputs(inputs);
}
@Test
@@ -129,8 +134,9 @@
TestableLooper.get(this));
ExpandableNotificationRow row = helper.createRow();
RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
+ RemoteInputViewController controller = bindController(view, row.getEntry());
- setTestPendingIntent(view);
+ setTestPendingIntent(view, controller);
view.focus();
@@ -140,6 +146,7 @@
sendButton.performClick();
Intent resultIntent = mReceiver.waitForIntent();
+ assertNotNull(resultIntent);
assertEquals(TEST_REPLY,
RemoteInput.getResultsFromIntent(resultIntent).get(TEST_RESULT_KEY));
assertEquals(RemoteInput.SOURCE_FREE_FORM_INPUT,
@@ -167,8 +174,9 @@
UserHandle.getUid(fromUser.getIdentifier(), DUMMY_MESSAGE_APP_ID),
toUser);
RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
+ RemoteInputViewController controller = bindController(view, row.getEntry());
- setTestPendingIntent(view);
+ setTestPendingIntent(view, controller);
view.focus();
@@ -224,8 +232,9 @@
TestableLooper.get(this));
ExpandableNotificationRow row = helper.createRow();
RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
+ RemoteInputViewController controller = bindController(view, row.getEntry());
- setTestPendingIntent(view);
+ setTestPendingIntent(view, controller);
// Open view, send a reply
view.focus();
@@ -253,8 +262,9 @@
TestableLooper.get(this));
ExpandableNotificationRow row = helper.createRow();
RemoteInputView view = RemoteInputView.inflate(mContext, null, row.getEntry(), mController);
+ RemoteInputViewController controller = bindController(view, row.getEntry());
- setTestPendingIntent(view);
+ setTestPendingIntent(view, controller);
// Open view, attach an image
view.focus();
@@ -279,4 +289,21 @@
.NOTIFICATION_REMOTE_INPUT_ATTACH_IMAGE.getId(),
mUiEventLoggerFake.eventId(1));
}
+
+ // NOTE: because we're refactoring the RemoteInputView and moving logic into the
+ // RemoteInputViewController, it's easiest to just test the system of the two classes together.
+ @NonNull
+ private RemoteInputViewController bindController(
+ RemoteInputView view,
+ NotificationEntry entry) {
+ RemoteInputViewControllerImpl viewController = new RemoteInputViewControllerImpl(
+ view,
+ entry,
+ mRemoteInputQuickSettingsDisabler,
+ mController,
+ mShortcutManager,
+ mUiEventLoggerFake);
+ viewController.bind();
+ return viewController;
+ }
}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index cbca48c9..17efeb3 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -911,18 +911,21 @@
@Override
public void onPreExecute() {
mUseWakeLock = false;
- final int size = locationResult.size();
- for (int i = 0; i < size; ++i) {
- if (!locationResult.get(i).isMock()) {
- mUseWakeLock = true;
- break;
+
+ // don't acquire a wakelock for passive requests or for mock locations
+ if (getRequest().getIntervalMillis() != LocationRequest.PASSIVE_INTERVAL) {
+ final int size = locationResult.size();
+ for (int i = 0; i < size; ++i) {
+ if (!locationResult.get(i).isMock()) {
+ mUseWakeLock = true;
+ break;
+ }
}
}
// update last delivered location
setLastDeliveredLocation(locationResult.getLastLocation());
- // don't acquire a wakelock for mock locations to prevent abuse
if (mUseWakeLock) {
mWakeLock.acquire(WAKELOCK_TIMEOUT_MS);
}
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index af9c401..27db2f9 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -121,6 +121,8 @@
private final Injector mInjector;
+ private final DexOptHelper mDexOptHelper;
+
private final Object mLock = new Object();
// Thread currently running dexopt. This will be null if dexopt is not running.
@@ -168,13 +170,15 @@
void onPackagesUpdated(ArraySet<String> updatedPackages);
}
- public BackgroundDexOptService(Context context, DexManager dexManager) {
- this(new Injector(context, dexManager));
+ public BackgroundDexOptService(Context context, DexManager dexManager,
+ PackageManagerService pm) {
+ this(new Injector(context, dexManager, pm));
}
@VisibleForTesting
public BackgroundDexOptService(Injector injector) {
mInjector = injector;
+ mDexOptHelper = mInjector.getDexOptHelper();
LocalServices.addService(BackgroundDexOptService.class, this);
mDowngradeUnusedAppsThresholdInMillis = mInjector.getDowngradeUnusedAppsThresholdInMillis();
}
@@ -251,15 +255,13 @@
resetStatesForNewDexOptRunLocked(Thread.currentThread());
}
PackageManagerService pm = mInjector.getPackageManagerService();
- DexOptHelper dexOptHelper = new DexOptHelper(pm);
ArraySet<String> packagesToOptimize;
if (packageNames == null) {
- packagesToOptimize = dexOptHelper.getOptimizablePackages();
+ packagesToOptimize = mDexOptHelper.getOptimizablePackages();
} else {
packagesToOptimize = new ArraySet<>(packageNames);
}
- return runIdleOptimization(pm, dexOptHelper, packagesToOptimize,
- /* isPostBootUpdate= */ false);
+ return runIdleOptimization(pm, packagesToOptimize, /* isPostBootUpdate= */ false);
} finally {
Binder.restoreCallingIdentity(identity);
markDexOptCompleted();
@@ -320,8 +322,7 @@
return false;
}
- DexOptHelper dexOptHelper = new DexOptHelper(pm);
- ArraySet<String> pkgs = dexOptHelper.getOptimizablePackages();
+ ArraySet<String> pkgs = mDexOptHelper.getOptimizablePackages();
if (pkgs.isEmpty()) {
Slog.i(TAG, "No packages to optimize");
markPostBootUpdateCompleted(params);
@@ -347,7 +348,7 @@
tr.traceBegin("jobExecution");
boolean completed = false;
try {
- completed = runIdleOptimization(pm, dexOptHelper, pkgs,
+ completed = runIdleOptimization(pm, pkgs,
params.getJobId() == JOB_POST_BOOT_UPDATE);
} finally { // Those cleanup should be done always.
tr.traceEnd();
@@ -461,7 +462,7 @@
@GuardedBy("mLock")
private void controlDexOptBlockingLocked(boolean block) {
PackageManagerService pm = mInjector.getPackageManagerService();
- new DexOptHelper(pm).controlDexOptBlocking(block);
+ mDexOptHelper.controlDexOptBlocking(block);
}
private void scheduleAJob(int jobId) {
@@ -511,10 +512,10 @@
}
/** Returns true if completed */
- private boolean runIdleOptimization(PackageManagerService pm, DexOptHelper dexOptHelper,
- ArraySet<String> pkgs, boolean isPostBootUpdate) {
+ private boolean runIdleOptimization(PackageManagerService pm, ArraySet<String> pkgs,
+ boolean isPostBootUpdate) {
long lowStorageThreshold = getLowStorageThreshold();
- int status = idleOptimizePackages(pm, dexOptHelper, pkgs, lowStorageThreshold,
+ int status = idleOptimizePackages(pm, pkgs, lowStorageThreshold,
isPostBootUpdate);
logStatus(status);
synchronized (mLock) {
@@ -562,8 +563,8 @@
}
@Status
- private int idleOptimizePackages(PackageManagerService pm, DexOptHelper dexOptHelper,
- ArraySet<String> pkgs, long lowStorageThreshold, boolean isPostBootUpdate) {
+ private int idleOptimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
+ long lowStorageThreshold, boolean isPostBootUpdate) {
ArraySet<String> updatedPackages = new ArraySet<>();
ArraySet<String> updatedPackagesDueToSecondaryDex = new ArraySet<>();
@@ -600,7 +601,7 @@
// Should be aborted by the scheduler.
return abortCode;
}
- @DexOptResult int downgradeResult = downgradePackage(pm, dexOptHelper, pkg,
+ @DexOptResult int downgradeResult = downgradePackage(pm, pkg,
/* isForPrimaryDex= */ true, isPostBootUpdate);
if (downgradeResult == PackageDexOptimizer.DEX_OPT_PERFORMED) {
updatedPackages.add(pkg);
@@ -611,7 +612,7 @@
return status;
}
if (supportSecondaryDex) {
- downgradeResult = downgradePackage(pm, dexOptHelper, pkg,
+ downgradeResult = downgradePackage(pm, pkg,
/* isForPrimaryDex= */false, isPostBootUpdate);
status = convertPackageDexOptimizerStatusToInternal(downgradeResult);
if (status != STATUS_OK) {
@@ -625,9 +626,8 @@
}
}
- @Status int primaryResult = optimizePackages(dexOptHelper, pkgs,
- lowStorageThreshold, /*isForPrimaryDex=*/ true, updatedPackages,
- isPostBootUpdate);
+ @Status int primaryResult = optimizePackages(pkgs, lowStorageThreshold,
+ /*isForPrimaryDex=*/ true, updatedPackages, isPostBootUpdate);
if (primaryResult != STATUS_OK) {
return primaryResult;
}
@@ -636,9 +636,9 @@
return STATUS_OK;
}
- @Status int secondaryResult = optimizePackages(dexOptHelper, pkgs,
- lowStorageThreshold, /*isForPrimaryDex*/ false,
- updatedPackagesDueToSecondaryDex, isPostBootUpdate);
+ @Status int secondaryResult = optimizePackages(pkgs, lowStorageThreshold,
+ /*isForPrimaryDex*/ false, updatedPackagesDueToSecondaryDex,
+ isPostBootUpdate);
return secondaryResult;
} finally {
// Always let the pinner service know about changes.
@@ -651,9 +651,8 @@
}
@Status
- private int optimizePackages(DexOptHelper dexOptHelper,
- ArraySet<String> pkgs, long lowStorageThreshold, boolean isForPrimaryDex,
- ArraySet<String> updatedPackages, boolean isPostBootUpdate) {
+ private int optimizePackages(ArraySet<String> pkgs, long lowStorageThreshold,
+ boolean isForPrimaryDex, ArraySet<String> updatedPackages, boolean isPostBootUpdate) {
for (String pkg : pkgs) {
int abortCode = abortIdleOptimizations(lowStorageThreshold);
if (abortCode != STATUS_OK) {
@@ -661,8 +660,7 @@
return abortCode;
}
- @DexOptResult int result = optimizePackage(dexOptHelper, pkg, isForPrimaryDex,
- isPostBootUpdate);
+ @DexOptResult int result = optimizePackage(pkg, isForPrimaryDex, isPostBootUpdate);
if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
updatedPackages.add(pkg);
} else if (result != PackageDexOptimizer.DEX_OPT_SKIPPED) {
@@ -681,8 +679,8 @@
* @return PackageDexOptimizer.DEX_*
*/
@DexOptResult
- private int downgradePackage(PackageManagerService pm, DexOptHelper dexOptHelper, String pkg,
- boolean isForPrimaryDex, boolean isPostBootUpdate) {
+ private int downgradePackage(PackageManagerService pm, String pkg, boolean isForPrimaryDex,
+ boolean isPostBootUpdate) {
if (DEBUG) {
Slog.d(TAG, "Downgrading " + pkg);
}
@@ -704,10 +702,10 @@
// remove their compiler artifacts from dalvik cache.
pm.deleteOatArtifactsOfPackage(pkg);
} else {
- result = performDexOptPrimary(dexOptHelper, pkg, reason, dexoptFlags);
+ result = performDexOptPrimary(pkg, reason, dexoptFlags);
}
} else {
- result = performDexOptSecondary(dexOptHelper, pkg, reason, dexoptFlags);
+ result = performDexOptSecondary(pkg, reason, dexoptFlags);
}
if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -733,15 +731,13 @@
*
* Optimize package if needed. Note that there can be no race between
* concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
- * @param dexOptHelper An instance of DexOptHelper
* @param pkg The package to be downgraded.
* @param isForPrimaryDex Apps can have several dex file, primary and secondary.
* @param isPostBootUpdate is post boot update or not.
* @return PackageDexOptimizer#DEX_OPT_*
*/
@DexOptResult
- private int optimizePackage(DexOptHelper dexOptHelper, String pkg,
- boolean isForPrimaryDex, boolean isPostBootUpdate) {
+ private int optimizePackage(String pkg, boolean isForPrimaryDex, boolean isPostBootUpdate) {
int reason = isPostBootUpdate ? PackageManagerService.REASON_POST_BOOT
: PackageManagerService.REASON_BACKGROUND_DEXOPT;
int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE;
@@ -753,27 +749,27 @@
// System server share the same code path as primary dex files.
// PackageManagerService will select the right optimization path for it.
if (isForPrimaryDex || PLATFORM_PACKAGE_NAME.equals(pkg)) {
- return performDexOptPrimary(dexOptHelper, pkg, reason, dexoptFlags);
+ return performDexOptPrimary(pkg, reason, dexoptFlags);
} else {
- return performDexOptSecondary(dexOptHelper, pkg, reason, dexoptFlags);
+ return performDexOptSecondary(pkg, reason, dexoptFlags);
}
}
@DexOptResult
- private int performDexOptPrimary(DexOptHelper dexOptHelper, String pkg, int reason,
+ private int performDexOptPrimary(String pkg, int reason,
int dexoptFlags) {
return trackPerformDexOpt(pkg, /*isForPrimaryDex=*/ true,
- () -> dexOptHelper.performDexOptWithStatus(
+ () -> mDexOptHelper.performDexOptWithStatus(
new DexoptOptions(pkg, reason, dexoptFlags)));
}
@DexOptResult
- private int performDexOptSecondary(DexOptHelper dexOptHelper, String pkg, int reason,
+ private int performDexOptSecondary(String pkg, int reason,
int dexoptFlags) {
DexoptOptions dexoptOptions = new DexoptOptions(pkg, reason,
dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX);
return trackPerformDexOpt(pkg, /*isForPrimaryDex=*/ false,
- () -> dexOptHelper.performDexOpt(dexoptOptions)
+ () -> mDexOptHelper.performDexOpt(dexoptOptions)
? PackageDexOptimizer.DEX_OPT_PERFORMED : PackageDexOptimizer.DEX_OPT_FAILED
);
}
@@ -911,11 +907,13 @@
static final class Injector {
private final Context mContext;
private final DexManager mDexManager;
+ private final PackageManagerService mPackageManagerService;
private final File mDataDir = Environment.getDataDirectory();
- Injector(Context context, DexManager dexManager) {
+ Injector(Context context, DexManager dexManager, PackageManagerService pm) {
mContext = context;
mDexManager = dexManager;
+ mPackageManagerService = pm;
}
Context getContext() {
@@ -923,7 +921,11 @@
}
PackageManagerService getPackageManagerService() {
- return (PackageManagerService) ServiceManager.getService("package");
+ return mPackageManagerService;
+ }
+
+ DexOptHelper getDexOptHelper() {
+ return new DexOptHelper(getPackageManagerService());
}
JobScheduler getJobScheduler() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index accb7a0..5f56d4e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1486,7 +1486,7 @@
new DefaultSystemWrapper(),
LocalServices::getService,
context::getSystemService,
- (i, pm) -> new BackgroundDexOptService(i.getContext(), i.getDexManager()));
+ (i, pm) -> new BackgroundDexOptService(i.getContext(), i.getDexManager(), pm));
if (Build.VERSION.SDK_INT <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 8644d1e..5b062fc 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2755,14 +2755,13 @@
return false;
}
- boolean isKeyguardLocked = mAtmService.isKeyguardLocked();
boolean isCurrentAppLocked =
mAtmService.getLockTaskModeState() != LOCK_TASK_MODE_NONE;
final TaskDisplayArea taskDisplayArea = getDisplayArea();
boolean hasRootPinnedTask = taskDisplayArea != null && taskDisplayArea.hasPinnedTask();
// Don't return early if !isNotLocked, since we want to throw an exception if the activity
// is in an incorrect state
- boolean isNotLockedOrOnKeyguard = !isKeyguardLocked && !isCurrentAppLocked;
+ boolean isNotLockedOrOnKeyguard = !isKeyguardLocked() && !isCurrentAppLocked;
// We don't allow auto-PiP when something else is already pipped.
if (beforeStopping && hasRootPinnedTask) {
@@ -3161,7 +3160,7 @@
// If the current activity is not opaque, we need to make sure the visibilities of
// activities be updated, they may be seen by users.
ensureVisibility = true;
- } else if (mTaskSupervisor.getKeyguardController().isKeyguardLocked()
+ } else if (isKeyguardLocked()
&& mTaskSupervisor.getKeyguardController().topActivityOccludesKeyguard(this)) {
// Ensure activity visibilities and update lockscreen occluded/dismiss state when
// finishing the top activity that occluded keyguard. So that, the
@@ -3990,6 +3989,11 @@
});
}
+ boolean isKeyguardLocked() {
+ return (mDisplayContent != null) ? mDisplayContent.isKeyguardLocked() :
+ mRootWindowContainer.getDefaultDisplay().isKeyguardLocked();
+ }
+
void checkKeyguardFlagsChanged() {
final boolean containsDismissKeyguard = containsDismissKeyguardWindow();
final boolean containsShowWhenLocked = containsShowWhenLockedWindow();
@@ -5179,7 +5183,7 @@
void notifyUnknownVisibilityLaunchedForKeyguardTransition() {
// No display activities never add a window, so there is no point in waiting them for
// relayout.
- if (noDisplay || !mTaskSupervisor.getKeyguardController().isKeyguardLocked()) {
+ if (noDisplay || !isKeyguardLocked()) {
return;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 9455ce6..0a85ba1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2799,7 +2799,10 @@
mH.sendMessage(msg);
}
try {
- mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing);
+ mRootWindowContainer.forAllDisplays(displayContent -> {
+ mKeyguardController.setKeyguardShown(displayContent.getDisplayId(),
+ keyguardShowing, aodShowing);
+ });
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -3359,7 +3362,9 @@
final long token = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
- mKeyguardController.keyguardGoingAway(flags);
+ mRootWindowContainer.forAllDisplays(displayContent -> {
+ mKeyguardController.keyguardGoingAway(displayContent.getDisplayId(), flags);
+ });
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -3442,7 +3447,7 @@
}
};
- if (isKeyguardLocked()) {
+ if (r.isKeyguardLocked()) {
// If the keyguard is showing or occluded, then try and dismiss it before
// entering picture-in-picture (this will prompt the user to authenticate if the
// device is currently locked).
@@ -3787,8 +3792,8 @@
mRecentTasks.notifyTaskPersisterLocked(task, flush);
}
- boolean isKeyguardLocked() {
- return mKeyguardController.isKeyguardLocked();
+ boolean isKeyguardLocked(int displayId) {
+ return mKeyguardController.isKeyguardLocked(displayId);
}
/**
@@ -6035,7 +6040,7 @@
final long ident = Binder.clearCallingIdentity();
try {
if (mAmInternal.shouldConfirmCredentials(userId)) {
- if (mKeyguardController.isKeyguardLocked()) {
+ if (mKeyguardController.isKeyguardLocked(DEFAULT_DISPLAY)) {
// Showing launcher to avoid user entering credential twice.
startHomeActivity(currentUserId, "notifyLockedProfile");
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 7485a1e..63fb793 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -560,7 +560,7 @@
// Ignore the orientation of keyguard if it is going away or is not showing while
// the device is fully awake. In other words, use the orientation of keyguard if
// its window is visible while the device is going to sleep or is sleeping.
- if (!mWmService.mAtmService.isKeyguardLocked()
+ if (!mDisplayContent.isKeyguardLocked()
&& mDisplayContent.getDisplayPolicy().isAwake()
// Device is not going to sleep.
&& policy.okToAnimate(true /* ignoreScreenOn */)) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7a38554..4cb70fd 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5841,6 +5841,30 @@
return (flags & FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0;
}
+ /**
+ * @return whether keyguard is locked for this display
+ */
+ boolean isKeyguardLocked() {
+ return mRootWindowContainer.mTaskSupervisor
+ .getKeyguardController().isKeyguardLocked(mDisplayId);
+ }
+
+ /**
+ * @return whether keyguard is going away on this display
+ */
+ boolean isKeyguardGoingAway() {
+ return mRootWindowContainer.mTaskSupervisor
+ .getKeyguardController().isKeyguardGoingAway(mDisplayId);
+ }
+
+ /**
+ * @return whether AOD is showing on this display
+ */
+ boolean isAodShowing() {
+ return mRootWindowContainer.mTaskSupervisor
+ .getKeyguardController().isAodShowing(mDisplayId);
+ }
+
@VisibleForTesting
void removeAllTasks() {
forAllTasks((t) -> { t.getRootTask().removeChild(t, "removeAllTasks"); });
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 2ea6334..e0dc4e4 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1730,7 +1730,7 @@
// drawing. This way, we know that the IME can be safely shown since the other windows are
// now shown.
final boolean hideIme = win.mIsImWindow
- && (mService.mAtmService.mKeyguardController.isAodShowing()
+ && (mDisplayContent.isAodShowing()
|| (mDisplayContent.isDefaultDisplay && !mWindowManagerDrawComplete));
if (hideIme) {
return true;
diff --git a/services/core/java/com/android/server/wm/EventLogTags.logtags b/services/core/java/com/android/server/wm/EventLogTags.logtags
index 40b80f7..e228a4b 100644
--- a/services/core/java/com/android/server/wm/EventLogTags.logtags
+++ b/services/core/java/com/android/server/wm/EventLogTags.logtags
@@ -52,7 +52,7 @@
30066 wm_add_to_stopping (User|1|5),(Token|1|5),(Component Name|3),(Reason|3)
# Keyguard status changed
-30067 wm_set_keyguard_shown (keyguardShowing|1),(aodShowing|1),(keyguardGoingAway|1),(Reason|3)
+30067 wm_set_keyguard_shown (Display Id|1|5),(keyguardShowing|1),(aodShowing|1),(keyguardGoingAway|1),(Reason|3)
# Out of memory for surfaces.
31000 wm_no_surface_memory (Window|3),(PID|1|5),(Operation|3)
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index d17c109..83fd3fa 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -497,12 +497,16 @@
resetInputConsumers(mInputTransaction);
// Update recents input consumer layer if active
- if (mAddRecentsAnimationInputConsumerHandle
- && getWeak(mActiveRecentsActivity) != null) {
- final WindowContainer layer = getWeak(mActiveRecentsLayerRef);
- mRecentsAnimationInputConsumer.show(mInputTransaction,
- layer != null ? layer : getWeak(mActiveRecentsActivity));
- mAddRecentsAnimationInputConsumerHandle = false;
+ final ActivityRecord activeRecents = getWeak(mActiveRecentsActivity);
+ if (mAddRecentsAnimationInputConsumerHandle && activeRecents != null
+ && activeRecents.getSurfaceControl() != null) {
+ WindowContainer layer = getWeak(mActiveRecentsLayerRef);
+ layer = layer != null ? layer : activeRecents;
+ // Handle edge-case for SUW where windows don't exist yet
+ if (layer.getSurfaceControl() != null) {
+ mRecentsAnimationInputConsumer.show(mInputTransaction, layer);
+ mAddRecentsAnimationInputConsumerHandle = false;
+ }
}
mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */);
updateInputFocusRequest(mRecentsAnimationInputConsumer);
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index bd41de3..fee9884 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -38,10 +38,8 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.KeyguardControllerProto.AOD_SHOWING;
-import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES;
+import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_PER_DISPLAY;
import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_SHOWING;
-import static com.android.server.wm.KeyguardOccludedProto.DISPLAY_ID;
-import static com.android.server.wm.KeyguardOccludedProto.KEYGUARD_OCCLUDED;
import android.annotation.Nullable;
import android.os.IBinder;
@@ -73,10 +71,7 @@
private final ActivityTaskSupervisor mTaskSupervisor;
private WindowManagerService mWindowManager;
- private boolean mKeyguardShowing;
- private boolean mAodShowing;
- private boolean mKeyguardGoingAway;
- private boolean mDismissalRequested;
+
private final SparseArray<KeyguardDisplayState> mDisplayStates = new SparseArray<>();
private final ActivityTaskManagerService mService;
private RootWindowContainer mRootWindowContainer;
@@ -95,8 +90,8 @@
mRootWindowContainer = mService.mRootWindowContainer;
}
- boolean isAodShowing() {
- return mAodShowing;
+ boolean isAodShowing(int displayId) {
+ return getDisplayState(displayId).mAodShowing;
}
/**
@@ -104,7 +99,9 @@
* on the given display, false otherwise.
*/
boolean isKeyguardOrAodShowing(int displayId) {
- return (mKeyguardShowing || mAodShowing) && !mKeyguardGoingAway
+ final KeyguardDisplayState state = getDisplayState(displayId);
+ return (state.mKeyguardShowing || state.mAodShowing)
+ && !state.mKeyguardGoingAway
&& !isDisplayOccluded(displayId);
}
@@ -114,8 +111,9 @@
* TODO(b/125198167): Replace isKeyguardOrAodShowing() by this logic.
*/
boolean isKeyguardUnoccludedOrAodShowing(int displayId) {
- if (displayId == DEFAULT_DISPLAY && mAodShowing) {
- return !mKeyguardGoingAway;
+ final KeyguardDisplayState state = getDisplayState(displayId);
+ if (displayId == DEFAULT_DISPLAY && state.mAodShowing) {
+ return !state.mKeyguardGoingAway;
}
return isKeyguardOrAodShowing(displayId);
}
@@ -125,14 +123,17 @@
* display, false otherwise
*/
boolean isKeyguardShowing(int displayId) {
- return mKeyguardShowing && !mKeyguardGoingAway && !isDisplayOccluded(displayId);
+ final KeyguardDisplayState state = getDisplayState(displayId);
+ return state.mKeyguardShowing && !state.mKeyguardGoingAway
+ && !isDisplayOccluded(displayId);
}
/**
* @return true if Keyguard is either showing or occluded, but not going away
*/
- boolean isKeyguardLocked() {
- return mKeyguardShowing && !mKeyguardGoingAway;
+ boolean isKeyguardLocked(int displayId) {
+ final KeyguardDisplayState state = getDisplayState(displayId);
+ return state.mKeyguardShowing && !state.mKeyguardGoingAway;
}
/**
@@ -146,28 +147,31 @@
/**
* @return {@code true} if the keyguard is going away, {@code false} otherwise.
*/
- boolean isKeyguardGoingAway() {
+ boolean isKeyguardGoingAway(int displayId) {
+ final KeyguardDisplayState state = getDisplayState(displayId);
// Also check keyguard showing in case value is stale.
- return mKeyguardGoingAway && mKeyguardShowing;
+ return state.mKeyguardGoingAway && state.mKeyguardShowing;
}
/**
* Update the Keyguard showing state.
*/
- void setKeyguardShown(boolean keyguardShowing, boolean aodShowing) {
- final boolean aodChanged = aodShowing != mAodShowing;
+ void setKeyguardShown(int displayId, boolean keyguardShowing, boolean aodShowing) {
+ final KeyguardDisplayState state = getDisplayState(displayId);
+ final boolean aodChanged = aodShowing != state.mAodShowing;
// If keyguard is going away, but SystemUI aborted the transition, need to reset state.
// Do not reset keyguardChanged status if this is aodChanged.
- final boolean keyguardChanged = (keyguardShowing != mKeyguardShowing)
- || (mKeyguardGoingAway && keyguardShowing && !aodChanged);
+ final boolean keyguardChanged = (keyguardShowing != state.mKeyguardShowing)
+ || (state.mKeyguardGoingAway && keyguardShowing && !aodChanged);
if (!keyguardChanged && !aodChanged) {
setWakeTransitionReady();
return;
}
EventLogTags.writeWmSetKeyguardShown(
+ displayId,
keyguardShowing ? 1 : 0,
aodShowing ? 1 : 0,
- mKeyguardGoingAway ? 1 : 0,
+ state.mKeyguardGoingAway ? 1 : 0,
"setKeyguardShown");
// Update the task snapshot if the screen will not be turned off. To make sure that the
@@ -180,13 +184,13 @@
// - The display state is ON. Because if AOD is not on or pulsing, the display state will
// be OFF or DOZE (the path of screen off may have handled it).
if (((aodShowing ^ keyguardShowing) || (aodShowing && aodChanged && keyguardChanged))
- && !mKeyguardGoingAway && Display.isOnState(
+ && !state.mKeyguardGoingAway && Display.isOnState(
mRootWindowContainer.getDefaultDisplay().getDisplayInfo().state)) {
mWindowManager.mTaskSnapshotController.snapshotForSleeping(DEFAULT_DISPLAY);
}
- mKeyguardShowing = keyguardShowing;
- mAodShowing = aodShowing;
+ state.mKeyguardShowing = keyguardShowing;
+ state.mAodShowing = aodShowing;
if (aodChanged) {
// Ensure the new state takes effect.
mWindowManager.mWindowPlacerLocked.performSurfacePlacement();
@@ -194,10 +198,11 @@
if (keyguardChanged) {
// Irrelevant to AOD.
- dismissMultiWindowModeForTaskIfNeeded(null /* currentTaskControllsingOcclusion */);
- mKeyguardGoingAway = false;
+ dismissMultiWindowModeForTaskIfNeeded(displayId,
+ null /* currentTaskControllsingOcclusion */);
+ state.mKeyguardGoingAway = false;
if (keyguardShowing) {
- mDismissalRequested = false;
+ state.mDismissalRequested = false;
}
}
@@ -223,17 +228,19 @@
* @param flags See {@link WindowManagerPolicy#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE}
* etc.
*/
- void keyguardGoingAway(int flags) {
- if (!mKeyguardShowing) {
+ void keyguardGoingAway(int displayId, int flags) {
+ final KeyguardDisplayState state = getDisplayState(displayId);
+ if (!state.mKeyguardShowing) {
return;
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "keyguardGoingAway");
mService.deferWindowLayout();
- mKeyguardGoingAway = true;
+ state.mKeyguardGoingAway = true;
try {
EventLogTags.writeWmSetKeyguardShown(
+ displayId,
1 /* keyguardShowing */,
- mAodShowing ? 1 : 0,
+ state.mAodShowing ? 1 : 0,
1 /* keyguardGoingAway */,
"keyguardGoingAway");
final int transitFlags = convertTransitFlags(flags);
@@ -307,10 +314,10 @@
// Allow to show it when we are about to dismiss Keyguard. This isn't allowed if r is
// already the dismissing activity, in which case we don't allow it to repeatedly dismiss
// Keyguard.
- return r.containsDismissKeyguardWindow() && canDismissKeyguard() && !mAodShowing
- && (mDismissalRequested
- || (r.canShowWhenLocked()
- && getDisplayState(r.getDisplayId()).mDismissingKeyguardActivity != r));
+ final KeyguardDisplayState state = getDisplayState(r.getDisplayId());
+ return r.containsDismissKeyguardWindow() && canDismissKeyguard() && !state.mAodShowing
+ && (state.mDismissalRequested
+ || (r.canShowWhenLocked() && state.mDismissingKeyguardActivity != r));
}
/**
@@ -335,7 +342,7 @@
// If keyguard is showing, nothing is visible, except if we are able to dismiss Keyguard
// right away and AOD isn't visible.
return canShowActivityWhileKeyguardShowing(r);
- } else if (isKeyguardLocked()) {
+ } else if (isKeyguardLocked(r.getDisplayId())) {
return canShowWhileOccluded(r.containsDismissKeyguardWindow(), r.canShowWhenLocked());
} else {
return true;
@@ -348,19 +355,15 @@
* ({@link ActivityTaskSupervisor#beginActivityVisibilityUpdate}).
*/
void updateVisibility() {
- boolean requestDismissKeyguard = false;
for (int displayNdx = mRootWindowContainer.getChildCount() - 1;
displayNdx >= 0; displayNdx--) {
final DisplayContent display = mRootWindowContainer.getChildAt(displayNdx);
if (display.isRemoving() || display.isRemoved()) continue;
final KeyguardDisplayState state = getDisplayState(display.mDisplayId);
state.updateVisibility(this, display);
- requestDismissKeyguard |= state.mRequestDismissKeyguard;
- }
-
- // Dismissing Keyguard happens globally using the information from all displays.
- if (requestDismissKeyguard) {
- handleDismissKeyguard();
+ if (state.mRequestDismissKeyguard) {
+ handleDismissKeyguard(display.getDisplayId());
+ }
}
}
@@ -381,7 +384,7 @@
}
mWindowManager.mPolicy.onKeyguardOccludedChangedLw(isDisplayOccluded(DEFAULT_DISPLAY));
- if (isKeyguardLocked()) {
+ if (isKeyguardLocked(displayId)) {
mService.deferWindowLayout();
try {
mRootWindowContainer.getDefaultDisplay()
@@ -395,14 +398,14 @@
mService.continueWindowLayout();
}
}
- dismissMultiWindowModeForTaskIfNeeded(topActivity != null
+ dismissMultiWindowModeForTaskIfNeeded(displayId, topActivity != null
? topActivity.getRootTask() : null);
}
/**
* Called when somebody wants to dismiss the Keyguard via the flag.
*/
- private void handleDismissKeyguard() {
+ private void handleDismissKeyguard(int displayId) {
// We only allow dismissing Keyguard via the flag when Keyguard is secure for legacy
// reasons, because that's how apps used to dismiss Keyguard in the secure case. In the
// insecure case, we actually show it on top of the lockscreen. See #canShowWhileOccluded.
@@ -411,12 +414,13 @@
}
mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
- mDismissalRequested = true;
+ final KeyguardDisplayState state = getDisplayState(displayId);
+ state.mDismissalRequested = true;
// If we are about to unocclude the Keyguard, but we can dismiss it without security,
// we immediately dismiss the Keyguard so the activity gets shown without a flicker.
final DisplayContent dc = mRootWindowContainer.getDefaultDisplay();
- if (mKeyguardShowing && canDismissKeyguard()
+ if (state.mKeyguardShowing && canDismissKeyguard()
&& dc.mAppTransition.containsTransitRequest(TRANSIT_KEYGUARD_UNOCCLUDE)) {
mWindowManager.executeAppTransition();
}
@@ -434,10 +438,10 @@
|| !mWindowManager.isKeyguardSecure(mService.getCurrentUserId());
}
- private void dismissMultiWindowModeForTaskIfNeeded(
+ private void dismissMultiWindowModeForTaskIfNeeded(int displayId,
@Nullable Task currentTaskControllingOcclusion) {
// TODO(b/113840485): Handle docked stack for individual display.
- if (!mKeyguardShowing || !isDisplayOccluded(DEFAULT_DISPLAY)) {
+ if (!getDisplayState(displayId).mKeyguardShowing || !isDisplayOccluded(DEFAULT_DISPLAY)) {
return;
}
@@ -497,6 +501,10 @@
/** Represents Keyguard state per individual display. */
private static class KeyguardDisplayState {
private final int mDisplayId;
+ private boolean mKeyguardShowing;
+ private boolean mAodShowing;
+ private boolean mKeyguardGoingAway;
+ private boolean mDismissalRequested;
private boolean mOccluded;
private ActivityRecord mTopOccludesActivity;
@@ -604,7 +612,16 @@
void dumpStatus(PrintWriter pw, String prefix) {
final StringBuilder sb = new StringBuilder();
sb.append(prefix);
- sb.append(" Occluded=").append(mOccluded)
+ sb.append(" KeyguardShowing=")
+ .append(mKeyguardShowing)
+ .append(" AodShowing=")
+ .append(mAodShowing)
+ .append(" KeyguardGoingAway=")
+ .append(mKeyguardGoingAway)
+ .append(" DismissalRequested=")
+ .append(mDismissalRequested)
+ .append(" Occluded=")
+ .append(mOccluded)
.append(" DismissingKeyguardActivity=")
.append(mDismissingKeyguardActivity)
.append(" TurnScreenOnActivity=")
@@ -616,27 +633,31 @@
void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
- proto.write(DISPLAY_ID, mDisplayId);
- proto.write(KEYGUARD_OCCLUDED, mOccluded);
+ proto.write(KeyguardPerDisplayProto.DISPLAY_ID, mDisplayId);
+ proto.write(KeyguardPerDisplayProto.KEYGUARD_SHOWING, mKeyguardShowing);
+ proto.write(KeyguardPerDisplayProto.AOD_SHOWING, mAodShowing);
+ proto.write(KeyguardPerDisplayProto.KEYGUARD_OCCLUDED, mOccluded);
proto.end(token);
}
}
void dump(PrintWriter pw, String prefix) {
+ final KeyguardDisplayState default_state = getDisplayState(DEFAULT_DISPLAY);
pw.println(prefix + "KeyguardController:");
- pw.println(prefix + " mKeyguardShowing=" + mKeyguardShowing);
- pw.println(prefix + " mAodShowing=" + mAodShowing);
- pw.println(prefix + " mKeyguardGoingAway=" + mKeyguardGoingAway);
+ pw.println(prefix + " mKeyguardShowing=" + default_state.mKeyguardShowing);
+ pw.println(prefix + " mAodShowing=" + default_state.mAodShowing);
+ pw.println(prefix + " mKeyguardGoingAway=" + default_state.mKeyguardGoingAway);
dumpDisplayStates(pw, prefix);
- pw.println(prefix + " mDismissalRequested=" + mDismissalRequested);
+ pw.println(prefix + " mDismissalRequested=" + default_state.mDismissalRequested);
pw.println();
}
void dumpDebug(ProtoOutputStream proto, long fieldId) {
+ final KeyguardDisplayState default_state = getDisplayState(DEFAULT_DISPLAY);
final long token = proto.start(fieldId);
- proto.write(AOD_SHOWING, mAodShowing);
- proto.write(KEYGUARD_SHOWING, mKeyguardShowing);
- writeDisplayStatesToProto(proto, KEYGUARD_OCCLUDED_STATES);
+ proto.write(AOD_SHOWING, default_state.mAodShowing);
+ proto.write(KEYGUARD_SHOWING, default_state.mKeyguardShowing);
+ writeDisplayStatesToProto(proto, KEYGUARD_PER_DISPLAY);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index c28d089..ccee458 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3480,7 +3480,9 @@
// avoid power mode from being cleared before that, add a special reason to consider whether
// the unknown visibility is resolved. The case from SystemUI is excluded because it should
// rely on keyguard-going-away.
- if (mService.mKeyguardController.isKeyguardLocked() && targetActivity != null
+ final boolean isKeyguardLocked = (targetActivity != null)
+ ? targetActivity.isKeyguardLocked() : mDefaultDisplay.isKeyguardLocked();
+ if (isKeyguardLocked && targetActivity != null
&& !targetActivity.isLaunchSourceType(ActivityRecord.LAUNCH_SOURCE_TYPE_SYSTEMUI)) {
final ActivityOptions opts = targetActivity.getOptions();
if (opts == null || opts.getSourceInfo() == null
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 35d93f6..bfb1a8e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -6080,11 +6080,13 @@
boolean shouldSleepActivities() {
final DisplayContent display = mDisplayContent;
+ final boolean isKeyguardGoingAway = (mDisplayContent != null)
+ ? mDisplayContent.isKeyguardGoingAway()
+ : mRootWindowContainer.getDefaultDisplay().isKeyguardGoingAway();
// Do not sleep activities in this root task if we're marked as focused and the keyguard
// is in the process of going away.
- if (mTaskSupervisor.getKeyguardController().isKeyguardGoingAway()
- && isFocusedRootTaskOnDisplay()
+ if (isKeyguardGoingAway && isFocusedRootTaskOnDisplay()
// Avoid resuming activities on secondary displays since we don't want bubble
// activities to be resumed while bubble is still collapsed.
// TODO(b/113840485): Having keyguard going away state for secondary displays.
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 3387e37..600545e 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1728,7 +1728,7 @@
// the locked state, the keyguard isn't locked, or we can show when locked.
if (topRunning != null && considerKeyguardState
&& mRootWindowContainer.mTaskSupervisor.getKeyguardController()
- .isKeyguardLocked()
+ .isKeyguardLocked(topRunning.getDisplayId())
&& !topRunning.canShowWhenLocked()) {
return null;
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 4db8ef4..f175eec 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -513,7 +513,7 @@
mState = STATE_PLAYING;
mController.moveToPlaying(this);
- if (mController.mAtm.mTaskSupervisor.getKeyguardController().isKeyguardLocked()) {
+ if (mController.mAtm.mTaskSupervisor.getKeyguardController().isKeyguardLocked(displayId)) {
mFlags |= TRANSIT_FLAG_KEYGUARD_LOCKED;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java
new file mode 100644
index 0000000..8223b8c
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/BackgroundDexOptServiceUnitTest.java
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2021 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.pm;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.HandlerThread;
+import android.os.PowerManager;
+import android.util.ArraySet;
+
+import com.android.server.LocalServices;
+import com.android.server.PinnerService;
+import com.android.server.pm.dex.DexManager;
+import com.android.server.pm.dex.DexoptOptions;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.stream.Collectors;
+
+@RunWith(MockitoJUnitRunner.class)
+public final class BackgroundDexOptServiceUnitTest {
+ private static final String TAG = BackgroundDexOptServiceUnitTest.class.getSimpleName();
+
+ private static final long USABLE_SPACE_NORMAL = 1_000_000_000;
+ private static final long STORAGE_LOW_BYTES = 1_000_000;
+
+ private static final long TEST_WAIT_TIMEOUT_MS = 10_000;
+
+ private static final ArraySet<String> DEFAULT_PACKAGE_LIST = new ArraySet<>(
+ Arrays.asList("aaa", "bbb"));
+ private static final ArraySet<String> EMPTY_PACKAGE_LIST = new ArraySet<>();
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private PackageManagerService mPackageManager;
+ @Mock
+ private DexOptHelper mDexOptHelper;
+ @Mock
+ private DexManager mDexManager;
+ @Mock
+ private PinnerService mPinnerService;
+ @Mock
+ private JobScheduler mJobScheduler;
+ @Mock
+ private BackgroundDexOptService.Injector mInjector;
+ @Mock
+ private BackgroundDexOptJobService mJobServiceForPostBoot;
+ @Mock
+ private BackgroundDexOptJobService mJobServiceForIdle;
+
+ private final JobParameters mJobParametersForPostBoot = new JobParameters(null,
+ BackgroundDexOptService.JOB_POST_BOOT_UPDATE, null, null, null,
+ 0, false, false, null, null, null);
+ private final JobParameters mJobParametersForIdle = new JobParameters(null,
+ BackgroundDexOptService.JOB_IDLE_OPTIMIZE, null, null, null,
+ 0, false, false, null, null, null);
+
+ private BackgroundDexOptService mService;
+
+ private StartAndWaitThread mDexOptThread;
+ private StartAndWaitThread mCancelThread;
+
+ @Before
+ public void setUp() throws Exception {
+ when(mInjector.getContext()).thenReturn(mContext);
+ when(mInjector.getDexOptHelper()).thenReturn(mDexOptHelper);
+ when(mInjector.getDexManager()).thenReturn(mDexManager);
+ when(mInjector.getPinnerService()).thenReturn(mPinnerService);
+ when(mInjector.getJobScheduler()).thenReturn(mJobScheduler);
+ when(mInjector.getPackageManagerService()).thenReturn(mPackageManager);
+
+ // These mocking can be overwritten in some tests but still keep it here as alternative
+ // takes too many repetitive codes.
+ when(mInjector.getDataDirUsableSpace()).thenReturn(USABLE_SPACE_NORMAL);
+ when(mInjector.getDataDirStorageLowBytes()).thenReturn(STORAGE_LOW_BYTES);
+ when(mInjector.getDexOptThermalCutoff()).thenReturn(PowerManager.THERMAL_STATUS_CRITICAL);
+ when(mInjector.getCurrentThermalStatus()).thenReturn(PowerManager.THERMAL_STATUS_NONE);
+ when(mDexOptHelper.getOptimizablePackages()).thenReturn(DEFAULT_PACKAGE_LIST);
+ when(mDexOptHelper.performDexOptWithStatus(any())).thenReturn(
+ PackageDexOptimizer.DEX_OPT_PERFORMED);
+
+ mService = new BackgroundDexOptService(mInjector);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ LocalServices.removeServiceForTest(BackgroundDexOptService.class);
+ }
+
+ @Test
+ public void testGetService() {
+ assertThat(BackgroundDexOptService.getService()).isEqualTo(mService);
+ }
+
+ @Test
+ public void testBootCompleted() {
+ initUntilBootCompleted();
+ }
+
+ @Test
+ public void testNoExecutionForIdleJobBeforePostBootUpdate() {
+ initUntilBootCompleted();
+
+ assertThat(mService.onStartJob(mJobServiceForIdle, mJobParametersForIdle)).isFalse();
+ }
+
+ @Test
+ public void testNoExecutionForLowStorage() {
+ initUntilBootCompleted();
+ when(mPackageManager.isStorageLow()).thenReturn(true);
+
+ assertThat(mService.onStartJob(mJobServiceForPostBoot,
+ mJobParametersForPostBoot)).isFalse();
+ verify(mDexOptHelper, never()).performDexOpt(any());
+ }
+
+ @Test
+ public void testNoExecutionForNoOptimizablePackages() {
+ initUntilBootCompleted();
+ when(mDexOptHelper.getOptimizablePackages()).thenReturn(EMPTY_PACKAGE_LIST);
+
+ assertThat(mService.onStartJob(mJobServiceForPostBoot,
+ mJobParametersForPostBoot)).isFalse();
+ verify(mDexOptHelper, never()).performDexOpt(any());
+ }
+
+ @Test
+ public void testPostBootUpdateFullRun() {
+ initUntilBootCompleted();
+
+ runFullJob(mJobServiceForPostBoot, mJobParametersForPostBoot, false, 1);
+ }
+
+ @Test
+ public void testIdleJobFullRun() {
+ initUntilBootCompleted();
+
+ runFullJob(mJobServiceForPostBoot, mJobParametersForPostBoot, false, 1);
+ runFullJob(mJobServiceForIdle, mJobParametersForIdle, true, 2);
+ }
+
+ @Test
+ public void testSystemReadyWhenDisabled() {
+ when(mInjector.isBackgroundDexOptDisabled()).thenReturn(true);
+
+ mService.systemReady();
+
+ verify(mContext, never()).registerReceiver(any(), any());
+ }
+
+ @Test
+ public void testStopByCancelFlag() {
+ when(mInjector.createAndStartThread(any(), any())).thenReturn(Thread.currentThread());
+ initUntilBootCompleted();
+
+ assertThat(mService.onStartJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue();
+
+ ArgumentCaptor<Runnable> argDexOptThreadRunnable = ArgumentCaptor.forClass(Runnable.class);
+ verify(mInjector, atLeastOnce()).createAndStartThread(any(),
+ argDexOptThreadRunnable.capture());
+
+ // Stopping requires a separate thread
+ HandlerThread cancelThread = new HandlerThread("Stopping");
+ cancelThread.start();
+ when(mInjector.createAndStartThread(any(), any())).thenReturn(cancelThread);
+
+ // Cancel
+ assertThat(mService.onStopJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue();
+
+ // Capture Runnable for cancel
+ ArgumentCaptor<Runnable> argCancelThreadRunnable = ArgumentCaptor.forClass(Runnable.class);
+ verify(mInjector, atLeastOnce()).createAndStartThread(any(),
+ argCancelThreadRunnable.capture());
+
+ // Execute cancelling part
+ cancelThread.getThreadHandler().post(argCancelThreadRunnable.getValue());
+
+ verify(mDexOptHelper, timeout(TEST_WAIT_TIMEOUT_MS)).controlDexOptBlocking(true);
+
+ // Dexopt thread run and cancelled
+ argDexOptThreadRunnable.getValue().run();
+
+ // Wait until cancellation Runnable is completed.
+ assertThat(cancelThread.getThreadHandler().runWithScissors(
+ argCancelThreadRunnable.getValue(), TEST_WAIT_TIMEOUT_MS)).isTrue();
+
+ // Now cancel completed
+ verify(mJobServiceForPostBoot).jobFinished(mJobParametersForPostBoot, true);
+ verifyLastControlDexOptBlockingCall(false);
+ }
+
+ @Test
+ public void testPostUpdateCancelFirst() throws Exception {
+ initUntilBootCompleted();
+ when(mInjector.createAndStartThread(any(), any())).thenAnswer(
+ i -> createAndStartExecutionThread(i.getArgument(0), i.getArgument(1)));
+
+ // Start
+ assertThat(mService.onStartJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue();
+ // Cancel
+ assertThat(mService.onStopJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue();
+
+ mCancelThread.runActualRunnable();
+
+ // Wait until cancel has set the flag.
+ verify(mDexOptHelper, timeout(TEST_WAIT_TIMEOUT_MS)).controlDexOptBlocking(
+ true);
+
+ mDexOptThread.runActualRunnable();
+
+ // All threads should finish.
+ mDexOptThread.join(TEST_WAIT_TIMEOUT_MS);
+ mCancelThread.join(TEST_WAIT_TIMEOUT_MS);
+
+ // Retry later if post boot job was cancelled
+ verify(mJobServiceForPostBoot).jobFinished(mJobParametersForPostBoot, true);
+ verifyLastControlDexOptBlockingCall(false);
+ }
+
+ @Test
+ public void testPostUpdateCancelLater() throws Exception {
+ initUntilBootCompleted();
+ when(mInjector.createAndStartThread(any(), any())).thenAnswer(
+ i -> createAndStartExecutionThread(i.getArgument(0), i.getArgument(1)));
+
+ // Start
+ assertThat(mService.onStartJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue();
+ // Cancel
+ assertThat(mService.onStopJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue();
+
+ // Dexopt thread runs and finishes
+ mDexOptThread.runActualRunnable();
+ mDexOptThread.join(TEST_WAIT_TIMEOUT_MS);
+
+ mCancelThread.runActualRunnable();
+ mCancelThread.join(TEST_WAIT_TIMEOUT_MS);
+
+ // Already completed before cancel, so no rescheduling.
+ verify(mJobServiceForPostBoot).jobFinished(mJobParametersForPostBoot, false);
+ verify(mDexOptHelper, never()).controlDexOptBlocking(true);
+ }
+
+ @Test
+ public void testPeriodicJobCancelFirst() throws Exception {
+ initUntilBootCompleted();
+ when(mInjector.createAndStartThread(any(), any())).thenAnswer(
+ i -> createAndStartExecutionThread(i.getArgument(0), i.getArgument(1)));
+
+ // Start and finish post boot job
+ assertThat(mService.onStartJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue();
+ mDexOptThread.runActualRunnable();
+ mDexOptThread.join(TEST_WAIT_TIMEOUT_MS);
+
+ // Start
+ assertThat(mService.onStartJob(mJobServiceForIdle, mJobParametersForIdle)).isTrue();
+ // Cancel
+ assertThat(mService.onStopJob(mJobServiceForIdle, mJobParametersForIdle)).isTrue();
+
+ mCancelThread.runActualRunnable();
+
+ // Wait until cancel has set the flag.
+ verify(mDexOptHelper, timeout(TEST_WAIT_TIMEOUT_MS)).controlDexOptBlocking(
+ true);
+
+ mDexOptThread.runActualRunnable();
+
+ // All threads should finish.
+ mDexOptThread.join(TEST_WAIT_TIMEOUT_MS);
+ mCancelThread.join(TEST_WAIT_TIMEOUT_MS);
+
+ // Always reschedule for periodic job
+ verify(mJobServiceForIdle).jobFinished(mJobParametersForIdle, true);
+ verifyLastControlDexOptBlockingCall(false);
+ }
+
+ @Test
+ public void testPeriodicJobCancelLater() throws Exception {
+ initUntilBootCompleted();
+ when(mInjector.createAndStartThread(any(), any())).thenAnswer(
+ i -> createAndStartExecutionThread(i.getArgument(0), i.getArgument(1)));
+
+ // Start and finish post boot job
+ assertThat(mService.onStartJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue();
+ mDexOptThread.runActualRunnable();
+ mDexOptThread.join(TEST_WAIT_TIMEOUT_MS);
+
+ // Start
+ assertThat(mService.onStartJob(mJobServiceForIdle, mJobParametersForIdle)).isTrue();
+ // Cancel
+ assertThat(mService.onStopJob(mJobServiceForIdle, mJobParametersForIdle)).isTrue();
+
+ // Dexopt thread finishes first.
+ mDexOptThread.runActualRunnable();
+ mDexOptThread.join(TEST_WAIT_TIMEOUT_MS);
+
+ mCancelThread.runActualRunnable();
+ mCancelThread.join(TEST_WAIT_TIMEOUT_MS);
+
+ // Always reschedule for periodic job
+ verify(mJobServiceForIdle).jobFinished(mJobParametersForIdle, true);
+ verify(mDexOptHelper, never()).controlDexOptBlocking(true);
+ }
+
+ @Test
+ public void testStopByThermal() {
+ when(mInjector.createAndStartThread(any(), any())).thenReturn(Thread.currentThread());
+ initUntilBootCompleted();
+
+ assertThat(mService.onStartJob(mJobServiceForPostBoot, mJobParametersForPostBoot)).isTrue();
+
+ ArgumentCaptor<Runnable> argThreadRunnable = ArgumentCaptor.forClass(Runnable.class);
+ verify(mInjector, atLeastOnce()).createAndStartThread(any(), argThreadRunnable.capture());
+
+ // Thermal cancel level
+ when(mInjector.getCurrentThermalStatus()).thenReturn(PowerManager.THERMAL_STATUS_CRITICAL);
+
+ argThreadRunnable.getValue().run();
+
+ verify(mJobServiceForPostBoot).jobFinished(mJobParametersForPostBoot, true);
+ verifyLastControlDexOptBlockingCall(false);
+ }
+
+ @Test
+ public void testRunShellCommandWithInvalidUid() {
+ // Test uid cannot execute the command APIs
+ assertThrows(SecurityException.class, () -> mService.runBackgroundDexoptJob(null));
+ }
+
+ @Test
+ public void testCancelShellCommandWithInvalidUid() {
+ // Test uid cannot execute the command APIs
+ assertThrows(SecurityException.class, () -> mService.cancelBackgroundDexoptJob());
+ }
+
+ private void initUntilBootCompleted() {
+ ArgumentCaptor<BroadcastReceiver> argReceiver = ArgumentCaptor.forClass(
+ BroadcastReceiver.class);
+ ArgumentCaptor<IntentFilter> argIntentFilter = ArgumentCaptor.forClass(IntentFilter.class);
+
+ mService.systemReady();
+
+ verify(mContext).registerReceiver(argReceiver.capture(), argIntentFilter.capture());
+ assertThat(argIntentFilter.getValue().getAction(0)).isEqualTo(Intent.ACTION_BOOT_COMPLETED);
+
+ argReceiver.getValue().onReceive(mContext, null);
+
+ verify(mContext).unregisterReceiver(argReceiver.getValue());
+ ArgumentCaptor<JobInfo> argJobs = ArgumentCaptor.forClass(JobInfo.class);
+ verify(mJobScheduler, times(2)).schedule(argJobs.capture());
+
+ List<Integer> expectedJobIds = Arrays.asList(BackgroundDexOptService.JOB_IDLE_OPTIMIZE,
+ BackgroundDexOptService.JOB_POST_BOOT_UPDATE);
+ List<Integer> jobIds = argJobs.getAllValues().stream().map(job -> job.getId()).collect(
+ Collectors.toList());
+ assertThat(jobIds).containsExactlyElementsIn(expectedJobIds);
+ }
+
+ private void verifyLastControlDexOptBlockingCall(boolean expected) {
+ ArgumentCaptor<Boolean> argDexOptBlock = ArgumentCaptor.forClass(Boolean.class);
+ verify(mDexOptHelper, atLeastOnce()).controlDexOptBlocking(argDexOptBlock.capture());
+ assertThat(argDexOptBlock.getValue()).isEqualTo(expected);
+ }
+
+ private void runFullJob(BackgroundDexOptJobService jobService, JobParameters params,
+ boolean expectedReschedule, int totalJobRuns) {
+ when(mInjector.createAndStartThread(any(), any())).thenReturn(Thread.currentThread());
+ assertThat(mService.onStartJob(jobService, params)).isTrue();
+
+ ArgumentCaptor<Runnable> argThreadRunnable = ArgumentCaptor.forClass(Runnable.class);
+ verify(mInjector, atLeastOnce()).createAndStartThread(any(), argThreadRunnable.capture());
+
+ argThreadRunnable.getValue().run();
+
+ verify(jobService).jobFinished(params, expectedReschedule);
+ // Never block
+ verify(mDexOptHelper, never()).controlDexOptBlocking(true);
+ verifyPerformDexOpt(DEFAULT_PACKAGE_LIST, totalJobRuns);
+ }
+
+ private void verifyPerformDexOpt(ArraySet<String> pkgs, int expectedRuns) {
+ ArgumentCaptor<DexoptOptions> dexOptOptions = ArgumentCaptor.forClass(DexoptOptions.class);
+ verify(mDexOptHelper, atLeastOnce()).performDexOptWithStatus(dexOptOptions.capture());
+ HashMap<String, Integer> primaryPkgs = new HashMap<>(); // K: pkg, V: dexopt runs left
+ for (String pkg : pkgs) {
+ primaryPkgs.put(pkg, expectedRuns);
+ }
+
+ for (DexoptOptions opt : dexOptOptions.getAllValues()) {
+ assertThat(pkgs).contains(opt.getPackageName());
+ assertThat(opt.isDexoptOnlySecondaryDex()).isFalse();
+ Integer count = primaryPkgs.get(opt.getPackageName());
+ assertThat(count).isNotNull();
+ if (count == 1) {
+ primaryPkgs.remove(opt.getPackageName());
+ } else {
+ primaryPkgs.put(opt.getPackageName(), count - 1);
+ }
+ }
+ assertThat(primaryPkgs).isEmpty();
+ }
+
+ private static class StartAndWaitThread extends Thread {
+ private final Runnable mActualRunnable;
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+
+ private StartAndWaitThread(String name, Runnable runnable) {
+ super(name);
+ mActualRunnable = runnable;
+ }
+
+ private void runActualRunnable() {
+ mLatch.countDown();
+ }
+
+ @Override
+ public void run() {
+ // Thread is started but does not run actual code. This is for controlling the execution
+ // order while still meeting Thread.isAlive() check.
+ try {
+ mLatch.await();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ mActualRunnable.run();
+ }
+ }
+
+ private Thread createAndStartExecutionThread(String name, Runnable runnable) {
+ final boolean isDexOptThread = !name.equals("DexOptCancel");
+ StartAndWaitThread thread = new StartAndWaitThread(name, runnable);
+ if (isDexOptThread) {
+ mDexOptThread = thread;
+ } else {
+ mCancelThread = thread;
+ }
+ thread.start();
+ return thread;
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index bc0cc1f..ae24785 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1389,7 +1389,8 @@
final Task task = activity.getTask();
// Make keyguard locked and set the top activity show-when-locked.
KeyguardController keyguardController = activity.mTaskSupervisor.getKeyguardController();
- doReturn(true).when(keyguardController).isKeyguardLocked();
+ int displayId = activity.getDisplayId();
+ doReturn(true).when(keyguardController).isKeyguardLocked(eq(displayId));
final ActivityRecord topActivity = new ActivityBuilder(mAtm).setTask(task).build();
topActivity.mVisibleRequested = true;
topActivity.nowVisible = true;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index abd43a4..1266b2e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -39,6 +39,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.times;
@@ -246,7 +247,7 @@
doReturn(true).when(record)
.checkEnterPictureInPictureState("enterPictureInPictureMode", false);
doReturn(false).when(record).inPinnedWindowingMode();
- doReturn(false).when(mAtm).isKeyguardLocked();
+ doReturn(false).when(mAtm).isKeyguardLocked(anyInt());
//to simulate NPE
doReturn(null).when(record).getParent();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 046328d..562e958 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -827,12 +827,12 @@
SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
keyguard.mAttrs.screenOrientation = SCREEN_ORIENTATION_PORTRAIT;
- mAtm.mKeyguardController.setKeyguardShown(true /* keyguardShowing */,
+ mAtm.mKeyguardController.setKeyguardShown(window.getDisplayId(), true /* keyguardShowing */,
false /* aodShowing */);
assertEquals("Visible keyguard must influence device orientation",
SCREEN_ORIENTATION_PORTRAIT, dc.getOrientation());
- mAtm.mKeyguardController.keyguardGoingAway(0 /* flags */);
+ mAtm.mKeyguardController.keyguardGoingAway(window.getDisplayId(), 0 /* flags */);
assertEquals("Keyguard that is going away must not influence device orientation",
SCREEN_ORIENTATION_LANDSCAPE, dc.getOrientation());
}
@@ -2137,7 +2137,7 @@
assertTopRunningActivity(activity, display);
// Check to make sure activity not reported when it cannot show on lock and lock is on.
- doReturn(true).when(keyguard).isKeyguardLocked();
+ doReturn(true).when(keyguard).isKeyguardLocked(anyInt());
assertEquals(activity, display.topRunningActivity());
assertNull(display.topRunningActivity(true /* considerKeyguardState */));
@@ -2175,11 +2175,11 @@
final WindowState appWin = createWindow(null, TYPE_APPLICATION, mDisplayContent, "appWin");
final ActivityRecord activity = appWin.mActivityRecord;
- mAtm.mKeyguardController.setKeyguardShown(true /* keyguardShowing */,
+ mAtm.mKeyguardController.setKeyguardShown(appWin.getDisplayId(), true /* keyguardShowing */,
true /* aodShowing */);
assertFalse(activity.isVisibleRequested());
- mAtm.mKeyguardController.keyguardGoingAway(0 /* flags */);
+ mAtm.mKeyguardController.keyguardGoingAway(appWin.getDisplayId(), 0 /* flags */);
assertTrue(activity.isVisibleRequested());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index 030733b..bb49cd2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -29,6 +29,7 @@
import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -1505,7 +1506,7 @@
final UnknownAppVisibilityController unknownAppVisibilityController =
mDefaultTaskDisplayArea.mDisplayContent.mUnknownAppVisibilityController;
final KeyguardController keyguardController = mSupervisor.getKeyguardController();
- doReturn(true).when(keyguardController).isKeyguardLocked();
+ doReturn(true).when(keyguardController).isKeyguardLocked(eq(DEFAULT_DISPLAY));
// Start 2 activities that their processes have not yet started.
final ActivityRecord[] activities = new ActivityRecord[2];
@@ -1571,7 +1572,7 @@
display.isDefaultDisplay = isDefaultDisplay;
task.mDisplayContent = display;
- doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
+ doReturn(keyguardGoingAway).when(display).isKeyguardGoingAway();
doReturn(displaySleeping).when(display).isSleeping();
doReturn(focusedRootTask).when(task).isFocusedRootTaskOnDisplay();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index b6701dd..c742e56 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -69,7 +69,7 @@
}
@Override
- public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) throws RemoteException {
+ public void windowFocusChanged(boolean hasFocus) throws RemoteException {
}
@Override
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index a25abc9..fc76f99 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -135,7 +135,7 @@
throw new IllegalArgumentException("Invalid subscription ID: " + subscriptionId);
}
- return new ImsMmTelManager(subscriptionId, sTelephonyCache);
+ return new ImsMmTelManager(mContext, subscriptionId, sTelephonyCache);
}
/**
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 7a27ed7..683bb92 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -25,6 +25,7 @@
import android.annotation.SuppressAutoDoc;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.content.Context;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -215,6 +216,7 @@
}
}
+ private final Context mContext;
private final int mSubId;
private final BinderCacheManager<ITelephony> mBinderCache;
@@ -256,6 +258,16 @@
*/
@VisibleForTesting
public ImsMmTelManager(int subId, BinderCacheManager<ITelephony> binderCache) {
+ this(null, subId, binderCache);
+ }
+
+ /**
+ * Only visible for testing, use {@link ImsManager#getImsMmTelManager(int)} instead.
+ * @hide
+ */
+ @VisibleForTesting
+ public ImsMmTelManager(Context context, int subId, BinderCacheManager<ITelephony> binderCache) {
+ mContext = context;
mSubId = subId;
mBinderCache = binderCache;
}
@@ -1516,7 +1528,8 @@
try {
telephony.registerImsStateCallback(
- mSubId, ImsFeature.FEATURE_MMTEL, callback.getCallbackBinder());
+ mSubId, ImsFeature.FEATURE_MMTEL,
+ callback.getCallbackBinder(), getOpPackageName());
} catch (ServiceSpecificException e) {
throw new ImsException(e.getMessage(), e.errorCode);
} catch (RemoteException | IllegalStateException e) {
@@ -1542,6 +1555,14 @@
}
}
+ private String getOpPackageName() {
+ if (mContext != null) {
+ return mContext.getOpPackageName();
+ } else {
+ return null;
+ }
+ }
+
private ITelephony getITelephony() {
return mBinderCache.getBinder();
}
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index 42af025..1b047c7 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -572,7 +572,8 @@
try {
telephony.registerImsStateCallback(
- mSubId, ImsFeature.FEATURE_RCS, callback.getCallbackBinder());
+ mSubId, ImsFeature.FEATURE_RCS,
+ callback.getCallbackBinder(), mContext.getOpPackageName());
} catch (ServiceSpecificException e) {
throw new ImsException(e.getMessage(), e.errorCode);
} catch (RemoteException | IllegalStateException e) {
diff --git a/telephony/java/android/telephony/ims/SipDelegateManager.java b/telephony/java/android/telephony/ims/SipDelegateManager.java
index 48e3d45..f913df5 100644
--- a/telephony/java/android/telephony/ims/SipDelegateManager.java
+++ b/telephony/java/android/telephony/ims/SipDelegateManager.java
@@ -484,7 +484,8 @@
try {
telephony.registerImsStateCallback(
- mSubId, ImsFeature.FEATURE_RCS, callback.getCallbackBinder());
+ mSubId, ImsFeature.FEATURE_RCS,
+ callback.getCallbackBinder(), mContext.getOpPackageName());
} catch (ServiceSpecificException e) {
throw new ImsException(e.getMessage(), e.errorCode);
} catch (RemoteException | IllegalStateException e) {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index b3c5d042..2e59fc7 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2497,7 +2497,8 @@
/**
* Register an IMS connection state callback
*/
- void registerImsStateCallback(int subId, int feature, in IImsStateCallback cb);
+ void registerImsStateCallback(int subId, int feature, in IImsStateCallback cb,
+ in String callingPackage);
/**
* Unregister an IMS connection state callback