Merge "Also update manage profile state after a transition finishes" into main
diff --git a/apct-tests/perftests/core/src/android/os/OWNERS b/apct-tests/perftests/core/src/android/os/OWNERS
index a1719c9..76ab303 100644
--- a/apct-tests/perftests/core/src/android/os/OWNERS
+++ b/apct-tests/perftests/core/src/android/os/OWNERS
@@ -1 +1,4 @@
-per-file PackageParsingPerfTest.kt = file:/services/core/java/com/android/server/pm/OWNERS
\ No newline at end of file
+per-file PackageParsingPerfTest.kt = file:/services/core/java/com/android/server/pm/OWNERS
+
+# Bug component: 345036
+per-file VibratorPerfTest.java = file:/services/core/java/com/android/server/vibrator/OWNERS
\ No newline at end of file
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 853528f..12ffdb3 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -65,6 +65,7 @@
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.TransactionExecutor;
import android.app.servertransaction.TransactionExecutorHelper;
+import android.app.servertransaction.WindowTokenClientController;
import android.bluetooth.BluetoothFrameworkInitializer;
import android.companion.virtual.VirtualDeviceManager;
import android.compat.annotation.UnsupportedAppUsage;
@@ -6221,6 +6222,18 @@
false /* clearPending */);
}
+ @Override
+ public void handleWindowContextConfigurationChanged(@NonNull IBinder clientToken,
+ @NonNull Configuration configuration, int displayId) {
+ WindowTokenClientController.getInstance().onWindowContextConfigurationChanged(clientToken,
+ configuration, displayId);
+ }
+
+ @Override
+ public void handleWindowContextWindowRemoval(@NonNull IBinder clientToken) {
+ WindowTokenClientController.getInstance().onWindowContextWindowRemoved(clientToken);
+ }
+
/**
* Sends windowing mode change callbacks to {@link Activity} if applicable.
*
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 49fb794..f7a43f4 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -163,6 +163,13 @@
public abstract void handleActivityConfigurationChanged(@NonNull ActivityClientRecord r,
Configuration overrideConfig, int displayId);
+ /** Deliver {@link android.window.WindowContext} configuration change. */
+ public abstract void handleWindowContextConfigurationChanged(@NonNull IBinder clientToken,
+ @NonNull Configuration configuration, int displayId);
+
+ /** Deliver {@link android.window.WindowContext} window removal event. */
+ public abstract void handleWindowContextWindowRemoval(@NonNull IBinder clientToken);
+
/** Deliver result from another activity. */
public abstract void handleSendResult(
@NonNull ActivityClientRecord r, List<ResultInfo> results, String reason);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 59b0dac..1f95497 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -27,6 +27,7 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UiContext;
+import android.app.servertransaction.WindowTokenClientController;
import android.companion.virtual.VirtualDeviceManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.AttributionSource;
@@ -3276,7 +3277,8 @@
// if this Context is not a WindowContext. WindowContext finalization is handled in
// WindowContext class.
if (mToken instanceof WindowTokenClient && mOwnsToken) {
- ((WindowTokenClient) mToken).detachFromWindowContainerIfNeeded();
+ WindowTokenClientController.getInstance().detachIfNeeded(
+ (WindowTokenClient) mToken);
}
super.finalize();
}
@@ -3304,7 +3306,7 @@
final WindowTokenClient token = new WindowTokenClient();
final ContextImpl context = systemContext.createWindowContextBase(token, displayId);
token.attachContext(context);
- token.attachToDisplayContent(displayId);
+ WindowTokenClientController.getInstance().attachToDisplayContent(token, displayId);
context.mContextType = CONTEXT_TYPE_SYSTEM_OR_SYSTEM_UI;
context.mOwnsToken = true;
diff --git a/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java b/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java
new file mode 100644
index 0000000..3ac642f
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowContextConfigurationChangeItem.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.view.Display.INVALID_DISPLAY;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ClientTransactionHandler;
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.Parcel;
+
+import java.util.Objects;
+
+/**
+ * {@link android.window.WindowContext} configuration change message.
+ * @hide
+ */
+public class WindowContextConfigurationChangeItem extends ClientTransactionItem {
+
+ @Nullable
+ private IBinder mClientToken;
+ @Nullable
+ private Configuration mConfiguration;
+ private int mDisplayId;
+
+ @Override
+ public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ @NonNull PendingTransactionActions pendingActions) {
+ client.handleWindowContextConfigurationChanged(mClientToken, mConfiguration, mDisplayId);
+ }
+
+ // ObjectPoolItem implementation
+
+ private WindowContextConfigurationChangeItem() {}
+
+ /** Obtains an instance initialized with provided params. */
+ public static WindowContextConfigurationChangeItem obtain(
+ @NonNull IBinder clientToken, @NonNull Configuration config, int displayId) {
+ WindowContextConfigurationChangeItem instance =
+ ObjectPool.obtain(WindowContextConfigurationChangeItem.class);
+ if (instance == null) {
+ instance = new WindowContextConfigurationChangeItem();
+ }
+ instance.mClientToken = requireNonNull(clientToken);
+ instance.mConfiguration = requireNonNull(config);
+ instance.mDisplayId = displayId;
+
+ return instance;
+ }
+
+ @Override
+ public void recycle() {
+ mClientToken = null;
+ mConfiguration = null;
+ mDisplayId = INVALID_DISPLAY;
+ ObjectPool.recycle(this);
+ }
+
+ // Parcelable implementation
+
+ /** Writes to Parcel. */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStrongBinder(mClientToken);
+ dest.writeTypedObject(mConfiguration, flags);
+ dest.writeInt(mDisplayId);
+ }
+
+ /** Reads from Parcel. */
+ private WindowContextConfigurationChangeItem(@NonNull Parcel in) {
+ mClientToken = in.readStrongBinder();
+ mConfiguration = in.readTypedObject(Configuration.CREATOR);
+ mDisplayId = in.readInt();
+ }
+
+ public static final @NonNull Creator<WindowContextConfigurationChangeItem> CREATOR =
+ new Creator<>() {
+ public WindowContextConfigurationChangeItem createFromParcel(Parcel in) {
+ return new WindowContextConfigurationChangeItem(in);
+ }
+
+ public WindowContextConfigurationChangeItem[] newArray(int size) {
+ return new WindowContextConfigurationChangeItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final WindowContextConfigurationChangeItem other = (WindowContextConfigurationChangeItem) o;
+ return Objects.equals(mClientToken, other.mClientToken)
+ && Objects.equals(mConfiguration, other.mConfiguration)
+ && mDisplayId == other.mDisplayId;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + Objects.hashCode(mClientToken);
+ result = 31 * result + Objects.hashCode(mConfiguration);
+ result = 31 * result + mDisplayId;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "WindowContextConfigurationChangeItem{clientToken=" + mClientToken
+ + ", config=" + mConfiguration
+ + ", displayId=" + mDisplayId
+ + "}";
+ }
+}
diff --git a/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
new file mode 100644
index 0000000..ed52a64
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowContextWindowRemovalItem.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+import android.os.Parcel;
+
+import java.util.Objects;
+
+/**
+ * {@link android.window.WindowContext} window removal message.
+ * @hide
+ */
+public class WindowContextWindowRemovalItem extends ClientTransactionItem {
+
+ @Nullable
+ private IBinder mClientToken;
+
+ @Override
+ public void execute(@NonNull ClientTransactionHandler client, @NonNull IBinder token,
+ @NonNull PendingTransactionActions pendingActions) {
+ client.handleWindowContextWindowRemoval(mClientToken);
+ }
+
+ // ObjectPoolItem implementation
+
+ private WindowContextWindowRemovalItem() {}
+
+ /** Obtains an instance initialized with provided params. */
+ public static WindowContextWindowRemovalItem obtain(@NonNull IBinder clientToken) {
+ WindowContextWindowRemovalItem instance =
+ ObjectPool.obtain(WindowContextWindowRemovalItem.class);
+ if (instance == null) {
+ instance = new WindowContextWindowRemovalItem();
+ }
+ instance.mClientToken = requireNonNull(clientToken);
+
+ return instance;
+ }
+
+ @Override
+ public void recycle() {
+ mClientToken = null;
+ ObjectPool.recycle(this);
+ }
+
+ // Parcelable implementation
+
+ /** Writes to Parcel. */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeStrongBinder(mClientToken);
+ }
+
+ /** Reads from Parcel. */
+ private WindowContextWindowRemovalItem(@NonNull Parcel in) {
+ mClientToken = in.readStrongBinder();
+ }
+
+ public static final @NonNull Creator<WindowContextWindowRemovalItem> CREATOR = new Creator<>() {
+ public WindowContextWindowRemovalItem createFromParcel(Parcel in) {
+ return new WindowContextWindowRemovalItem(in);
+ }
+
+ public WindowContextWindowRemovalItem[] newArray(int size) {
+ return new WindowContextWindowRemovalItem[size];
+ }
+ };
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ final WindowContextWindowRemovalItem other = (WindowContextWindowRemovalItem) o;
+ return Objects.equals(mClientToken, other.mClientToken);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 17;
+ result = 31 * result + Objects.hashCode(mClientToken);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "WindowContextWindowRemovalItem{clientToken=" + mClientToken + "}";
+ }
+}
diff --git a/core/java/android/app/servertransaction/WindowTokenClientController.java b/core/java/android/app/servertransaction/WindowTokenClientController.java
new file mode 100644
index 0000000..5d123a0
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowTokenClientController.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.view.WindowManager.LayoutParams.WindowType;
+import static android.view.WindowManagerGlobal.getWindowManagerService;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.view.IWindowManager;
+import android.window.WindowContext;
+import android.window.WindowTokenClient;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Singleton controller to manage the attached {@link WindowTokenClient}s, and to dispatch
+ * corresponding window configuration change from server side.
+ * @hide
+ */
+public class WindowTokenClientController {
+
+ private static final String TAG = WindowTokenClientController.class.getSimpleName();
+ private static WindowTokenClientController sController;
+
+ private final Object mLock = new Object();
+
+ /** Mapping from a client defined token to the {@link WindowTokenClient} it represents. */
+ @GuardedBy("mLock")
+ private final ArrayMap<IBinder, WindowTokenClient> mWindowTokenClientMap = new ArrayMap<>();
+
+ /** Gets the singleton controller. */
+ public static WindowTokenClientController getInstance() {
+ synchronized (WindowTokenClientController.class) {
+ if (sController == null) {
+ sController = new WindowTokenClientController();
+ }
+ return sController;
+ }
+ }
+
+ /** Overrides the {@link #getInstance()} for test only. */
+ @VisibleForTesting
+ public static void overrideForTesting(@NonNull WindowTokenClientController controller) {
+ synchronized (WindowTokenClientController.class) {
+ sController = controller;
+ }
+ }
+
+ private WindowTokenClientController() {}
+
+ /**
+ * Attaches a {@link WindowTokenClient} to a {@link com.android.server.wm.DisplayArea}.
+ *
+ * @param client The {@link WindowTokenClient} to attach.
+ * @param type The window type of the {@link WindowContext}
+ * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
+ * @param options The window context launched option
+ * @return {@code true} if attaching successfully.
+ */
+ public boolean attachToDisplayArea(@NonNull WindowTokenClient client,
+ @WindowType int type, int displayId, @Nullable Bundle options) {
+ final Configuration configuration;
+ try {
+ configuration = getWindowManagerService()
+ .attachWindowContextToDisplayArea(client, type, displayId, options);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ if (configuration == null) {
+ return false;
+ }
+ onWindowContextTokenAttached(client, displayId, configuration);
+ return true;
+ }
+
+ /**
+ * Attaches a {@link WindowTokenClient} to a {@code DisplayContent}.
+ *
+ * @param client The {@link WindowTokenClient} to attach.
+ * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
+ * @return {@code true} if attaching successfully.
+ */
+ public boolean attachToDisplayContent(@NonNull WindowTokenClient client, int displayId) {
+ final IWindowManager wms = getWindowManagerService();
+ // #createSystemUiContext may call this method before WindowManagerService is initialized.
+ if (wms == null) {
+ return false;
+ }
+ final Configuration configuration;
+ try {
+ configuration = wms.attachToDisplayContent(client, displayId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ if (configuration == null) {
+ return false;
+ }
+ onWindowContextTokenAttached(client, displayId, configuration);
+ return true;
+ }
+
+ /**
+ * Attaches this {@link WindowTokenClient} to a {@code windowToken}.
+ *
+ * @param client The {@link WindowTokenClient} to attach.
+ * @param windowToken the window token to associated with
+ */
+ public void attachToWindowToken(@NonNull WindowTokenClient client,
+ @NonNull IBinder windowToken) {
+ try {
+ getWindowManagerService().attachWindowContextToWindowToken(client, windowToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ // We don't report configuration change for now.
+ synchronized (mLock) {
+ mWindowTokenClientMap.put(client.asBinder(), client);
+ }
+ }
+
+ /** Detaches a {@link WindowTokenClient} from associated WindowContainer if there's one. */
+ public void detachIfNeeded(@NonNull WindowTokenClient client) {
+ synchronized (mLock) {
+ if (mWindowTokenClientMap.remove(client.asBinder()) == null) {
+ return;
+ }
+ }
+ try {
+ getWindowManagerService().detachWindowContextFromWindowContainer(client);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private void onWindowContextTokenAttached(@NonNull WindowTokenClient client, int displayId,
+ @NonNull Configuration configuration) {
+ synchronized (mLock) {
+ mWindowTokenClientMap.put(client.asBinder(), client);
+ }
+ client.onConfigurationChanged(configuration, displayId,
+ false /* shouldReportConfigChange */);
+ }
+
+ /** Called when receives {@link WindowContextConfigurationChangeItem}. */
+ public void onWindowContextConfigurationChanged(@NonNull IBinder clientToken,
+ @NonNull Configuration configuration, int displayId) {
+ final WindowTokenClient windowTokenClient = getWindowTokenClient(clientToken);
+ if (windowTokenClient != null) {
+ windowTokenClient.onConfigurationChanged(configuration, displayId);
+ }
+ }
+
+ /** Called when receives {@link WindowContextWindowRemovalItem}. */
+ public void onWindowContextWindowRemoved(@NonNull IBinder clientToken) {
+ final WindowTokenClient windowTokenClient = getWindowTokenClient(clientToken);
+ if (windowTokenClient != null) {
+ windowTokenClient.onWindowTokenRemoved();
+ }
+ }
+
+ @Nullable
+ private WindowTokenClient getWindowTokenClient(@NonNull IBinder clientToken) {
+ final WindowTokenClient windowTokenClient;
+ synchronized (mLock) {
+ windowTokenClient = mWindowTokenClientMap.get(clientToken);
+ }
+ if (windowTokenClient == null) {
+ Log.w(TAG, "Can't find attached WindowTokenClient for " + clientToken);
+ }
+ return windowTokenClient;
+ }
+}
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index ceaf337..2584f04 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -119,7 +119,7 @@
* crashes (if a handler is sometimes created on a thread without a Looper active), or race
* conditions, where the thread a handler is associated with is not what the author
* anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper
- * explicitly, using {@link Looper#getMainLooper}, {link android.view.View#getHandler}, or
+ * explicitly, using {@link Looper#getMainLooper}, {@link android.view.View#getHandler}, or
* similar. If the implicit thread local behavior is required for compatibility, use
* {@code new Handler(Looper.myLooper())} to make it clear to readers.
*
@@ -144,7 +144,7 @@
* crashes (if a handler is sometimes created on a thread without a Looper active), or race
* conditions, where the thread a handler is associated with is not what the author
* anticipated. Instead, use an {@link java.util.concurrent.Executor} or specify the Looper
- * explicitly, using {@link Looper#getMainLooper}, {link android.view.View#getHandler}, or
+ * explicitly, using {@link Looper#getMainLooper}, {@link android.view.View#getHandler}, or
* similar. If the implicit thread local behavior is required for compatibility, use
* {@code new Handler(Looper.myLooper(), callback)} to make it clear to readers.
*/
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 99a4f6b..6aa8506 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -34,6 +34,7 @@
import android.util.Log;
import android.view.inputmethod.InputMethodManager;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastPrintWriter;
import java.io.FileDescriptor;
@@ -184,6 +185,15 @@
}
}
+ /** Overrides the {@link #getWindowManagerService()} for test only. */
+ @VisibleForTesting
+ public static void overrideWindowManagerServiceForTesting(
+ @NonNull IWindowManager windowManager) {
+ synchronized (WindowManagerGlobal.class) {
+ sWindowManagerService = windowManager;
+ }
+ }
+
@UnsupportedAppUsage
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 8ba8b8c..0699bc1 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -32,6 +32,7 @@
import android.util.AttributeSet;
import android.util.TimeUtils;
import android.util.Xml;
+import android.view.Choreographer;
import android.view.InflateException;
import org.xmlpull.v1.XmlPullParser;
@@ -153,7 +154,13 @@
*/
public static long getExpectedPresentationTimeNanos() {
AnimationState state = sAnimationState.get();
- return state.mExpectedPresentationTimeNanos;
+ if (state.animationClockLocked) {
+ return state.mExpectedPresentationTimeNanos;
+ }
+ // When this methoed is called outside of a Choreographer callback,
+ // we obtain the value of expectedPresentTimeNanos from the Choreographer.
+ // This helps avoid returning a time that could potentially be earlier than current time.
+ return Choreographer.getInstance().getLatestExpectedPresentTimeNanos();
}
/**
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index e9d7b9b..a95748c 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -43,7 +43,6 @@
import android.view.View;
import android.view.ViewRootImpl;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
import com.android.internal.inputmethod.IRemoteInputConnection;
@@ -54,6 +53,7 @@
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -158,18 +158,13 @@
boolean cancellable();
}
- @GuardedBy("mLock")
- @Nullable
- private InputConnection mInputConnection;
+ @NonNull
+ private final AtomicReference<InputConnection> mInputConnectionRef;
@NonNull
private final Looper mLooper;
private final Handler mH;
- private final Object mLock = new Object();
- @GuardedBy("mLock")
- private boolean mFinished = false;
-
private final InputMethodManager mParentInputMethodManager;
private final WeakReference<View> mServedView;
@@ -185,7 +180,7 @@
RemoteInputConnectionImpl(@NonNull Looper looper,
@NonNull InputConnection inputConnection,
@NonNull InputMethodManager inputMethodManager, @Nullable View servedView) {
- mInputConnection = inputConnection;
+ mInputConnectionRef = new AtomicReference<>(inputConnection);
mLooper = looper;
mH = new Handler(mLooper);
mParentInputMethodManager = inputMethodManager;
@@ -197,9 +192,7 @@
*/
@Nullable
public InputConnection getInputConnection() {
- synchronized (mLock) {
- return mInputConnection;
- }
+ return mInputConnectionRef.get();
}
/**
@@ -215,9 +208,7 @@
* {@link InputConnection#closeConnection()} as a result of {@link #deactivate()}.
*/
private boolean isFinished() {
- synchronized (mLock) {
- return mFinished;
- }
+ return mInputConnectionRef.get() == null;
}
private boolean isActive() {
@@ -386,10 +377,7 @@
// TODO(b/199934664): See if we can remove this by providing a default impl.
}
} finally {
- synchronized (mLock) {
- mInputConnection = null;
- mFinished = true;
- }
+ mInputConnectionRef.set(null);
Trace.traceEnd(Trace.TRACE_TAG_INPUT);
}
@@ -441,7 +429,6 @@
public String toString() {
return "RemoteInputConnectionImpl{"
+ "connection=" + getInputConnection()
- + " finished=" + isFinished()
+ " mParentInputMethodManager.isActive()=" + mParentInputMethodManager.isActive()
+ " mServedView=" + mServedView.get()
+ "}";
@@ -455,16 +442,14 @@
* {@link DumpableInputConnection#dumpDebug(ProtoOutputStream, long)}.
*/
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
- synchronized (mLock) {
- // Check that the call is initiated in the target thread of the current InputConnection
- // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
- // executed on this thread. Otherwise the messages are dispatched to the correct thread
- // in IInputConnectionWrapper, but this is not wanted while dumpng, for performance
- // reasons.
- if ((mInputConnection instanceof DumpableInputConnection)
- && mLooper.isCurrentThread()) {
- ((DumpableInputConnection) mInputConnection).dumpDebug(proto, fieldId);
- }
+ final InputConnection ic = mInputConnectionRef.get();
+ // Check that the call is initiated in the target thread of the current InputConnection
+ // {@link InputConnection#getHandler} since the messages to IInputConnectionWrapper are
+ // executed on this thread. Otherwise the messages are dispatched to the correct thread
+ // in IInputConnectionWrapper, but this is not wanted while dumping, for performance
+ // reasons.
+ if ((ic instanceof DumpableInputConnection) && mLooper.isCurrentThread()) {
+ ((DumpableInputConnection) ic).dumpDebug(proto, fieldId);
}
}
diff --git a/core/java/android/window/WindowContextController.java b/core/java/android/window/WindowContextController.java
index 4b9a957..eb270e2 100644
--- a/core/java/android/window/WindowContextController.java
+++ b/core/java/android/window/WindowContextController.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.servertransaction.WindowTokenClientController;
import android.content.Context;
import android.os.Bundle;
import android.os.IBinder;
@@ -104,7 +105,8 @@
throw new IllegalStateException("A Window Context can be only attached to "
+ "a DisplayArea once.");
}
- mAttachedToDisplayArea = mToken.attachToDisplayArea(type, displayId, options)
+ mAttachedToDisplayArea = WindowTokenClientController.getInstance().attachToDisplayArea(
+ mToken, type, displayId, options)
? AttachStatus.STATUS_ATTACHED : AttachStatus.STATUS_FAILED;
if (mAttachedToDisplayArea == AttachStatus.STATUS_FAILED) {
Log.w(TAG, "attachToDisplayArea fail, type:" + type + ", displayId:"
@@ -140,13 +142,13 @@
throw new IllegalStateException("The Window Context should have been attached"
+ " to a DisplayArea. AttachToDisplayArea:" + mAttachedToDisplayArea);
}
- mToken.attachToWindowToken(windowToken);
+ WindowTokenClientController.getInstance().attachToWindowToken(mToken, windowToken);
}
/** Detaches the window context from the node it's currently associated with. */
public void detachIfNeeded() {
if (mAttachedToDisplayArea == AttachStatus.STATUS_ATTACHED) {
- mToken.detachFromWindowContainerIfNeeded();
+ WindowTokenClientController.getInstance().detachIfNeeded(mToken);
mAttachedToDisplayArea = AttachStatus.STATUS_DETACHED;
if (DEBUG_ATTACH) {
Log.d(TAG, "Detach Window Context.");
diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java
index a208634..47d3df87 100644
--- a/core/java/android/window/WindowTokenClient.java
+++ b/core/java/android/window/WindowTokenClient.java
@@ -20,13 +20,12 @@
import static android.window.ConfigurationHelper.shouldUpdateResources;
import android.annotation.AnyThread;
-import android.annotation.BinderThread;
import android.annotation.MainThread;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.ActivityThread;
import android.app.IWindowToken;
import android.app.ResourcesManager;
+import android.app.servertransaction.WindowTokenClientController;
import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -36,14 +35,9 @@
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
-import android.os.RemoteException;
import android.util.Log;
-import android.view.IWindowManager;
-import android.view.WindowManager.LayoutParams.WindowType;
-import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.function.pooled.PooledLambda;
import java.lang.ref.WeakReference;
@@ -70,15 +64,11 @@
private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
- private IWindowManager mWms;
-
@GuardedBy("itself")
private final Configuration mConfiguration = new Configuration();
private boolean mShouldDumpConfigForIme;
- private boolean mAttachToWindowContainer;
-
private final Handler mHandler = ActivityThread.currentActivityThread().getHandler();
/**
@@ -101,96 +91,15 @@
}
/**
- * Attaches this {@link WindowTokenClient} to a {@link com.android.server.wm.DisplayArea}.
- *
- * @param type The window type of the {@link WindowContext}
- * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
- * @param options The window context launched option
- * @return {@code true} if attaching successfully.
- */
- public boolean attachToDisplayArea(@WindowType int type, int displayId,
- @Nullable Bundle options) {
- try {
- final Configuration configuration = getWindowManagerService()
- .attachWindowContextToDisplayArea(this, type, displayId, options);
- if (configuration == null) {
- return false;
- }
- onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
- mAttachToWindowContainer = true;
- return true;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Attaches this {@link WindowTokenClient} to a {@code DisplayContent}.
- *
- * @param displayId The {@link Context#getDisplayId() ID of display} to associate with
- * @return {@code true} if attaching successfully.
- */
- public boolean attachToDisplayContent(int displayId) {
- final IWindowManager wms = getWindowManagerService();
- // #createSystemUiContext may call this method before WindowManagerService is initialized.
- if (wms == null) {
- return false;
- }
- try {
- final Configuration configuration = wms.attachToDisplayContent(this, displayId);
- if (configuration == null) {
- return false;
- }
- onConfigurationChanged(configuration, displayId, false /* shouldReportConfigChange */);
- mAttachToWindowContainer = true;
- return true;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Attaches this {@link WindowTokenClient} to a {@code windowToken}.
- *
- * @param windowToken the window token to associated with
- */
- public void attachToWindowToken(IBinder windowToken) {
- try {
- getWindowManagerService().attachWindowContextToWindowToken(this, windowToken);
- mAttachToWindowContainer = true;
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /** Detaches this {@link WindowTokenClient} from associated WindowContainer if there's one. */
- public void detachFromWindowContainerIfNeeded() {
- if (!mAttachToWindowContainer) {
- return;
- }
- try {
- getWindowManagerService().detachWindowContextFromWindowContainer(this);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- private IWindowManager getWindowManagerService() {
- if (mWms == null) {
- mWms = WindowManagerGlobal.getWindowManagerService();
- }
- return mWms;
- }
-
- /**
* Called when {@link Configuration} updates from the server side receive.
*
* @param newConfig the updated {@link Configuration}
* @param newDisplayId the updated {@link android.view.Display} ID
*/
- @BinderThread
+ @AnyThread
@Override
public void onConfigurationChanged(Configuration newConfig, int newDisplayId) {
+ // TODO(b/290876897): No need to post on mHandler after migrating to ClientTransaction
mHandler.post(PooledLambda.obtainRunnable(this::onConfigurationChanged, newConfig,
newDisplayId, true /* shouldReportConfigChange */).recycleOnUse());
}
@@ -207,15 +116,14 @@
* {@code shouldReportConfigChange} is {@code true}, which is usually from
* {@link IWindowToken#onConfigurationChanged(Configuration, int)}
* directly, while this method could be run on any thread if it is used to initialize
- * Context's {@code Configuration} via {@link #attachToDisplayArea(int, int, Bundle)}
- * or {@link #attachToDisplayContent(int)}.
+ * Context's {@code Configuration} via {@link WindowTokenClientController#attachToDisplayArea}
+ * or {@link WindowTokenClientController#attachToDisplayContent}.
*
* @param shouldReportConfigChange {@code true} to indicate that the {@code Configuration}
* should be dispatched to listeners.
*
*/
@AnyThread
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public void onConfigurationChanged(Configuration newConfig, int newDisplayId,
boolean shouldReportConfigChange) {
final Context context = mContextRef.get();
@@ -280,9 +188,10 @@
}
}
- @BinderThread
+ @AnyThread
@Override
public void onWindowTokenRemoved() {
+ // TODO(b/290876897): No need to post on mHandler after migrating to ClientTransaction
mHandler.post(PooledLambda.obtainRunnable(
WindowTokenClient::onWindowTokenRemovedInner, this).recycleOnUse());
}
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index c2cfcd6..66b0158 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -1565,6 +1565,13 @@
mInStealthMode = ss.isInStealthMode();
}
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ setSystemGestureExclusionRects(List.of(new Rect(left, top, right, bottom)));
+ }
+
/**
* The parecelable for saving and restoring a lock pattern view.
*/
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 4857741..c1b55cd 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -21,6 +21,7 @@
import static android.content.Intent.ACTION_VIEW;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static com.google.common.truth.Truth.assertThat;
@@ -29,6 +30,8 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import android.annotation.Nullable;
import android.app.Activity;
@@ -46,6 +49,7 @@
import android.app.servertransaction.NewIntentItem;
import android.app.servertransaction.ResumeActivityItem;
import android.app.servertransaction.StopActivityItem;
+import android.app.servertransaction.WindowTokenClientController;
import android.content.Context;
import android.content.Intent;
import android.content.res.CompatibilityInfo;
@@ -70,6 +74,7 @@
import com.android.internal.content.ReferrerIntent;
import org.junit.After;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -83,7 +88,7 @@
/**
* Test for verifying {@link android.app.ActivityThread} class.
* Build/Install/Run:
- * atest FrameworksCoreTests:android.app.activity.ActivityThreadTest
+ * atest FrameworksCoreTests:ActivityThreadTest
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
@@ -100,14 +105,24 @@
new ActivityTestRule<>(TestActivity.class, true /* initialTouchMode */,
false /* launchActivity */);
+ private WindowTokenClientController mOriginalWindowTokenClientController;
+
private ArrayList<VirtualDisplay> mCreatedVirtualDisplays;
+ @Before
+ public void setup() {
+ // Keep track of the original controller, so that it can be used to restore in tearDown()
+ // when there is override in some test cases.
+ mOriginalWindowTokenClientController = WindowTokenClientController.getInstance();
+ }
+
@After
public void tearDown() {
if (mCreatedVirtualDisplays != null) {
mCreatedVirtualDisplays.forEach(VirtualDisplay::release);
mCreatedVirtualDisplays = null;
}
+ WindowTokenClientController.overrideForTesting(mOriginalWindowTokenClientController);
}
@Test
@@ -730,6 +745,39 @@
assertFalse(activity.enterPipSkipped());
}
+ @Test
+ public void testHandleWindowContextConfigurationChanged() {
+ final Activity activity = mActivityTestRule.launchActivity(new Intent());
+ final ActivityThread activityThread = activity.getActivityThread();
+ final WindowTokenClientController windowTokenClientController =
+ mock(WindowTokenClientController.class);
+ WindowTokenClientController.overrideForTesting(windowTokenClientController);
+ final IBinder clientToken = mock(IBinder.class);
+ final Configuration configuration = new Configuration();
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> activityThread
+ .handleWindowContextConfigurationChanged(
+ clientToken, configuration, DEFAULT_DISPLAY));
+
+ verify(windowTokenClientController).onWindowContextConfigurationChanged(
+ clientToken, configuration, DEFAULT_DISPLAY);
+ }
+
+ @Test
+ public void testHandleWindowContextWindowRemoval() {
+ final Activity activity = mActivityTestRule.launchActivity(new Intent());
+ final ActivityThread activityThread = activity.getActivityThread();
+ final WindowTokenClientController windowTokenClientController =
+ mock(WindowTokenClientController.class);
+ WindowTokenClientController.overrideForTesting(windowTokenClientController);
+ final IBinder clientToken = mock(IBinder.class);
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> activityThread
+ .handleWindowContextWindowRemoval(clientToken));
+
+ verify(windowTokenClientController).onWindowContextWindowRemoved(clientToken);
+ }
+
/**
* Calls {@link ActivityThread#handleActivityConfigurationChanged(ActivityClientRecord,
* Configuration, int)} to try to push activity configuration to the activity for the given
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java
new file mode 100644
index 0000000..7811e1a
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowContextConfigurationChangeItemTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.ClientTransactionHandler;
+import android.content.res.Configuration;
+import android.os.IBinder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowContextConfigurationChangeItem}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:WindowContextConfigurationChangeItemTest
+ */
+public class WindowContextConfigurationChangeItemTest {
+
+ @Mock
+ private ClientTransactionHandler mHandler;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private PendingTransactionActions mPendingActions;
+ @Mock
+ private IBinder mClientToken;
+ // Can't mock final class.
+ private final Configuration mConfiguration = new Configuration();
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testExecute() {
+ final WindowContextConfigurationChangeItem item = WindowContextConfigurationChangeItem
+ .obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
+ item.execute(mHandler, mToken, mPendingActions);
+
+ verify(mHandler).handleWindowContextConfigurationChanged(mClientToken, mConfiguration,
+ DEFAULT_DISPLAY);
+ }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
new file mode 100644
index 0000000..2c83c70
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static org.mockito.Mockito.verify;
+
+import android.app.ClientTransactionHandler;
+import android.os.IBinder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowContextWindowRemovalItem}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:WindowContextWindowRemovalItemTest
+ */
+public class WindowContextWindowRemovalItemTest {
+
+ @Mock
+ private ClientTransactionHandler mHandler;
+ @Mock
+ private IBinder mToken;
+ @Mock
+ private PendingTransactionActions mPendingActions;
+ @Mock
+ private IBinder mClientToken;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testExecute() {
+ final WindowContextWindowRemovalItem item = WindowContextWindowRemovalItem.obtain(
+ mClientToken);
+ item.execute(mHandler, mToken, mPendingActions);
+
+ verify(mHandler).handleWindowContextWindowRemoval(mClientToken);
+ }
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowTokenClientControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowTokenClientControllerTest.java
new file mode 100644
index 0000000..3b2fe58
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/WindowTokenClientControllerTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+import android.window.WindowTokenClient;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link WindowTokenClientController}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:WindowTokenClientControllerTest
+ */
+@SmallTest
+@Presubmit
+public class WindowTokenClientControllerTest {
+
+ @Mock
+ private IWindowManager mWindowManagerService;
+ @Mock
+ private WindowTokenClient mWindowTokenClient;
+ @Mock
+ private IBinder mClientToken;
+ @Mock
+ private IBinder mWindowToken;
+ // Can't mock final class.
+ private final Configuration mConfiguration = new Configuration();
+
+ private IWindowManager mOriginalWindowManagerService;
+
+ private WindowTokenClientController mController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mOriginalWindowManagerService = WindowManagerGlobal.getWindowManagerService();
+ WindowManagerGlobal.overrideWindowManagerServiceForTesting(mWindowManagerService);
+ doReturn(mClientToken).when(mWindowTokenClient).asBinder();
+ mController = spy(WindowTokenClientController.getInstance());
+ }
+
+ @After
+ public void tearDown() {
+ WindowManagerGlobal.overrideWindowManagerServiceForTesting(mOriginalWindowManagerService);
+ }
+
+ @Test
+ public void testAttachToDisplayArea() throws RemoteException {
+ doReturn(null).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ any(), anyInt(), anyInt(), any());
+
+ assertFalse(mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+ DEFAULT_DISPLAY, null /* options */));
+ verify(mWindowManagerService).attachWindowContextToDisplayArea(mWindowTokenClient,
+ TYPE_APPLICATION_OVERLAY, DEFAULT_DISPLAY, null /* options */);
+ verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
+
+ doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ any(), anyInt(), anyInt(), any());
+
+ assertTrue(mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+ DEFAULT_DISPLAY, null /* options */));
+ verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY,
+ false /* shouldReportConfigChange */);
+ }
+
+ @Test
+ public void testAttachToDisplayArea_detachIfNeeded() throws RemoteException {
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ doReturn(null).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ any(), anyInt(), anyInt(), any());
+ mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+ DEFAULT_DISPLAY, null /* options */);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ doReturn(mConfiguration).when(mWindowManagerService).attachWindowContextToDisplayArea(
+ any(), anyInt(), anyInt(), any());
+ mController.attachToDisplayArea(mWindowTokenClient, TYPE_APPLICATION_OVERLAY,
+ DEFAULT_DISPLAY, null /* options */);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService).detachWindowContextFromWindowContainer(any());
+ }
+
+ @Test
+ public void testAttachToDisplayContent() throws RemoteException {
+ doReturn(null).when(mWindowManagerService).attachToDisplayContent(
+ any(), anyInt());
+
+ assertFalse(mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY));
+ verify(mWindowManagerService).attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
+ verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt(), anyBoolean());
+
+ doReturn(mConfiguration).when(mWindowManagerService).attachToDisplayContent(
+ any(), anyInt());
+
+ assertTrue(mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY));
+ verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY,
+ false /* shouldReportConfigChange */);
+ }
+
+ @Test
+ public void testAttachToDisplayContent_detachIfNeeded() throws RemoteException {
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ doReturn(null).when(mWindowManagerService).attachToDisplayContent(
+ any(), anyInt());
+ mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ doReturn(mConfiguration).when(mWindowManagerService).attachToDisplayContent(
+ any(), anyInt());
+ mController.attachToDisplayContent(mWindowTokenClient, DEFAULT_DISPLAY);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService).detachWindowContextFromWindowContainer(any());
+ }
+
+ @Test
+ public void testAttachToWindowToken() throws RemoteException {
+ mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+
+ verify(mWindowManagerService).attachWindowContextToWindowToken(mWindowTokenClient,
+ mWindowToken);
+ }
+
+ @Test
+ public void testAttachToWindowToken_detachIfNeeded() throws RemoteException {
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService, never()).detachWindowContextFromWindowContainer(any());
+
+ mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+ mController.detachIfNeeded(mWindowTokenClient);
+
+ verify(mWindowManagerService).detachWindowContextFromWindowContainer(any());
+ }
+
+ @Test
+ public void testOnWindowContextConfigurationChanged() {
+ mController.onWindowContextConfigurationChanged(
+ mClientToken, mConfiguration, DEFAULT_DISPLAY);
+
+ verify(mWindowTokenClient, never()).onConfigurationChanged(any(), anyInt());
+
+ mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+
+ mController.onWindowContextConfigurationChanged(
+ mClientToken, mConfiguration, DEFAULT_DISPLAY);
+
+ verify(mWindowTokenClient).onConfigurationChanged(mConfiguration, DEFAULT_DISPLAY);
+ }
+
+ @Test
+ public void testOnWindowContextWindowRemoved() {
+ mController.onWindowContextWindowRemoved(mClientToken);
+
+ verify(mWindowTokenClient, never()).onWindowTokenRemoved();
+
+ mController.attachToWindowToken(mWindowTokenClient, mWindowToken);
+
+ mController.onWindowContextWindowRemoved(mClientToken);
+
+ verify(mWindowTokenClient).onWindowTokenRemoved();
+ }
+}
diff --git a/core/tests/coretests/src/android/content/TEST_MAPPING b/core/tests/coretests/src/android/content/TEST_MAPPING
new file mode 100644
index 0000000..bbc2458
--- /dev/null
+++ b/core/tests/coretests/src/android/content/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.content.ContentCaptureOptionsTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
new file mode 100644
index 0000000..f8beac2
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentcapture/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.view.contentcapture"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
new file mode 100644
index 0000000..3cd4e17
--- /dev/null
+++ b/core/tests/coretests/src/android/view/contentprotection/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "android.view.contentprotection"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/core/tests/coretests/src/android/window/WindowContextControllerTest.java b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
index a52d2e8..5f2aecc 100644
--- a/core/tests/coretests/src/android/window/WindowContextControllerTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextControllerTest.java
@@ -24,17 +24,20 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.app.servertransaction.WindowTokenClientController;
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,14 +59,26 @@
public class WindowContextControllerTest {
private WindowContextController mController;
@Mock
+ private WindowTokenClientController mWindowTokenClientController;
+ @Mock
private WindowTokenClient mMockToken;
+ private WindowTokenClientController mOriginalController;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mController = new WindowContextController(mMockToken);
doNothing().when(mMockToken).onConfigurationChanged(any(), anyInt(), anyBoolean());
- doReturn(true).when(mMockToken).attachToDisplayArea(anyInt(), anyInt(), any());
+ mOriginalController = WindowTokenClientController.getInstance();
+ WindowTokenClientController.overrideForTesting(mWindowTokenClientController);
+ doReturn(true).when(mWindowTokenClientController).attachToDisplayArea(
+ eq(mMockToken), anyInt(), anyInt(), any());
+ }
+
+ @After
+ public void tearDown() {
+ WindowTokenClientController.overrideForTesting(mOriginalController);
}
@Test(expected = IllegalStateException.class)
@@ -78,7 +93,7 @@
public void testDetachIfNeeded_NotAttachedYet_DoNothing() {
mController.detachIfNeeded();
- verify(mMockToken, never()).detachFromWindowContainerIfNeeded();
+ verify(mWindowTokenClientController, never()).detachIfNeeded(any());
}
@Test
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index a605e2b..8bd500e 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -166,7 +166,7 @@
// *.kt sources are inside a filegroup.
"kotlin-annotations",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
manifest: "AndroidManifest.xml",
plugins: ["dagger2-compiler"],
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt
index d5d072a..122dcbb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/FloatingContentCoordinator.kt
@@ -94,7 +94,6 @@
* non-overlapping.
* @return The new bounds for this content.
*/
- @JvmDefault
fun calculateNewBoundsOnOverlap(
overlappingContentBounds: Rect,
otherContentBounds: List<Rect>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
index 711df0d..c05af73 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -368,7 +368,6 @@
/**
* Called when the active tasks change in desktop mode.
*/
- @JvmDefault
fun onActiveTasksChanged(displayId: Int) {}
}
@@ -379,17 +378,15 @@
/**
* Called when the desktop starts or stops showing freeform tasks.
*/
- @JvmDefault
fun onVisibilityChanged(displayId: Int, hasVisibleFreeformTasks: Boolean) {}
/**
* Called when the desktop stashed status changes.
*/
- @JvmDefault
fun onStashedChanged(displayId: Int, stashed: Boolean) {}
}
}
private fun <T> Iterable<T>.toDumpString(): String {
return joinToString(separator = ", ", prefix = "[", postfix = "]")
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index a242c72..c22cc6f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -186,9 +186,12 @@
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
final RemoteTransition remoteTransition = mRequestedRemotes.get(mergeTarget);
- final IRemoteTransition remote = remoteTransition.getRemoteTransition();
+ if (remoteTransition == null) return;
+
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Merge into remote: %s",
remoteTransition);
+
+ final IRemoteTransition remote = remoteTransition.getRemoteTransition();
if (remote == null) return;
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp
index ad4d97f..38e9f39 100644
--- a/libs/WindowManager/Shell/tests/unittest/Android.bp
+++ b/libs/WindowManager/Shell/tests/unittest/Android.bp
@@ -61,7 +61,7 @@
"libstaticjvmtiagent",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
plugins: ["dagger2-compiler"],
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 7ef82a7..ffc664c 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -61,18 +61,10 @@
ADD_FAILURE() << "ClipState not a rect"; \
}
-#define INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, functionCall) \
- TEST(test_case_name, test_name##_##pipeline) { \
- RenderPipelineType oldType = Properties::getRenderPipelineType(); \
- Properties::overrideRenderPipelineType(RenderPipelineType::pipeline); \
- functionCall; \
- Properties::overrideRenderPipelineType(oldType); \
- };
-
-#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, pipeline) \
- INNER_PIPELINE_TEST(test_case_name, test_name, pipeline, \
- TestUtils::runOnRenderThread( \
- test_case_name##_##test_name##_RenderThreadTest::doTheThing))
+#define INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name) \
+ TEST(test_case_name, test_name) { \
+ TestUtils::runOnRenderThread(test_case_name##_##test_name##_RenderThreadTest::doTheThing); \
+ }
/**
* Like gtest's TEST, but runs on the RenderThread, and 'renderThread' is passed, in top level scope
@@ -83,21 +75,7 @@
public: \
static void doTheThing(renderthread::RenderThread& renderThread); \
}; \
- INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \
- /* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \
- /* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \
- void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \
- renderthread::RenderThread& renderThread)
-
-/**
- * Like RENDERTHREAD_TEST, but only runs with the Skia RenderPipelineTypes
- */
-#define RENDERTHREAD_SKIA_PIPELINE_TEST(test_case_name, test_name) \
- class test_case_name##_##test_name##_RenderThreadTest { \
- public: \
- static void doTheThing(renderthread::RenderThread& renderThread); \
- }; \
- INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaGL); \
+ INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name); \
/* Temporarily disabling Vulkan until we can figure out a way to stub out the driver */ \
/* INNER_PIPELINE_RENDERTHREAD_TEST(test_case_name, test_name, SkiaVulkan); */ \
void test_case_name##_##test_name##_RenderThreadTest::doTheThing( \
diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
index cc7d34b..89d00d3 100644
--- a/libs/hwui/tests/unit/CacheManagerTests.cpp
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -35,7 +35,7 @@
}
// TOOD(258700630): fix this test and re-enable
-RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, DISABLED_trimMemory) {
+RENDERTHREAD_TEST(CacheManager, DISABLED_trimMemory) {
int32_t width = DeviceInfo::get()->getWidth();
int32_t height = DeviceInfo::get()->getHeight();
GrDirectContext* grContext = renderThread.getGrContext();
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 1e055c2..073a835 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -355,7 +355,7 @@
EXPECT_EQ(3, canvas.getIndex());
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, emptyReceiver) {
+RENDERTHREAD_TEST(RenderNodeDrawable, emptyReceiver) {
class ProjectionTestCanvas : public SkCanvas {
public:
ProjectionTestCanvas(int width, int height) : SkCanvas(width, height) {}
@@ -419,7 +419,7 @@
EXPECT_EQ(2, canvas.getDrawCounter());
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) {
+RENDERTHREAD_TEST(RenderNodeDrawable, projectionHwLayer) {
/* R is backward projected on B and C is a layer.
A
/ \
@@ -1052,7 +1052,7 @@
}
// Verify that layers are composed with linear filtering.
-RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) {
+RENDERTHREAD_TEST(RenderNodeDrawable, layerComposeQuality) {
static const int CANVAS_WIDTH = 1;
static const int CANVAS_HEIGHT = 1;
static const int LAYER_WIDTH = 1;
@@ -1170,7 +1170,7 @@
}
// Draw a vector drawable twice but with different bounds and verify correct bounds are used.
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
+RENDERTHREAD_TEST(SkiaRecordingCanvas, drawVectorDrawable) {
static const int CANVAS_WIDTH = 100;
static const int CANVAS_HEIGHT = 200;
class VectorDrawableTestCanvas : public TestCanvasBase {
diff --git a/libs/hwui/tests/unit/ShaderCacheTests.cpp b/libs/hwui/tests/unit/ShaderCacheTests.cpp
index 9aa2e1d..0f8bd13 100644
--- a/libs/hwui/tests/unit/ShaderCacheTests.cpp
+++ b/libs/hwui/tests/unit/ShaderCacheTests.cpp
@@ -370,9 +370,9 @@
}
using namespace android::uirenderer;
-RENDERTHREAD_SKIA_PIPELINE_TEST(ShaderCacheTest, testOnVkFrameFlushed) {
+RENDERTHREAD_TEST(ShaderCacheTest, testOnVkFrameFlushed) {
if (Properties::getRenderPipelineType() != RenderPipelineType::SkiaVulkan) {
- // RENDERTHREAD_SKIA_PIPELINE_TEST declares both SkiaVK and SkiaGL variants.
+ // RENDERTHREAD_TEST declares both SkiaVK and SkiaGL variants.
GTEST_SKIP() << "This test is only applicable to RenderPipelineType::SkiaVulkan";
}
if (!folderExist(getExternalStorageFolder())) {
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 1a1ce1e..f6be7b2 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -136,7 +136,7 @@
}
};
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren) {
+RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren) {
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
@@ -195,7 +195,7 @@
canvasContext->destroy();
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) {
+RENDERTHREAD_TEST(SkiaDisplayList, prepareListAndChildren_vdOffscreen) {
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index 6f180e7..3ded540 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -42,7 +42,7 @@
using namespace android::uirenderer::renderthread;
using namespace android::uirenderer::skiapipeline;
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) {
+RENDERTHREAD_TEST(SkiaPipeline, renderFrame) {
auto redNode = TestUtils::createSkiaNode(
0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -62,7 +62,7 @@
ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) {
+RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckOpaque) {
auto halfGreenNode = TestUtils::createSkiaNode(
0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& bottomHalfGreenCanvas) {
Paint greenPaint;
@@ -89,7 +89,7 @@
ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
+RENDERTHREAD_TEST(SkiaPipeline, renderFrameCheckDirtyRect) {
auto redNode = TestUtils::createSkiaNode(
0, 0, 2, 2, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -111,7 +111,7 @@
ASSERT_EQ(TestUtils::getColor(surface, 1, 1), SK_ColorRED);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) {
+RENDERTHREAD_TEST(SkiaPipeline, renderLayer) {
auto redNode = TestUtils::createSkiaNode(
0, 0, 1, 1, [](RenderProperties& props, SkiaRecordingCanvas& redCanvas) {
redCanvas.drawColor(SK_ColorRED, SkBlendMode::kSrcOver);
@@ -154,7 +154,7 @@
blueNode->setLayerSurface(sk_sp<SkSurface>());
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) {
+RENDERTHREAD_TEST(SkiaPipeline, renderOverdraw) {
ScopedProperty<bool> prop(Properties::debugOverdraw, true);
auto whiteNode = TestUtils::createSkiaNode(
@@ -227,7 +227,7 @@
};
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) {
+RENDERTHREAD_TEST(SkiaPipeline, deferRenderNodeScene) {
class DeferTestCanvas : public SkCanvas {
public:
DeferTestCanvas() : SkCanvas(800, 600) {}
@@ -297,7 +297,7 @@
EXPECT_EQ(4, surface->canvas()->mDrawCounter);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) {
+RENDERTHREAD_TEST(SkiaPipeline, clipped) {
static const int CANVAS_WIDTH = 200;
static const int CANVAS_HEIGHT = 200;
class ClippedTestCanvas : public SkCanvas {
@@ -330,7 +330,7 @@
}
// Test renderFrame with a dirty clip and a pre-transform matrix.
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped_rotated) {
+RENDERTHREAD_TEST(SkiaPipeline, clipped_rotated) {
static const int CANVAS_WIDTH = 200;
static const int CANVAS_HEIGHT = 100;
static const SkMatrix rotateMatrix = SkMatrix::MakeAll(0, -1, CANVAS_HEIGHT, 1, 0, 0, 0, 0, 1);
@@ -366,7 +366,7 @@
EXPECT_EQ(1, surface->canvas()->mDrawCounter);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) {
+RENDERTHREAD_TEST(SkiaPipeline, clip_replace) {
static const int CANVAS_WIDTH = 50;
static const int CANVAS_HEIGHT = 50;
class ClipReplaceTestCanvas : public SkCanvas {
@@ -396,7 +396,7 @@
EXPECT_EQ(1, surface->canvas()->mDrawCounter);
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, context_lost) {
+RENDERTHREAD_TEST(SkiaPipeline, context_lost) {
test::TestContext context;
auto surface = context.surface();
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
@@ -410,7 +410,7 @@
EXPECT_TRUE(pipeline->isSurfaceReady());
}
-RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, pictureCallback) {
+RENDERTHREAD_TEST(SkiaPipeline, pictureCallback) {
// create a pipeline and add a picture callback
auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread);
int callbackCount = 0;
diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp
index 10c874e..76cbc8a 100644
--- a/libs/hwui/tests/unit/main.cpp
+++ b/libs/hwui/tests/unit/main.cpp
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
+#include <getopt.h>
+#include <signal.h>
#include "Properties.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
#include "hwui/Typeface.h"
#include "tests/common/LeakChecker.h"
-#include <signal.h>
-
using namespace std;
using namespace android;
using namespace android::uirenderer;
@@ -45,6 +45,57 @@
raise(sig);
}
+// For options that only exist in long-form. Anything in the
+// 0-255 range is reserved for short options (which just use their ASCII value)
+namespace LongOpts {
+enum {
+ Reserved = 255,
+ Renderer,
+};
+}
+
+static const struct option LONG_OPTIONS[] = {
+ {"renderer", required_argument, nullptr, LongOpts::Renderer}, {0, 0, 0, 0}};
+
+static RenderPipelineType parseRenderer(const char* renderer) {
+ // Anything that's not skiavk is skiagl
+ if (!strcmp(renderer, "skiavk")) {
+ return RenderPipelineType::SkiaVulkan;
+ }
+ return RenderPipelineType::SkiaGL;
+}
+
+struct Options {
+ RenderPipelineType renderer = RenderPipelineType::SkiaGL;
+};
+
+Options parseOptions(int argc, char* argv[]) {
+ int c;
+ opterr = 0;
+ Options opts;
+
+ while (true) {
+ /* getopt_long stores the option index here. */
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "", LONG_OPTIONS, &option_index);
+
+ if (c == -1) break;
+
+ switch (c) {
+ case 0:
+ // Option set a flag, don't need to do anything
+ // (although none of the current LONG_OPTIONS do this...)
+ break;
+
+ case LongOpts::Renderer:
+ opts.renderer = parseRenderer(optarg);
+ break;
+ }
+ }
+ return opts;
+}
+
class TypefaceEnvironment : public testing::Environment {
public:
virtual void SetUp() { Typeface::setRobotoTypefaceForTest(); }
@@ -64,8 +115,9 @@
// Avoid talking to SF
Properties::isolatedProcess = true;
- // Default to GLES (Vulkan-aware tests will override this)
- Properties::overrideRenderPipelineType(RenderPipelineType::SkiaGL);
+
+ auto opts = parseOptions(argc, argv);
+ Properties::overrideRenderPipelineType(opts.renderer);
// Run the tests
testing::InitGoogleTest(&argc, argv);
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index dea7f03..5ea98c0c 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -2615,7 +2615,7 @@
return;
}
auto cryptoInfo =
- cryptoInfoObj ? NativeCryptoInfo{size} : NativeCryptoInfo{env, cryptoInfoObj};
+ cryptoInfoObj ? NativeCryptoInfo{env, cryptoInfoObj} : NativeCryptoInfo{size};
if (env->ExceptionCheck()) {
// Creation of cryptoInfo failed. Let the exception bubble up.
return;
diff --git a/packages/CredentialManager/Android.bp b/packages/CredentialManager/Android.bp
index 28b9bc0..fe26dc3 100644
--- a/packages/CredentialManager/Android.bp
+++ b/packages/CredentialManager/Android.bp
@@ -44,7 +44,7 @@
platform_apis: true,
privileged: true,
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
optimize: {
proguard_compatibility: false,
diff --git a/packages/EasterEgg/Android.bp b/packages/EasterEgg/Android.bp
index 8699f59..0caf505 100644
--- a/packages/EasterEgg/Android.bp
+++ b/packages/EasterEgg/Android.bp
@@ -70,5 +70,5 @@
manifest: "AndroidManifest.xml",
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 7be6043..0f16d93 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -203,7 +203,7 @@
manifest: "AndroidManifest.xml",
javacflags: ["-Adagger.fastInit=enabled"],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
plugins: ["dagger2-compiler"],
@@ -394,7 +394,7 @@
"android.test.base",
"android.test.mock",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
aaptflags: [
"--extra-packages",
"com.android.systemui",
@@ -516,7 +516,7 @@
certificate: "platform",
privileged: true,
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
dxflags: ["--multi-dex"],
optimize: {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 764a855..8306620 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -302,10 +302,9 @@
interface Callback {
/** Whether we are currently on the keyguard or not. */
- @JvmDefault fun isOnKeyguard(): Boolean = false
+ fun isOnKeyguard(): Boolean = false
/** Hide the keyguard and animate using [runner]. */
- @JvmDefault
fun hideKeyguardWithAnimation(runner: IRemoteAnimationRunner) {
throw UnsupportedOperationException()
}
@@ -316,16 +315,16 @@
interface Listener {
/** Called when an activity launch animation started. */
- @JvmDefault fun onLaunchAnimationStart() {}
+ fun onLaunchAnimationStart() {}
/**
* Called when an activity launch animation is finished. This will be called if and only if
* [onLaunchAnimationStart] was called earlier.
*/
- @JvmDefault fun onLaunchAnimationEnd() {}
+ fun onLaunchAnimationEnd() {}
/** Called when an activity launch animation made progress. */
- @JvmDefault fun onLaunchAnimationProgress(linearProgress: Float) {}
+ fun onLaunchAnimationProgress(linearProgress: Float) {}
}
/**
diff --git a/packages/SystemUI/compose/core/tests/Android.bp b/packages/SystemUI/compose/core/tests/Android.bp
index 6119e96..06d94ac 100644
--- a/packages/SystemUI/compose/core/tests/Android.bp
+++ b/packages/SystemUI/compose/core/tests/Android.bp
@@ -44,5 +44,5 @@
"androidx.compose.ui_ui-test-manifest",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/packages/SystemUI/compose/features/tests/Android.bp b/packages/SystemUI/compose/features/tests/Android.bp
index ff534bd..c7c9140 100644
--- a/packages/SystemUI/compose/features/tests/Android.bp
+++ b/packages/SystemUI/compose/features/tests/Android.bp
@@ -44,5 +44,5 @@
"androidx.compose.ui_ui-test-manifest",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/packages/SystemUI/customization/Android.bp b/packages/SystemUI/customization/Android.bp
index dc450bb..fc37b355 100644
--- a/packages/SystemUI/customization/Android.bp
+++ b/packages/SystemUI/customization/Android.bp
@@ -48,5 +48,5 @@
],
min_sdk_version: "current",
plugins: ["dagger2-compiler"],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
}
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 9c864ab..a38c629 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -366,10 +366,16 @@
<!-- Whether or not notifications that can be expanded will always be in their expanded state.
This value only affects notifications that are not a group of notifications from the same
- applications. If this value is false, then only the first notification will be expanded;
- the other notifications need to be manually expanded by the user. -->
+ applications. If this value is false, then only the first notification will be expanded
+ when config_autoExpandFirstNotification is true; the other notifications need to be
+ manually expanded by the user. -->
<bool name="config_alwaysExpandNonGroupedNotifications">false</bool>
+ <!-- Whether or not the first expandable notification will be expanded automatically by the
+ system. This value only affects notifications that are not a group of notifications from
+ the same applications and when config_alwaysExpandNonGroupedNotifications is false. -->
+ <bool name="config_autoExpandFirstNotification">true</bool>
+
<!-- Whether or not an expandable notification can be manually expanded or collapsed by the
user. Grouped notifications are still expandable even if this value is false. -->
<bool name="config_enableNonGroupedNotificationExpand">true</bool>
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index 28e786b..ca30e15 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -65,7 +65,7 @@
],
min_sdk_version: "current",
plugins: ["dagger2-compiler"],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
}
java_library {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 0670ec3..79a1728 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -254,7 +254,7 @@
/** Migrate the indication area to the new keyguard root view. */
// TODO(b/280067944): Tracking bug.
@JvmField
- val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area", teamfood = true)
+ val MIGRATE_INDICATION_AREA = releasedFlag(236, "migrate_indication_area")
/**
* Migrate the bottom area to the new keyguard root view.
@@ -294,6 +294,11 @@
@JvmField
val MIGRATE_NSSL = unreleasedFlag(242, "migrate_nssl")
+ /** Migrate the status view from the notification panel to keyguard root view. */
+ // TODO(b/291767565): Tracking bug.
+ @JvmField
+ val MIGRATE_KEYGUARD_STATUS_VIEW = unreleasedFlag(243, "migrate_keyguard_status_view")
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 3646144..489d2ab 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -171,7 +171,6 @@
* for the canned animation (if applicable) so interested parties can sync with it. If no
* canned animation is playing, these are both 0.
*/
- @JvmDefault
fun onUnlockAnimationStarted(
playingCannedAnimation: Boolean,
isWakeAndUnlockNotFromDream: Boolean,
@@ -184,7 +183,6 @@
* The keyguard is no longer visible in this state and the app/launcher behind the keyguard
* is now completely visible.
*/
- @JvmDefault
fun onUnlockAnimationFinished() {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
index d652889..d949a2a 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
@@ -145,13 +145,10 @@
}
interface Callback {
- @JvmDefault
fun onFlagMicCameraChanged(flag: Boolean) {}
- @JvmDefault
fun onFlagLocationChanged(flag: Boolean) {}
- @JvmDefault
fun onFlagMediaProjectionChanged(flag: Boolean) {}
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
index a676150..eb8ef9b 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
@@ -216,7 +216,6 @@
interface Callback : PrivacyConfig.Callback {
fun onPrivacyItemsChanged(privacyItems: List<PrivacyItem>)
- @JvmDefault
fun onFlagAllChanged(flag: Boolean) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
index bb7f721..468a75d 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/DisplayTracker.kt
@@ -52,12 +52,12 @@
interface Callback {
/** Notifies that a display has been added. */
- @JvmDefault fun onDisplayAdded(displayId: Int) {}
+ fun onDisplayAdded(displayId: Int) {}
/** Notifies that a display has been removed. */
- @JvmDefault fun onDisplayRemoved(displayId: Int) {}
+ fun onDisplayRemoved(displayId: Int) {}
/** Notifies a display has been changed */
- @JvmDefault fun onDisplayChanged(displayId: Int) {}
+ fun onDisplayChanged(displayId: Int) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index 33a3125..93a3e90 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -71,7 +71,6 @@
* Same as {@link onUserChanging(Int, Context, CountDownLatch)} but the latch will be
* auto-decremented after the completion of this method.
*/
- @JvmDefault
fun onUserChanging(newUser: Int, userContext: Context) {}
/**
@@ -82,7 +81,6 @@
* user switch duration. When overriding this method, countDown() MUST be called on the
* latch once execution is complete.
*/
- @JvmDefault
fun onUserChanging(newUser: Int, userContext: Context, latch: CountDownLatch) {
onUserChanging(newUser, userContext)
latch.countDown()
@@ -93,13 +91,11 @@
* Override this method to run things after the screen is unfrozen for the user switch.
* Please see {@link #onUserChanging} if you need to hide jank.
*/
- @JvmDefault
fun onUserChanged(newUser: Int, userContext: Context) {}
/**
* Notifies that the current user's profiles have changed.
*/
- @JvmDefault
fun onProfilesChanged(profiles: List<@JvmSuppressWildcards UserInfo>) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
index 56bb1a6..5804040 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateEvents.kt
@@ -29,12 +29,12 @@
interface ShadeStateEventsListener {
/** Invoked when the notification panel starts or stops collapsing. */
- @JvmDefault fun onPanelCollapsingChanged(isCollapsing: Boolean) {}
+ fun onPanelCollapsingChanged(isCollapsing: Boolean) {}
/**
* Invoked when the notification panel starts or stops launching an [android.app.Activity].
*/
- @JvmDefault fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) {}
+ fun onLaunchingActivityChanged(isLaunchingActivity: Boolean) {}
/**
* Invoked when the "expand immediate" attribute changes.
@@ -45,6 +45,6 @@
* Another example is when full QS is showing, and we swipe up from the bottom. Instead of
* going to QQS, the panel fully collapses.
*/
- @JvmDefault fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {}
+ fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index fb88a96..763400b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -27,10 +27,12 @@
import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.Point;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
+import android.hardware.display.DisplayManager;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
@@ -41,6 +43,7 @@
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.Log;
+import android.view.Display;
import android.view.View;
import android.widget.ImageView;
@@ -74,11 +77,15 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashSet;
+import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.stream.Collectors;
import dagger.Lazy;
@@ -138,6 +145,14 @@
private BackDropView mBackdrop;
private ImageView mBackdropFront;
private ImageView mBackdropBack;
+ private final Point mTmpDisplaySize = new Point();
+
+ private final DisplayManager mDisplayManager;
+ @Nullable
+ private List<String> mSmallerInternalDisplayUids;
+ private Display mCurrentDisplay;
+
+ private LockscreenWallpaper.WallpaperDrawable mWallapperDrawable;
private final MediaController.Callback mMediaListener = new MediaController.Callback() {
@Override
@@ -184,7 +199,8 @@
SysuiColorExtractor colorExtractor,
KeyguardStateController keyguardStateController,
DumpManager dumpManager,
- WallpaperManager wallpaperManager) {
+ WallpaperManager wallpaperManager,
+ DisplayManager displayManager) {
mContext = context;
mMediaArtworkProcessor = mediaArtworkProcessor;
mKeyguardBypassController = keyguardBypassController;
@@ -200,6 +216,7 @@
mStatusBarStateController = statusBarStateController;
mColorExtractor = colorExtractor;
mKeyguardStateController = keyguardStateController;
+ mDisplayManager = displayManager;
mIsLockscreenLiveWallpaperEnabled = wallpaperManager.isLockscreenLiveWallpaperEnabled();
setupNotifPipeline();
@@ -477,6 +494,48 @@
}
/**
+ * Notify lockscreen wallpaper drawable the current internal display.
+ */
+ public void onDisplayUpdated(Display display) {
+ Trace.beginSection("NotificationMediaManager#onDisplayUpdated");
+ mCurrentDisplay = display;
+ if (mWallapperDrawable != null) {
+ mWallapperDrawable.onDisplayUpdated(isOnSmallerInternalDisplays());
+ }
+ Trace.endSection();
+ }
+
+ private boolean isOnSmallerInternalDisplays() {
+ if (mSmallerInternalDisplayUids == null) {
+ mSmallerInternalDisplayUids = findSmallerInternalDisplayUids();
+ }
+ return mSmallerInternalDisplayUids.contains(mCurrentDisplay.getUniqueId());
+ }
+
+ private List<String> findSmallerInternalDisplayUids() {
+ if (mSmallerInternalDisplayUids != null) {
+ return mSmallerInternalDisplayUids;
+ }
+ List<Display> internalDisplays = Arrays.stream(mDisplayManager.getDisplays(
+ DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED))
+ .filter(display -> display.getType() == Display.TYPE_INTERNAL)
+ .collect(Collectors.toList());
+ if (internalDisplays.isEmpty()) {
+ return List.of();
+ }
+ Display largestDisplay = internalDisplays.stream()
+ .max(Comparator.comparingInt(this::getRealDisplayArea))
+ .orElse(internalDisplays.get(0));
+ internalDisplays.remove(largestDisplay);
+ return internalDisplays.stream().map(Display::getUniqueId).collect(Collectors.toList());
+ }
+
+ private int getRealDisplayArea(Display display) {
+ display.getRealSize(mTmpDisplaySize);
+ return mTmpDisplaySize.x * mTmpDisplaySize.y;
+ }
+
+ /**
* Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
*/
public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
@@ -551,7 +610,7 @@
mLockscreenWallpaper != null ? mLockscreenWallpaper.getBitmap() : null;
if (lockWallpaper != null) {
artworkDrawable = new LockscreenWallpaper.WallpaperDrawable(
- mBackdropBack.getResources(), lockWallpaper);
+ mBackdropBack.getResources(), lockWallpaper, isOnSmallerInternalDisplays());
// We're in the SHADE mode on the SIM screen - yet we still need to show
// the lockscreen wallpaper in that mode.
allowWhenShade = mStatusBarStateController.getState() == KEYGUARD;
@@ -611,6 +670,10 @@
mBackdropBack.setBackgroundColor(0xFFFFFFFF);
mBackdropBack.setImageDrawable(new ColorDrawable(c));
} else {
+ if (artworkDrawable instanceof LockscreenWallpaper.WallpaperDrawable) {
+ mWallapperDrawable =
+ (LockscreenWallpaper.WallpaperDrawable) artworkDrawable;
+ }
mBackdropBack.setImageDrawable(artworkDrawable);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 0e20df6..b624115 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -553,7 +553,6 @@
*/
fun onWallpaperZoomOutChanged(zoomOut: Float)
- @JvmDefault
fun onBlurRadiusChanged(blurRadius: Int) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
index 599beec..6be407a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
@@ -29,7 +29,6 @@
*
* @param wifiIndicators a box type containing enough information to properly draw a Wi-Fi icon
*/
- @JvmDefault
fun setWifiIndicators(wifiIndicators: WifiIndicators) {}
/**
@@ -42,7 +41,6 @@
* NOTE: phones can have multiple subscriptions, so this [mobileDataIndicators] object should be
* indexed based on its [subId][MobileDataIndicators.subId]
*/
- @JvmDefault
fun setMobileDataIndicators(mobileDataIndicators: MobileDataIndicators) {}
/**
@@ -51,7 +49,6 @@
*
* @param subs a [SubscriptionInfo] for each subscription that we know about
*/
- @JvmDefault
fun setSubs(subs: List<@JvmSuppressWildcards SubscriptionInfo>) {}
/**
@@ -63,7 +60,6 @@
* @param show whether or not to show a "no sim" view
* @param simDetected whether any SIM is detected or not
*/
- @JvmDefault
fun setNoSims(show: Boolean, simDetected: Boolean) {}
/**
@@ -72,7 +68,6 @@
*
* @param icon an [IconState] for the current ethernet status
*/
- @JvmDefault
fun setEthernetIndicators(icon: IconState) {}
/**
@@ -80,7 +75,6 @@
*
* @param icon an [IconState] for the current airplane mode status
*/
- @JvmDefault
fun setIsAirplaneMode(icon: IconState) {}
/**
@@ -88,7 +82,6 @@
*
* @param enabled the current mobile data feature ennabled state
*/
- @JvmDefault
fun setMobileDataEnabled(enabled: Boolean) {}
/**
@@ -97,7 +90,6 @@
* @param noValidatedNetwork whether there is any validated network.
* @param noNetworksAvailable whether there is any WiFi networks available.
*/
- @JvmDefault
fun setConnectivityStatus(
noDefaultNetwork: Boolean,
noValidatedNetwork: Boolean,
@@ -109,7 +101,6 @@
* @param statusIcon the icon for the call indicator
* @param subId subscription ID for which to update the UI
*/
- @JvmDefault
fun setCallIndicator(statusIcon: IconState, subId: Int) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index e5ba3ce..1c7a186 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -19,6 +19,7 @@
import android.app.IActivityManager;
import android.app.WallpaperManager;
import android.content.Context;
+import android.hardware.display.DisplayManager;
import android.os.RemoteException;
import android.service.dreams.IDreamManager;
import android.util.Log;
@@ -146,7 +147,8 @@
SysuiColorExtractor colorExtractor,
KeyguardStateController keyguardStateController,
DumpManager dumpManager,
- WallpaperManager wallpaperManager) {
+ WallpaperManager wallpaperManager,
+ DisplayManager displayManager) {
return new NotificationMediaManager(
context,
centralSurfacesOptionalLazy,
@@ -162,7 +164,8 @@
colorExtractor,
keyguardStateController,
dumpManager,
- wallpaperManager);
+ wallpaperManager,
+ displayManager);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index 2a18f1f..ef90890 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -49,11 +49,10 @@
fun onSystemEventAnimationFinish(hasPersistentDot: Boolean): Animator? { return null }
// Best method name, change my mind
- @JvmDefault
fun onSystemStatusAnimationTransitionToPersistentDot(contentDescription: String?): Animator? {
return null
}
- @JvmDefault fun onHidePersistentDot(): Animator? { return null }
+ fun onHidePersistentDot(): Animator? { return null }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 9ba2199..8d1e8d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -613,20 +613,20 @@
interface WakeUpListener {
/** Called whenever the notifications are fully hidden or shown */
- @JvmDefault fun onFullyHiddenChanged(isFullyHidden: Boolean) {}
+ fun onFullyHiddenChanged(isFullyHidden: Boolean) {}
/**
* Called whenever the pulseExpansion changes
*
* @param expandingChanged if the user has started or stopped expanding
*/
- @JvmDefault fun onPulseExpansionChanged(expandingChanged: Boolean) {}
+ fun onPulseExpansionChanged(expandingChanged: Boolean) {}
/**
* Called when the animator started by [scheduleDelayedDozeAmountAnimation] begins running
* after the start delay, or after it ends/is cancelled.
*/
- @JvmDefault fun onDelayedDozeAmountAnimationRunning(running: Boolean) {}
+ fun onDelayedDozeAmountAnimationRunning(running: Boolean) {}
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
index 1c5aa3c..8029f48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt
@@ -27,37 +27,31 @@
/** Current top roundness */
@get:FloatRange(from = 0.0, to = 1.0)
- @JvmDefault
val topRoundness: Float
get() = roundableState.topRoundness
/** Current bottom roundness */
@get:FloatRange(from = 0.0, to = 1.0)
- @JvmDefault
val bottomRoundness: Float
get() = roundableState.bottomRoundness
/** Max radius in pixel */
- @JvmDefault
val maxRadius: Float
get() = roundableState.maxRadius
/** Current top corner in pixel, based on [topRoundness] and [maxRadius] */
- @JvmDefault
val topCornerRadius: Float
get() =
if (roundableState.newHeadsUpAnim.isEnabled) roundableState.topCornerRadius
else topRoundness * maxRadius
/** Current bottom corner in pixel, based on [bottomRoundness] and [maxRadius] */
- @JvmDefault
val bottomCornerRadius: Float
get() =
if (roundableState.newHeadsUpAnim.isEnabled) roundableState.bottomCornerRadius
else bottomRoundness * maxRadius
/** Get and update the current radii */
- @JvmDefault
val updatedRadii: FloatArray
get() =
roundableState.radiiBuffer.also { radii ->
@@ -80,7 +74,6 @@
* @param sourceType the source from which the request for roundness comes.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestTopRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float,
sourceType: SourceType,
@@ -125,7 +118,6 @@
* @param sourceType the source from which the request for roundness comes.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestTopRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float,
sourceType: SourceType,
@@ -149,7 +141,6 @@
* @param sourceType the source from which the request for roundness comes.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestBottomRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float,
sourceType: SourceType,
@@ -194,7 +185,6 @@
* @param sourceType the source from which the request for roundness comes.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestBottomRoundness(
@FloatRange(from = 0.0, to = 1.0) value: Float,
sourceType: SourceType,
@@ -219,7 +209,6 @@
* @param animate true if it should animate to that value.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestRoundness(
@FloatRange(from = 0.0, to = 1.0) top: Float,
@FloatRange(from = 0.0, to = 1.0) bottom: Float,
@@ -246,7 +235,6 @@
* @param sourceType the source from which the request for roundness comes.
* @return Whether the roundness was changed.
*/
- @JvmDefault
fun requestRoundness(
@FloatRange(from = 0.0, to = 1.0) top: Float,
@FloatRange(from = 0.0, to = 1.0) bottom: Float,
@@ -270,7 +258,6 @@
* @param sourceType the source from which the request for roundness comes.
* @param animate true if it should animate to that value.
*/
- @JvmDefault
fun requestRoundnessReset(sourceType: SourceType, animate: Boolean) {
requestRoundness(top = 0f, bottom = 0f, sourceType = sourceType, animate = animate)
}
@@ -284,19 +271,16 @@
*
* @param sourceType the source from which the request for roundness comes.
*/
- @JvmDefault
fun requestRoundnessReset(sourceType: SourceType) {
requestRoundnessReset(sourceType = sourceType, animate = roundableState.targetView.isShown)
}
/** Apply the roundness changes, usually means invalidate the [RoundableState.targetView]. */
- @JvmDefault
fun applyRoundnessAndInvalidate() {
roundableState.targetView.invalidate()
}
/** @return true if top or bottom roundness is not zero. */
- @JvmDefault
fun hasRoundedCorner(): Boolean {
return topRoundness != 0f || bottomRoundness != 0f
}
@@ -307,7 +291,6 @@
*
* This method reuses the previous [radii] for performance reasons.
*/
- @JvmDefault
fun updateRadii(
topCornerRadius: Float,
bottomCornerRadius: Float,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
index 1494574..93a34af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
@@ -48,6 +48,14 @@
private val mAlwaysExpandNonGroupedNotification =
context.resources.getBoolean(R.bool.config_alwaysExpandNonGroupedNotifications)
+ /**
+ * `true` if the first non-group expandable notification should be expanded automatically
+ * when possible. If `false`, then the first non-group expandable notification should not
+ * be expanded.
+ */
+ private val mAutoExpandFirstNotification =
+ context.resources.getBoolean(R.bool.config_autoExpandFirstNotification)
+
override fun attach(pipeline: NotifPipeline) {
pipeline.addOnBeforeRenderListListener(::onBeforeRenderList)
pipeline.addOnAfterRenderEntryListener(::onAfterRenderEntry)
@@ -61,8 +69,10 @@
private fun onAfterRenderEntry(entry: NotificationEntry, controller: NotifRowController) {
// If mAlwaysExpandNonGroupedNotification is false, then only expand the
- // very first notification and if it's not a child of grouped notifications.
- controller.setSystemExpanded(mAlwaysExpandNonGroupedNotification || entry == entryToExpand)
+ // very first notification if it's not a child of grouped notifications and when
+ // mAutoExpandFirstNotification is true.
+ controller.setSystemExpanded(mAlwaysExpandNonGroupedNotification ||
+ (mAutoExpandFirstNotification && entry == entryToExpand))
// Show/hide the feedback icon
controller.setFeedbackIcon(mAssistantFeedbackController.getFeedbackIcon(entry))
// Show the "alerted" bell icon
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 8361d6b..c3c9a61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -2061,6 +2061,7 @@
void updateDisplaySize() {
mDisplay.getMetrics(mDisplayMetrics);
mDisplay.getSize(mCurrentDisplaySize);
+ mMediaManager.onDisplayUpdated(mDisplay);
if (DEBUG_GESTURES) {
mGestureRec.tag("display",
String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index b2c39f7..92c786f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -306,19 +306,25 @@
/**
* Drawable that aligns left horizontally and center vertically (like ImageWallpaper).
+ *
+ * <p>Aligns to the center when showing on the smaller internal display of a multi display
+ * device.
*/
public static class WallpaperDrawable extends DrawableWrapper {
private final ConstantState mState;
private final Rect mTmpRect = new Rect();
+ private boolean mIsOnSmallerInternalDisplays;
- public WallpaperDrawable(Resources r, Bitmap b) {
- this(r, new ConstantState(b));
+ public WallpaperDrawable(Resources r, Bitmap b, boolean isOnSmallerInternalDisplays) {
+ this(r, new ConstantState(b), isOnSmallerInternalDisplays);
}
- private WallpaperDrawable(Resources r, ConstantState state) {
+ private WallpaperDrawable(Resources r, ConstantState state,
+ boolean isOnSmallerInternalDisplays) {
super(new BitmapDrawable(r, state.mBackground));
mState = state;
+ mIsOnSmallerInternalDisplays = isOnSmallerInternalDisplays;
}
@Override
@@ -357,10 +363,17 @@
}
dy = (vheight - dheight * scale) * 0.5f;
+ int offsetX = 0;
+ // Offset to show the center area of the wallpaper on a smaller display for multi
+ // display device
+ if (mIsOnSmallerInternalDisplays) {
+ offsetX = bounds.centerX() - (Math.round(dwidth * scale) / 2);
+ }
+
mTmpRect.set(
- bounds.left,
+ bounds.left + offsetX,
bounds.top + Math.round(dy),
- bounds.left + Math.round(dwidth * scale),
+ bounds.left + Math.round(dwidth * scale) + offsetX,
bounds.top + Math.round(dheight * scale + dy));
super.onBoundsChange(mTmpRect);
@@ -371,6 +384,17 @@
return mState;
}
+ /**
+ * Update bounds when the hosting display or the display size has changed.
+ *
+ * @param isOnSmallerInternalDisplays true if the drawable is on one of the internal
+ * displays with the smaller area.
+ */
+ public void onDisplayUpdated(boolean isOnSmallerInternalDisplays) {
+ mIsOnSmallerInternalDisplays = isOnSmallerInternalDisplays;
+ onBoundsChange(getBounds());
+ }
+
static class ConstantState extends Drawable.ConstantState {
private final Bitmap mBackground;
@@ -386,7 +410,7 @@
@Override
public Drawable newDrawable(@Nullable Resources res) {
- return new WallpaperDrawable(res, this);
+ return new WallpaperDrawable(res, this, /* isOnSmallerInternalDisplays= */ false);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index a9135ac..ad8530d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -79,7 +79,6 @@
private final HeadsUpManagerPhone mHeadsUpManager;
private final AboveShelfObserver mAboveShelfObserver;
private final DozeScrimController mDozeScrimController;
- private final CentralSurfaces mCentralSurfaces;
private final NotificationsInteractor mNotificationsInteractor;
private final NotificationStackScrollLayoutController mNsslController;
private final LockscreenShadeTransitionController mShadeTransitionController;
@@ -107,7 +106,6 @@
NotificationShadeWindowController notificationShadeWindowController,
DynamicPrivacyController dynamicPrivacyController,
KeyguardStateController keyguardStateController,
- CentralSurfaces centralSurfaces,
NotificationsInteractor notificationsInteractor,
LockscreenShadeTransitionController shadeTransitionController,
PowerInteractor powerInteractor,
@@ -128,8 +126,6 @@
mQsController = quickSettingsController;
mHeadsUpManager = headsUp;
mDynamicPrivacyController = dynamicPrivacyController;
- // TODO: use KeyguardStateController#isOccluded to remove this dependency
- mCentralSurfaces = centralSurfaces;
mNotificationsInteractor = notificationsInteractor;
mNsslController = stackScrollerController;
mShadeTransitionController = shadeTransitionController;
@@ -283,7 +279,7 @@
@Override
public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
- if (mCentralSurfaces.isOccluded()) {
+ if (mKeyguardStateController.isOccluded()) {
boolean devicePublic = mLockscreenUserManager
.isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
boolean userPublic = devicePublic
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 5d09e06..a501e87 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -20,6 +20,11 @@
import com.android.systemui.Dependency;
+/**
+ * @deprecated Don't use this class to listen to Secure Settings. Use {@code SecureSettings} instead
+ * or {@code SettingsObserver} to be able to specify the handler.
+ */
+@Deprecated
public abstract class TunerService {
public static final String ACTION_CLEAR = "com.android.systemui.action.CLEAR_TUNER";
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 8cfe2ea..ccc0a79 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -56,7 +56,11 @@
/**
+ * @deprecated Don't use this class to listen to Secure Settings. Use {@code SecureSettings} instead
+ * or {@code SettingsObserver} to be able to specify the handler.
+ * This class will interact with SecureSettings using the main looper.
*/
+@Deprecated
@SysUISingleton
public class TunerServiceImpl extends TunerService {
diff --git a/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt b/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
index 693c270..5582ced 100644
--- a/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
@@ -23,7 +23,6 @@
* changes.
*/
interface UserAwareController {
- @JvmDefault
fun changeUser(newUser: UserHandle) {}
val currentUserId: Int
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index cd8aaa2..9c52788 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -79,7 +79,6 @@
private CommandQueue mCommandQueue;
private FakeMetricsLogger mMetricsLogger;
private final ShadeController mShadeController = mock(ShadeController.class);
- private final CentralSurfaces mCentralSurfaces = mock(CentralSurfaces.class);
private final NotificationsInteractor mNotificationsInteractor =
mock(NotificationsInteractor.class);
private final KeyguardStateController mKeyguardStateController =
@@ -118,7 +117,6 @@
mock(NotificationShadeWindowController.class),
mock(DynamicPrivacyController.class),
mKeyguardStateController,
- mCentralSurfaces,
mNotificationsInteractor,
mock(LockscreenShadeTransitionController.class),
mock(PowerInteractor.class),
@@ -202,7 +200,6 @@
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isOccluded()).thenReturn(false);
- when(mCentralSurfaces.isOccluded()).thenReturn(false);
assertFalse(mInterruptSuppressor.suppressAwakeHeadsUp(entry));
}
diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp
index 2e0a946..1f0181f 100644
--- a/packages/SystemUI/unfold/Android.bp
+++ b/packages/SystemUI/unfold/Android.bp
@@ -33,7 +33,7 @@
"dagger2",
"jsr330",
],
- kotlincflags: ["-Xjvm-default=enable"],
+ kotlincflags: ["-Xjvm-default=all"],
java_version: "1.8",
sdk_version: "current",
min_sdk_version: "current",
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
index fee485d..896444d 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
@@ -35,14 +35,12 @@
interface TransitionProgressListener {
/** Called when transition is started */
- @JvmDefault
fun onTransitionStarted() {}
/**
* Called whenever transition progress is updated, [progress] is a value of the animation
* where 0 is fully folded, 1 is fully unfolded
*/
- @JvmDefault
fun onTransitionProgress(@FloatRange(from = 0.0, to = 1.0) progress: Float) {}
/**
@@ -51,11 +49,9 @@
* For example, in [PhysicsBasedUnfoldTransitionProgressProvider] this could happen when the
* animation is not tied to the hinge angle anymore and it is about to run fixed animation.
*/
- @JvmDefault
fun onTransitionFinishing() {}
/** Called when transition is completely finished */
- @JvmDefault
fun onTransitionFinished() {}
}
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
index 0af372f..bce7e88 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
@@ -31,9 +31,9 @@
val isFinishedOpening: Boolean
interface FoldUpdatesListener {
- @JvmDefault fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float) {}
- @JvmDefault fun onFoldUpdate(@FoldUpdate update: Int) {}
- @JvmDefault fun onUnfoldedScreenAvailable() {}
+ fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float) {}
+ fun onFoldUpdate(@FoldUpdate update: Int) {}
+ fun onUnfoldedScreenAvailable() {}
}
@IntDef(
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index 1482d07..3f3fa34 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -246,6 +246,31 @@
}
@GuardedBy("mLock")
+ boolean isAtEdge() {
+ return isAtLeftEdge() || isAtRightEdge() || isAtTopEdge() || isAtBottomEdge();
+ }
+
+ @GuardedBy("mLock")
+ boolean isAtLeftEdge() {
+ return getOffsetX() == getMaxOffsetXLocked();
+ }
+
+ @GuardedBy("mLock")
+ boolean isAtRightEdge() {
+ return getOffsetX() == getMinOffsetXLocked();
+ }
+
+ @GuardedBy("mLock")
+ boolean isAtTopEdge() {
+ return getOffsetY() == getMaxOffsetYLocked();
+ }
+
+ @GuardedBy("mLock")
+ boolean isAtBottomEdge() {
+ return getOffsetY() == getMinOffsetYLocked();
+ }
+
+ @GuardedBy("mLock")
float getCenterX() {
return (mMagnificationBounds.width() / 2.0f
+ mMagnificationBounds.left - getOffsetX()) / getScale();
@@ -1086,6 +1111,87 @@
}
/**
+ * Returns whether the user is at one of the edges (left, right, top, bottom)
+ * of the magnification viewport
+ *
+ * @param displayId
+ * @return if user is at the edge of the view
+ */
+ public boolean isAtEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtEdge();
+ }
+ }
+
+ /**
+ * Returns whether the user is at the left edge of the viewport
+ *
+ * @param displayId
+ * @return if user is at left edge of view
+ */
+ public boolean isAtLeftEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtLeftEdge();
+ }
+ }
+
+ /**
+ * Returns whether the user is at the right edge of the viewport
+ *
+ * @param displayId
+ * @return if user is at right edge of view
+ */
+ public boolean isAtRightEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtRightEdge();
+ }
+ }
+
+ /**
+ * Returns whether the user is at the top edge of the viewport
+ *
+ * @param displayId
+ * @return if user is at top edge of view
+ */
+ public boolean isAtTopEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtTopEdge();
+ }
+ }
+
+ /**
+ * Returns whether the user is at the bottom edge of the viewport
+ *
+ * @param displayId
+ * @return if user is at bottom edge of view
+ */
+ public boolean isAtBottomEdge(int displayId) {
+ synchronized (mLock) {
+ final DisplayMagnification display = mDisplays.get(displayId);
+ if (display == null) {
+ return false;
+ }
+ return display.isAtBottomEdge();
+ }
+ }
+
+ /**
* Returns the Y offset of the magnification viewport. If an animation
* is in progress, this reflects the end state of the animation.
*
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index 038847e..4aebbf1 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -137,6 +137,7 @@
@VisibleForTesting final DetectingState mDetectingState;
@VisibleForTesting final PanningScalingState mPanningScalingState;
@VisibleForTesting final ViewportDraggingState mViewportDraggingState;
+ @VisibleForTesting final SinglePanningState mSinglePanningState;
private final ScreenStateReceiver mScreenStateReceiver;
private final WindowMagnificationPromptController mPromptController;
@@ -146,7 +147,7 @@
private PointerCoords[] mTempPointerCoords;
private PointerProperties[] mTempPointerProperties;
-
+ @VisibleForTesting boolean mIsSinglePanningEnabled;
public FullScreenMagnificationGestureHandler(@UiContext Context context,
FullScreenMagnificationController fullScreenMagnificationController,
AccessibilityTraceManager trace,
@@ -202,6 +203,8 @@
mDetectingState = new DetectingState(context);
mViewportDraggingState = new ViewportDraggingState();
mPanningScalingState = new PanningScalingState(context);
+ mSinglePanningState = new SinglePanningState(context);
+ setSinglePanningEnabled(false);
if (mDetectShortcutTrigger) {
mScreenStateReceiver = new ScreenStateReceiver(context, this);
@@ -213,6 +216,11 @@
transitionTo(mDetectingState);
}
+ @VisibleForTesting
+ void setSinglePanningEnabled(boolean isEnabled) {
+ mIsSinglePanningEnabled = isEnabled;
+ }
+
@Override
void onMotionEventInternal(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
handleEventWith(mCurrentState, event, rawEvent, policyFlags);
@@ -223,6 +231,7 @@
// To keep InputEventConsistencyVerifiers within GestureDetectors happy
mPanningScalingState.mScrollGestureDetector.onTouchEvent(event);
mPanningScalingState.mScaleGestureDetector.onTouchEvent(event);
+ mSinglePanningState.mScrollGestureDetector.onTouchEvent(event);
try {
stateHandler.onMotionEvent(event, rawEvent, policyFlags);
@@ -669,7 +678,6 @@
@Override
public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-
// Ensures that the state at the end of delegation is consistent with the last delegated
// UP/DOWN event in queue: still delegating if pointer is down, detecting otherwise
switch (event.getActionMasked()) {
@@ -726,6 +734,8 @@
@VisibleForTesting Handler mHandler = new Handler(Looper.getMainLooper(), this);
+ private PointF mFirstPointerDownLocation = new PointF(Float.NaN, Float.NaN);
+
DetectingState(Context context) {
mLongTapMinDelay = ViewConfiguration.getLongPressTimeout();
mMultiTapMaxDelay = ViewConfiguration.getDoubleTapTimeout()
@@ -765,10 +775,11 @@
cacheDelayedMotionEvent(event, rawEvent, policyFlags);
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
-
mLastDetectingDownEventTime = event.getDownTime();
mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+ mFirstPointerDownLocation.set(event.getX(), event.getY());
+
if (!mFullScreenMagnificationController.magnificationRegionContains(
mDisplayId, event.getX(), event.getY())) {
@@ -800,7 +811,7 @@
break;
case ACTION_POINTER_DOWN: {
if (isActivated() && event.getPointerCount() == 2) {
- storeSecondPointerDownLocation(event);
+ storePointerDownLocation(mSecondPointerDownLocation, event);
mHandler.sendEmptyMessageDelayed(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
ViewConfiguration.getTapTimeout());
} else {
@@ -815,7 +826,6 @@
case ACTION_MOVE: {
if (isFingerDown()
&& distance(mLastDown, /* move */ event) > mSwipeMinDistance) {
-
// Swipe detected - transition immediately
// For convenience, viewport dragging takes precedence
@@ -826,10 +836,15 @@
} else if (isActivated() && event.getPointerCount() == 2) {
//Primary pointer is swiping, so transit to PanningScalingState
transitToPanningScalingStateAndClear();
+ } else if (mIsSinglePanningEnabled
+ && isActivated()
+ && event.getPointerCount() == 1
+ && !isOverscroll(event)) {
+ transitToSinglePanningStateAndClear();
} else {
transitionToDelegatingStateAndClear();
}
- } else if (isActivated() && secondPointerDownValid()
+ } else if (isActivated() && pointerDownValid(mSecondPointerDownLocation)
&& distanceClosestPointerToPoint(
mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance) {
//Second pointer is swiping, so transit to PanningScalingState
@@ -843,11 +858,9 @@
if (!mFullScreenMagnificationController.magnificationRegionContains(
mDisplayId, event.getX(), event.getY())) {
-
transitionToDelegatingStateAndClear();
} else if (isMultiTapTriggered(3 /* taps */)) {
-
onTripleTap(/* up */ event);
} else if (
@@ -856,7 +869,6 @@
//TODO long tap should never happen here
&& ((timeBetween(mLastDown, mLastUp) >= mLongTapMinDelay)
|| (distance(mLastDown, mLastUp) >= mSwipeMinDistance))) {
-
transitionToDelegatingStateAndClear();
}
@@ -865,14 +877,28 @@
}
}
- private void storeSecondPointerDownLocation(MotionEvent event) {
- final int index = event.getActionIndex();
- mSecondPointerDownLocation.set(event.getX(index), event.getY(index));
+ private boolean isOverscroll(MotionEvent event) {
+ if (!pointerDownValid(mFirstPointerDownLocation)) {
+ return false;
+ }
+ float dX = event.getX() - mFirstPointerDownLocation.x;
+ float dY = event.getY() - mFirstPointerDownLocation.y;
+ boolean didOverscroll =
+ mFullScreenMagnificationController.isAtLeftEdge(mDisplayId) && dX > 0
+ || mFullScreenMagnificationController.isAtRightEdge(mDisplayId) && dX < 0
+ || mFullScreenMagnificationController.isAtTopEdge(mDisplayId) && dY > 0
+ || mFullScreenMagnificationController.isAtBottomEdge(mDisplayId) && dY < 0;
+ return didOverscroll;
}
- private boolean secondPointerDownValid() {
- return !(Float.isNaN(mSecondPointerDownLocation.x) && Float.isNaN(
- mSecondPointerDownLocation.y));
+ private void storePointerDownLocation(PointF pointerDownLocation, MotionEvent event) {
+ final int index = event.getActionIndex();
+ pointerDownLocation.set(event.getX(index), event.getY(index));
+ }
+
+ private boolean pointerDownValid(PointF pointerDownLocation) {
+ return !(Float.isNaN(pointerDownLocation.x) && Float.isNaN(
+ pointerDownLocation.y));
}
private void transitToPanningScalingStateAndClear() {
@@ -880,6 +906,11 @@
clear();
}
+ private void transitToSinglePanningStateAndClear() {
+ transitionTo(mSinglePanningState);
+ clear();
+ }
+
public boolean isMultiTapTriggered(int numTaps) {
// Shortcut acts as the 2 initial taps
@@ -947,6 +978,7 @@
setShortcutTriggered(false);
removePendingDelayedMessages();
clearDelayedMotionEvents();
+ mFirstPointerDownLocation.set(Float.NaN, Float.NaN);
mSecondPointerDownLocation.set(Float.NaN, Float.NaN);
}
@@ -1165,12 +1197,14 @@
+ ", mDelegatingState=" + mDelegatingState
+ ", mMagnifiedInteractionState=" + mPanningScalingState
+ ", mViewportDraggingState=" + mViewportDraggingState
+ + ", mSinglePanningState=" + mSinglePanningState
+ ", mDetectTripleTap=" + mDetectTripleTap
+ ", mDetectShortcutTrigger=" + mDetectShortcutTrigger
+ ", mCurrentState=" + State.nameOf(mCurrentState)
+ ", mPreviousState=" + State.nameOf(mPreviousState)
+ ", mMagnificationController=" + mFullScreenMagnificationController
+ ", mDisplayId=" + mDisplayId
+ + ", mIsSinglePanningEnabled=" + mIsSinglePanningEnabled
+ '}';
}
@@ -1285,8 +1319,67 @@
* Indicates an error with a gesture handler or state.
*/
private static class GestureException extends Exception {
+
GestureException(String message) {
super(message);
}
}
+
+ final class SinglePanningState extends SimpleOnGestureListener implements State {
+ private final GestureDetector mScrollGestureDetector;
+ private MotionEventInfo mEvent;
+
+ SinglePanningState(Context context) {
+ mScrollGestureDetector = new GestureDetector(context, this, Handler.getMain());
+ }
+
+ @Override
+ public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ int action = event.getActionMasked();
+ switch (action) {
+ case ACTION_UP:
+ case ACTION_CANCEL:
+ clear();
+ transitionTo(mDetectingState);
+ break;
+ }
+ }
+
+ @Override
+ public boolean onScroll(
+ MotionEvent first, MotionEvent second, float distanceX, float distanceY) {
+ if (mCurrentState != mSinglePanningState) {
+ return true;
+ }
+ mFullScreenMagnificationController.offsetMagnifiedRegion(
+ mDisplayId,
+ distanceX,
+ distanceY,
+ AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID);
+ if (DEBUG_PANNING_SCALING) {
+ Slog.i(
+ mLogTag,
+ "SinglePanningState Panned content by scrollX: "
+ + distanceX
+ + " scrollY: "
+ + distanceY
+ + " isAtEdge: "
+ + mFullScreenMagnificationController.isAtEdge(mDisplayId));
+ }
+ // TODO: b/280812104 Dispatch events before Delegation
+ if (mFullScreenMagnificationController.isAtEdge(mDisplayId)) {
+ clear();
+ transitionTo(mDelegatingState);
+ }
+ return /* event consumed: */ true;
+ }
+
+ @Override
+ public String toString() {
+ return "SinglePanningState{"
+ + "isEdgeOfView="
+ + mFullScreenMagnificationController.isAtEdge(mDisplayId)
+ + "}";
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c9769b3..26bcb98 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5083,6 +5083,10 @@
// Tell anyone interested that we are done booting!
SystemProperties.set("sys.boot_completed", "1");
SystemProperties.set("dev.bootcomplete", "1");
+
+ // Start PSI monitoring in LMKD if it was skipped earlier.
+ ProcessList.startPsiMonitoringAfterBoot();
+
mUserController.onBootComplete(
new IIntentReceiver.Stub() {
@Override
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index acd9771..e484a6c 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -357,6 +357,7 @@
static final byte LMK_UPDATE_PROPS = 7;
static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event
static final byte LMK_STATE_CHANGED = 9; // Msg to subscribed clients on state changed
+ static final byte LMK_START_MONITORING = 9; // Start monitoring if delayed earlier
// Low Memory Killer Daemon command codes.
// These must be kept in sync with async_event_type definitions in lmkd.h
@@ -1568,6 +1569,15 @@
return true;
}
+ /**
+ * {@hide}
+ */
+ public static void startPsiMonitoringAfterBoot() {
+ ByteBuffer buf = ByteBuffer.allocate(4);
+ buf.putInt(LMK_START_MONITORING);
+ writeLmkd(buf, null);
+ }
+
private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) {
if (!sLmkdConnection.isConnected()) {
// try to connect immediately and then keep retrying
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2db724f..8e1ad65 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2491,16 +2491,6 @@
getContext().registerReceiver(mReviewNotificationPermissionsReceiver,
ReviewNotificationPermissionsReceiver.getFilter(),
Context.RECEIVER_NOT_EXPORTED);
-
- mAppOps.startWatchingMode(AppOpsManager.OP_POST_NOTIFICATION, null,
- new AppOpsManager.OnOpChangedInternalListener() {
- @Override
- public void onOpChanged(@NonNull String op, @NonNull String packageName,
- int userId) {
- mHandler.post(
- () -> handleNotificationPermissionChange(packageName, userId));
- }
- });
}
/**
@@ -3560,16 +3550,20 @@
}
mPermissionHelper.setNotificationPermission(
pkg, UserHandle.getUserId(uid), enabled, true);
+ sendAppBlockStateChangedBroadcast(pkg, uid, !enabled);
mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_BAN_APP_NOTES)
.setType(MetricsEvent.TYPE_ACTION)
.setPackageName(pkg)
.setSubtype(enabled ? 1 : 0));
mNotificationChannelLogger.logAppNotificationsAllowed(uid, pkg, enabled);
+ // Now, cancel any outstanding notifications that are part of a just-disabled app
+ if (!enabled) {
+ cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0,
+ UserHandle.getUserId(uid), REASON_PACKAGE_BANNED);
+ }
- // Outstanding notifications from this package will be cancelled, and the package will
- // be sent the ACTION_APP_BLOCK_STATE_CHANGED broadcast, as soon as we get the
- // callback from AppOpsManager.
+ handleSavePolicyFile();
}
/**
@@ -5889,21 +5883,6 @@
}
};
- private void handleNotificationPermissionChange(String pkg, @UserIdInt int userId) {
- int uid = mPackageManagerInternal.getPackageUid(pkg, 0, userId);
- if (uid == INVALID_UID) {
- Log.e(TAG, String.format("No uid found for %s, %s!", pkg, userId));
- return;
- }
- boolean hasPermission = mPermissionHelper.hasPermission(uid);
- sendAppBlockStateChangedBroadcast(pkg, uid, !hasPermission);
- if (!hasPermission) {
- cancelAllNotificationsInt(MY_UID, MY_PID, pkg, /* channelId= */ null,
- /* mustHaveFlags= */ 0, /* mustNotHaveFlags= */ 0, userId,
- REASON_PACKAGE_BANNED);
- }
- }
-
protected void checkNotificationListenerAccess() {
if (!isCallerSystemOrPhone()) {
getContext().enforceCallingPermission(
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 36a0b0c..1f5bd3e 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -75,6 +75,7 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
import com.android.internal.logging.MetricsLogger;
@@ -108,30 +109,34 @@
static final int RULE_LIMIT_PER_PACKAGE = 100;
// pkg|userId => uid
- protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
+ @VisibleForTesting protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
private final Context mContext;
private final H mHandler;
private final SettingsObserver mSettingsObserver;
private final AppOpsManager mAppOps;
- @VisibleForTesting protected final NotificationManager mNotificationManager;
+ private final NotificationManager mNotificationManager;
private final SysUiStatsEvent.BuilderFactory mStatsEventBuilderFactory;
- @VisibleForTesting protected ZenModeConfig mDefaultConfig;
+ private ZenModeConfig mDefaultConfig;
private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
private final ZenModeFiltering mFiltering;
- protected final RingerModeDelegate mRingerModeDelegate = new
+ private final RingerModeDelegate mRingerModeDelegate = new
RingerModeDelegate();
@VisibleForTesting protected final ZenModeConditions mConditions;
- Object mConfigsLock = new Object();
+ private final Object mConfigsArrayLock = new Object();
+ @GuardedBy("mConfigsArrayLock")
@VisibleForTesting final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>();
private final Metrics mMetrics = new Metrics();
private final ConditionProviders.Config mServiceConfig;
- private SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
- @VisibleForTesting protected ZenModeEventLogger mZenModeEventLogger;
+ private final SystemUiSystemPropertiesFlags.FlagResolver mFlagResolver;
+ private final ZenModeEventLogger mZenModeEventLogger;
@VisibleForTesting protected int mZenMode;
@VisibleForTesting protected NotificationManager.Policy mConsolidatedPolicy;
private int mUser = UserHandle.USER_SYSTEM;
+
+ private final Object mConfigLock = new Object();
+ @GuardedBy("mConfigLock")
@VisibleForTesting protected ZenModeConfig mConfig;
@VisibleForTesting protected AudioManagerInternal mAudioManager;
protected PackageManager mPm;
@@ -159,7 +164,7 @@
mDefaultConfig = readDefaultConfig(mContext.getResources());
updateDefaultAutomaticRuleNames();
mConfig = mDefaultConfig.copy();
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.put(UserHandle.USER_SYSTEM, mConfig);
}
mConsolidatedPolicy = mConfig.toNotificationPolicy();
@@ -186,7 +191,7 @@
public boolean matchesCallFilter(UserHandle userHandle, Bundle extras,
ValidateNotificationPeople validator, int contactsTimeoutMs, float timeoutAffinity,
int callingUid) {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
return ZenModeFiltering.matchesCallFilter(mContext, mZenMode, mConsolidatedPolicy,
userHandle, extras, validator, contactsTimeoutMs, timeoutAffinity,
callingUid);
@@ -206,7 +211,7 @@
}
public boolean shouldIntercept(NotificationRecord record) {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
return mFiltering.shouldIntercept(mZenMode, mConsolidatedPolicy, record);
}
}
@@ -221,7 +226,7 @@
public void initZenMode() {
if (DEBUG) Log.d(TAG, "initZenMode");
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
// "update" config to itself, which will have no effect in the case where a config
// was read in via XML, but will initialize zen mode if nothing was read in and the
// config remains the default.
@@ -250,7 +255,7 @@
public void onUserRemoved(int user) {
if (user < UserHandle.USER_SYSTEM) return;
if (DEBUG) Log.d(TAG, "onUserRemoved u=" + user);
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.remove(user);
}
}
@@ -268,7 +273,7 @@
mUser = user;
if (DEBUG) Log.d(TAG, reason + " u=" + user);
ZenModeConfig config = null;
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
if (mConfigs.get(user) != null) {
config = mConfigs.get(user).copy();
}
@@ -278,7 +283,7 @@
config = mDefaultConfig.copy();
config.user = user;
}
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
setConfigLocked(config, null, reason, Process.SYSTEM_UID, true);
}
cleanUpZenRules();
@@ -314,7 +319,7 @@
public List<ZenRule> getZenRules() {
List<ZenRule> rules = new ArrayList<>();
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return rules;
for (ZenRule rule : mConfig.automaticRules.values()) {
if (canManageAutomaticZenRule(rule)) {
@@ -327,7 +332,7 @@
public AutomaticZenRule getAutomaticZenRule(String id) {
ZenRule rule;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return null;
rule = mConfig.automaticRules.get(id);
}
@@ -364,7 +369,7 @@
}
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) {
throw new AndroidRuntimeException("Could not create rule");
}
@@ -387,7 +392,7 @@
public boolean updateAutomaticZenRule(String ruleId, AutomaticZenRule automaticZenRule,
String reason, int callingUid, boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return false;
if (DEBUG) {
Log.d(TAG, "updateAutomaticZenRule zenRule=" + automaticZenRule
@@ -419,7 +424,7 @@
public boolean removeAutomaticZenRule(String id, String reason, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return false;
newConfig = mConfig.copy();
ZenRule ruleToRemove = newConfig.automaticRules.get(id);
@@ -450,7 +455,7 @@
public boolean removeAutomaticZenRules(String packageName, String reason, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return false;
newConfig = mConfig.copy();
for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
@@ -467,7 +472,7 @@
public void setAutomaticZenRuleState(String id, Condition condition, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return;
newConfig = mConfig.copy();
@@ -481,7 +486,7 @@
public void setAutomaticZenRuleState(Uri ruleDefinition, Condition condition, int callingUid,
boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return;
newConfig = mConfig.copy();
@@ -491,6 +496,7 @@
}
}
+ @GuardedBy("mConfigLock")
private void setAutomaticZenRuleStateLocked(ZenModeConfig config, List<ZenRule> rules,
Condition condition, int callingUid, boolean fromSystemOrSystemUi) {
if (rules == null || rules.isEmpty()) return;
@@ -538,7 +544,7 @@
return 0;
}
int count = 0;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
for (ZenRule rule : mConfig.automaticRules.values()) {
if (cn.equals(rule.component) || cn.equals(rule.configurationActivity)) {
count++;
@@ -555,7 +561,7 @@
return 0;
}
int count = 0;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
for (ZenRule rule : mConfig.automaticRules.values()) {
if (pkg.equals(rule.getPkg())) {
count++;
@@ -588,19 +594,23 @@
protected void updateDefaultZenRules(int callingUid, boolean fromSystemOrSystemUi) {
updateDefaultAutomaticRuleNames();
- for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
- ZenRule currRule = mConfig.automaticRules.get(defaultRule.id);
- // if default rule wasn't user-modified nor enabled, use localized name
- // instead of previous system name
- if (currRule != null && !currRule.modified && !currRule.enabled
- && !defaultRule.name.equals(currRule.name)) {
- if (canManageAutomaticZenRule(currRule)) {
- if (DEBUG) Slog.d(TAG, "Locale change - updating default zen rule name "
- + "from " + currRule.name + " to " + defaultRule.name);
- // update default rule (if locale changed, name of rule will change)
- currRule.name = defaultRule.name;
- updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
- "locale changed", callingUid, fromSystemOrSystemUi);
+ synchronized (mConfigLock) {
+ for (ZenRule defaultRule : mDefaultConfig.automaticRules.values()) {
+ ZenRule currRule = mConfig.automaticRules.get(defaultRule.id);
+ // if default rule wasn't user-modified nor enabled, use localized name
+ // instead of previous system name
+ if (currRule != null && !currRule.modified && !currRule.enabled
+ && !defaultRule.name.equals(currRule.name)) {
+ if (canManageAutomaticZenRule(currRule)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Locale change - updating default zen rule name "
+ + "from " + currRule.name + " to " + defaultRule.name);
+ }
+ // update default rule (if locale changed, name of rule will change)
+ currRule.name = defaultRule.name;
+ updateAutomaticZenRule(defaultRule.id, createAutomaticZenRule(currRule),
+ "locale changed", callingUid, fromSystemOrSystemUi);
+ }
}
}
}
@@ -686,7 +696,7 @@
private void setManualZenMode(int zenMode, Uri conditionId, String reason, String caller,
boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) {
ZenModeConfig newConfig;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig == null) return;
if (!Global.isValidZenMode(zenMode)) return;
if (DEBUG) Log.d(TAG, "setManualZenMode " + Global.zenModeToString(zenMode)
@@ -715,7 +725,7 @@
void dump(ProtoOutputStream proto) {
proto.write(ZenModeProto.ZEN_MODE, mZenMode);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
if (mConfig.manualRule != null) {
mConfig.manualRule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS);
}
@@ -737,14 +747,14 @@
pw.println(Global.zenModeToString(mZenMode));
pw.print(prefix);
pw.println("mConsolidatedPolicy=" + mConsolidatedPolicy.toString());
- synchronized(mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
final int N = mConfigs.size();
for (int i = 0; i < N; i++) {
dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i));
}
}
pw.print(prefix); pw.print("mUser="); pw.println(mUser);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
dump(pw, prefix, "mConfig", mConfig);
}
@@ -833,7 +843,7 @@
Settings.Secure.ZEN_SETTINGS_UPDATED, 1, userId);
}
if (DEBUG) Log.d(TAG, reason);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
setConfigLocked(config, null, reason, Process.SYSTEM_UID, true);
}
}
@@ -841,7 +851,7 @@
public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId)
throws IOException {
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
final int n = mConfigs.size();
for (int i = 0; i < n; i++) {
if (forBackup && mConfigs.keyAt(i) != userId) {
@@ -856,7 +866,9 @@
* @return user-specified default notification policy for priority only do not disturb
*/
public Policy getNotificationPolicy() {
- return getNotificationPolicy(mConfig);
+ synchronized (mConfigLock) {
+ return getNotificationPolicy(mConfig);
+ }
}
private static Policy getNotificationPolicy(ZenModeConfig config) {
@@ -867,8 +879,8 @@
* Sets the global notification policy used for priority only do not disturb
*/
public void setNotificationPolicy(Policy policy, int callingUid, boolean fromSystemOrSystemUi) {
- if (policy == null || mConfig == null) return;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
+ if (policy == null || mConfig == null) return;
final ZenModeConfig newConfig = mConfig.copy();
newConfig.applyNotificationPolicy(policy);
setConfigLocked(newConfig, null, "setNotificationPolicy", callingUid,
@@ -881,7 +893,7 @@
*/
private void cleanUpZenRules() {
long currentTime = System.currentTimeMillis();
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
final ZenModeConfig newConfig = mConfig.copy();
if (newConfig.automaticRules != null) {
for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
@@ -906,7 +918,7 @@
* @return a copy of the zen mode configuration
*/
public ZenModeConfig getConfig() {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
return mConfig.copy();
}
}
@@ -918,7 +930,8 @@
return mConsolidatedPolicy.copy();
}
- public boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
+ @GuardedBy("mConfigLock")
+ private boolean setConfigLocked(ZenModeConfig config, ComponentName triggeringComponent,
String reason, int callingUid, boolean fromSystemOrSystemUi) {
return setConfigLocked(config, reason, triggeringComponent, true /*setRingerMode*/,
callingUid, fromSystemOrSystemUi);
@@ -926,11 +939,12 @@
public void setConfig(ZenModeConfig config, ComponentName triggeringComponent, String reason,
int callingUid, boolean fromSystemOrSystemUi) {
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
setConfigLocked(config, triggeringComponent, reason, callingUid, fromSystemOrSystemUi);
}
}
+ @GuardedBy("mConfigLock")
private boolean setConfigLocked(ZenModeConfig config, String reason,
ComponentName triggeringComponent, boolean setRingerMode, int callingUid,
boolean fromSystemOrSystemUi) {
@@ -942,7 +956,7 @@
}
if (config.user != mUser) {
// simply store away for background users
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.put(config.user, config);
}
if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user);
@@ -951,7 +965,7 @@
// handle CPS backed conditions - danger! may modify config
mConditions.evaluateConfig(config, null, false /*processSubscriptions*/);
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
mConfigs.put(config.user, config);
}
if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable());
@@ -979,6 +993,7 @@
* Carries out a config update (if needed) and (re-)evaluates the zen mode value afterwards.
* If logging is enabled, will also request logging of the outcome of this change if needed.
*/
+ @GuardedBy("mConfigLock")
private void updateConfigAndZenModeLocked(ZenModeConfig config, String reason,
boolean setRingerMode, int callingUid, boolean fromSystemOrSystemUi) {
final boolean logZenModeEvents = mFlagResolver.isEnabled(
@@ -993,7 +1008,7 @@
}
final String val = Integer.toString(config.hashCode());
Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
- evaluateZenMode(reason, setRingerMode);
+ evaluateZenModeLocked(reason, setRingerMode);
// After all changes have occurred, log if requested
if (logZenModeEvents) {
ZenModeEventLogger.ZenModeInfo newInfo = new ZenModeEventLogger.ZenModeInfo(
@@ -1025,7 +1040,8 @@
}
@VisibleForTesting
- protected void evaluateZenMode(String reason, boolean setRingerMode) {
+ @GuardedBy("mConfigLock")
+ protected void evaluateZenModeLocked(String reason, boolean setRingerMode) {
if (DEBUG) Log.d(TAG, "evaluateZenMode");
if (mConfig == null) return;
final int policyHashBefore = mConsolidatedPolicy == null ? 0
@@ -1056,8 +1072,8 @@
}
private int computeZenMode() {
- if (mConfig == null) return Global.ZEN_MODE_OFF;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
+ if (mConfig == null) return Global.ZEN_MODE_OFF;
if (mConfig.manualRule != null) return mConfig.manualRule.zenMode;
int zen = Global.ZEN_MODE_OFF;
for (ZenRule automaticRule : mConfig.automaticRules.values()) {
@@ -1094,8 +1110,8 @@
}
private void updateConsolidatedPolicy(String reason) {
- if (mConfig == null) return;
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
+ if (mConfig == null) return;
ZenPolicy policy = new ZenPolicy();
if (mConfig.manualRule != null) {
applyCustomPolicy(policy, mConfig.manualRule);
@@ -1293,7 +1309,7 @@
* Generate pulled atoms about do not disturb configurations.
*/
public void pullRules(List<StatsEvent> events) {
- synchronized (mConfigsLock) {
+ synchronized (mConfigsArrayLock) {
final int numConfigs = mConfigs.size();
for (int i = 0; i < numConfigs; i++) {
final int user = mConfigs.keyAt(i);
@@ -1319,6 +1335,7 @@
}
}
+ @GuardedBy("mConfigsArrayLock")
private void ruleToProtoLocked(int user, ZenRule rule, boolean isManualRule,
List<StatsEvent> events) {
// Make the ID safe.
@@ -1389,7 +1406,7 @@
if (mZenMode == Global.ZEN_MODE_OFF
|| (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- && !ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(mConfig))) {
+ && !areAllPriorityOnlyRingerSoundsMuted())) {
// in priority only with ringer not muted, save ringer mode changes
// in dnd off, save ringer mode changes
setPreviousRingerModeSetting(ringerModeNew);
@@ -1410,8 +1427,7 @@
&& (mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
|| mZenMode == Global.ZEN_MODE_ALARMS
|| (mZenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
- && ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(
- mConfig)))) {
+ && areAllPriorityOnlyRingerSoundsMuted()))) {
newZen = Global.ZEN_MODE_OFF;
} else if (mZenMode != Global.ZEN_MODE_OFF) {
ringerModeExternalOut = AudioManager.RINGER_MODE_SILENT;
@@ -1430,6 +1446,12 @@
return ringerModeExternalOut;
}
+ private boolean areAllPriorityOnlyRingerSoundsMuted() {
+ synchronized (mConfigLock) {
+ return ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(mConfig);
+ }
+ }
+
@Override
public int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew, String caller,
int ringerModeInternal, VolumePolicy policy) {
@@ -1633,7 +1655,7 @@
private void emitRules() {
final long now = SystemClock.elapsedRealtime();
final long since = (now - mRuleCountLogTime);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
int numZenRules = mConfig.automaticRules.size();
if (mNumZenRules != numZenRules
|| since > MINIMUM_LOG_PERIOD_MS) {
@@ -1651,7 +1673,7 @@
private void emitDndType() {
final long now = SystemClock.elapsedRealtime();
final long since = (now - mTypeLogTimeMs);
- synchronized (mConfig) {
+ synchronized (mConfigLock) {
boolean dndOn = mZenMode != Global.ZEN_MODE_OFF;
int zenType = !dndOn ? DND_OFF
: (mConfig.manualRule != null) ? DND_ON_MANUAL : DND_ON_AUTOMATIC;
diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING
index 3c846da..5e23765 100644
--- a/services/core/java/com/android/server/pm/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/TEST_MAPPING
@@ -18,7 +18,12 @@
"name": "CtsCompilationTestCases"
},
{
- "name": "CtsAppEnumerationTestCases"
+ "name": "CtsAppEnumerationTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
},
{
"name": "CtsMatchFlagTestCases"
@@ -129,6 +134,9 @@
},
{
"name": "PackageManagerServiceHostTests"
+ },
+ {
+ "name": "CtsAppEnumerationTestCases"
}
],
"imports": [
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7bbe878..5e01c49 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -208,7 +208,6 @@
import com.android.internal.policy.PhoneWindow;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.AccessibilityManagerInternal;
@@ -228,6 +227,7 @@
import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
import com.android.server.policy.keyguard.KeyguardStateMonitor.StateCallback;
import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.vibrator.HapticFeedbackVibrationProvider;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -371,12 +371,6 @@
private static final String TALKBACK_LABEL = "TalkBack";
private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800;
- private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
- private static final VibrationAttributes PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES =
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION);
- private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
/**
* Keyguard stuff
@@ -450,6 +444,7 @@
PackageManager mPackageManager;
SideFpsEventHandler mSideFpsEventHandler;
LockPatternUtils mLockPatternUtils;
+ private HapticFeedbackVibrationProvider mHapticFeedbackVibrationProvider;
private boolean mHasFeatureAuto;
private boolean mHasFeatureWatch;
private boolean mHasFeatureLeanback;
@@ -458,9 +453,6 @@
// Assigned on main thread, accessed on UI thread
volatile VrManagerInternal mVrManagerInternal;
- // Vibrator pattern for haptic feedback during boot when safe mode is enabled.
- long[] mSafeModeEnabledVibePattern;
-
/** If true, hitting shift & menu will broadcast Intent.ACTION_BUG_REPORT */
boolean mEnableShiftMenuBugReports = false;
@@ -558,7 +550,6 @@
int mPowerVolUpBehavior;
boolean mStylusButtonsEnabled = true;
boolean mHasSoftInput = false;
- boolean mHapticTextHandleEnabled;
boolean mUseTvRouting;
boolean mAllowStartActivityForLongPressOnPowerDuringSetup;
MetricsLogger mLogger;
@@ -2251,9 +2242,6 @@
mAllowStartActivityForLongPressOnPowerDuringSetup = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowStartActivityForLongPressOnPowerInSetup);
- mHapticTextHandleEnabled = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_enableHapticTextHandle);
-
mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION;
mHandleVolumeKeysInWM = mContext.getResources().getBoolean(
@@ -2294,8 +2282,8 @@
mContext.registerReceiver(mMultiuserReceiver, filter);
mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
- mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(),
- com.android.internal.R.array.config_safeModeEnabledVibePattern);
+ mHapticFeedbackVibrationProvider =
+ new HapticFeedbackVibrationProvider(mContext.getResources(), mVibrator);
mGlobalKeyManager = new GlobalKeyManager(mContext);
@@ -5499,10 +5487,6 @@
}
}
- static long[] getLongIntArray(Resources r, int resid) {
- return ArrayUtils.convertToLongArray(r.getIntArray(resid));
- }
-
private void bindKeyguard() {
synchronized (mLock) {
if (mKeyguardBound) {
@@ -5989,138 +5973,18 @@
if (!mVibrator.hasVibrator()) {
return false;
}
- VibrationEffect effect = getVibrationEffect(effectId);
+ VibrationEffect effect =
+ mHapticFeedbackVibrationProvider.getVibrationForHapticFeedback(effectId);
if (effect == null) {
return false;
}
- VibrationAttributes attrs = getVibrationAttributes(effectId);
- if (always) {
- attrs = new VibrationAttributes.Builder(attrs)
- .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
- .build();
- }
+ VibrationAttributes attrs =
+ mHapticFeedbackVibrationProvider.getVibrationAttributesForHapticFeedback(
+ effectId, /* bypassVibrationIntensitySetting= */ always);
mVibrator.vibrate(uid, packageName, effect, reason, attrs);
return true;
}
- private VibrationEffect getVibrationEffect(int effectId) {
- long[] pattern;
- switch (effectId) {
- case HapticFeedbackConstants.CONTEXT_CLICK:
- case HapticFeedbackConstants.GESTURE_END:
- case HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE:
- case HapticFeedbackConstants.SCROLL_TICK:
- case HapticFeedbackConstants.SEGMENT_TICK:
- return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
-
- case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
- if (!mHapticTextHandleEnabled) {
- return null;
- }
- // fallthrough
- case HapticFeedbackConstants.CLOCK_TICK:
- case HapticFeedbackConstants.SEGMENT_FREQUENT_TICK:
- return VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
-
- case HapticFeedbackConstants.KEYBOARD_RELEASE:
- case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
- case HapticFeedbackConstants.ENTRY_BUMP:
- case HapticFeedbackConstants.DRAG_CROSSING:
- return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
-
- case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
- case HapticFeedbackConstants.VIRTUAL_KEY:
- case HapticFeedbackConstants.EDGE_RELEASE:
- case HapticFeedbackConstants.CALENDAR_DATE:
- case HapticFeedbackConstants.CONFIRM:
- case HapticFeedbackConstants.GESTURE_START:
- case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
- case HapticFeedbackConstants.SCROLL_LIMIT:
- return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
-
- case HapticFeedbackConstants.LONG_PRESS:
- case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
- case HapticFeedbackConstants.DRAG_START:
- case HapticFeedbackConstants.EDGE_SQUEEZE:
- return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
-
- case HapticFeedbackConstants.REJECT:
- return VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
-
- case HapticFeedbackConstants.SAFE_MODE_ENABLED:
- pattern = mSafeModeEnabledVibePattern;
- break;
-
- case HapticFeedbackConstants.ASSISTANT_BUTTON:
- if (mVibrator.areAllPrimitivesSupported(
- VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
- VibrationEffect.Composition.PRIMITIVE_TICK)) {
- // quiet ramp, short pause, then sharp tick
- return VibrationEffect.startComposition()
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f)
- .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50)
- .compose();
- }
- // fallback for devices without composition support
- return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
-
- case HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE:
- return getScaledPrimitiveOrElseEffect(
- VibrationEffect.Composition.PRIMITIVE_TICK, 0.4f,
- VibrationEffect.EFFECT_TEXTURE_TICK);
-
- case HapticFeedbackConstants.TOGGLE_ON:
- return getScaledPrimitiveOrElseEffect(
- VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f,
- VibrationEffect.EFFECT_TICK);
-
- case HapticFeedbackConstants.TOGGLE_OFF:
- return getScaledPrimitiveOrElseEffect(
- VibrationEffect.Composition.PRIMITIVE_LOW_TICK, 0.2f,
- VibrationEffect.EFFECT_TEXTURE_TICK);
-
- case HapticFeedbackConstants.NO_HAPTICS:
- default:
- return null;
- }
- if (pattern.length == 0) {
- // No vibration
- return null;
- } else if (pattern.length == 1) {
- // One-shot vibration
- return VibrationEffect.createOneShot(pattern[0], VibrationEffect.DEFAULT_AMPLITUDE);
- } else {
- // Pattern vibration
- return VibrationEffect.createWaveform(pattern, -1);
- }
- }
-
- private VibrationEffect getScaledPrimitiveOrElseEffect(int primitiveId, float scale,
- int elseEffectId) {
- if (mVibrator.areAllPrimitivesSupported(primitiveId)) {
- return VibrationEffect.startComposition()
- .addPrimitive(primitiveId, scale)
- .compose();
- } else {
- return VibrationEffect.get(elseEffectId);
- }
- }
-
- private VibrationAttributes getVibrationAttributes(int effectId) {
- switch (effectId) {
- case HapticFeedbackConstants.EDGE_SQUEEZE:
- case HapticFeedbackConstants.EDGE_RELEASE:
- return PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES;
- case HapticFeedbackConstants.ASSISTANT_BUTTON:
- case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
- case HapticFeedbackConstants.SCROLL_TICK:
- case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
- case HapticFeedbackConstants.SCROLL_LIMIT:
- return HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES;
- default:
- return TOUCH_VIBRATION_ATTRIBUTES;
- }
- }
@Override
public void keepScreenOnStartedLw() {
@@ -6258,8 +6122,7 @@
pw.print("mAllowStartActivityForLongPressOnPowerDuringSetup=");
pw.println(mAllowStartActivityForLongPressOnPowerDuringSetup);
pw.print(prefix);
- pw.print("mHasSoftInput="); pw.print(mHasSoftInput);
- pw.print(" mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled);
+ pw.print("mHasSoftInput="); pw.println(mHasSoftInput);
pw.print(prefix);
pw.print("mDismissImeOnBackKeyPressed="); pw.print(mDismissImeOnBackKeyPressed);
pw.print(" mIncallPowerBehavior=");
@@ -6284,6 +6147,7 @@
pw.print(" mLockScreenTimeout="); pw.print(mLockScreenTimeout);
pw.print(" mLockScreenTimerActive="); pw.println(mLockScreenTimerActive);
+ mHapticFeedbackVibrationProvider.dump(prefix, pw);
mGlobalKeyManager.dump(prefix, pw);
mKeyCombinationManager.dump(prefix, pw);
mSingleKeyGestureDetector.dump(prefix, pw);
diff --git a/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
new file mode 100644
index 0000000..308ce4f
--- /dev/null
+++ b/services/core/java/com/android/server/vibrator/HapticFeedbackVibrationProvider.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2023 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.vibrator;
+
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.os.VibrationAttributes;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
+import android.view.HapticFeedbackConstants;
+
+import java.io.PrintWriter;
+
+/**
+ * Provides the {@link VibrationEffect} and {@link VibrationAttributes} for haptic feedback.
+ *
+ * @hide
+ */
+public final class HapticFeedbackVibrationProvider {
+ private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
+ private static final VibrationAttributes PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_PHYSICAL_EMULATION);
+ private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
+
+ private final Vibrator mVibrator;
+ private final boolean mHapticTextHandleEnabled;
+ // Vibrator effect for haptic feedback during boot when safe mode is enabled.
+ private final VibrationEffect mSafeModeEnabledVibrationEffect;
+
+ /** @hide */
+ public HapticFeedbackVibrationProvider(Resources res, Vibrator vibrator) {
+ mVibrator = vibrator;
+ mHapticTextHandleEnabled = res.getBoolean(
+ com.android.internal.R.bool.config_enableHapticTextHandle);
+ mSafeModeEnabledVibrationEffect =
+ VibrationSettings.createEffectFromResource(
+ res, com.android.internal.R.array.config_safeModeEnabledVibePattern);
+ }
+
+ /**
+ * Provides the {@link VibrationEffect} for a given haptic feedback effect ID (provided in
+ * {@link HapticFeedbackConstants}).
+ *
+ * @param effectId the haptic feedback effect ID whose respective vibration we want to get.
+ * @return a {@link VibrationEffect} for the given haptic feedback effect ID, or {@code null} if
+ * the provided effect ID is not supported.
+ */
+ @Nullable public VibrationEffect getVibrationForHapticFeedback(int effectId) {
+ switch (effectId) {
+ case HapticFeedbackConstants.CONTEXT_CLICK:
+ case HapticFeedbackConstants.GESTURE_END:
+ case HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE:
+ case HapticFeedbackConstants.SCROLL_TICK:
+ case HapticFeedbackConstants.SEGMENT_TICK:
+ return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
+
+ case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
+ if (!mHapticTextHandleEnabled) {
+ return null;
+ }
+ // fallthrough
+ case HapticFeedbackConstants.CLOCK_TICK:
+ case HapticFeedbackConstants.SEGMENT_FREQUENT_TICK:
+ return VibrationEffect.get(VibrationEffect.EFFECT_TEXTURE_TICK);
+
+ case HapticFeedbackConstants.KEYBOARD_RELEASE:
+ case HapticFeedbackConstants.VIRTUAL_KEY_RELEASE:
+ case HapticFeedbackConstants.ENTRY_BUMP:
+ case HapticFeedbackConstants.DRAG_CROSSING:
+ return VibrationEffect.get(VibrationEffect.EFFECT_TICK, false);
+
+ case HapticFeedbackConstants.KEYBOARD_TAP: // == KEYBOARD_PRESS
+ case HapticFeedbackConstants.VIRTUAL_KEY:
+ case HapticFeedbackConstants.EDGE_RELEASE:
+ case HapticFeedbackConstants.CALENDAR_DATE:
+ case HapticFeedbackConstants.CONFIRM:
+ case HapticFeedbackConstants.GESTURE_START:
+ case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
+ case HapticFeedbackConstants.SCROLL_LIMIT:
+ return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+
+ case HapticFeedbackConstants.LONG_PRESS:
+ case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
+ case HapticFeedbackConstants.DRAG_START:
+ case HapticFeedbackConstants.EDGE_SQUEEZE:
+ return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
+
+ case HapticFeedbackConstants.REJECT:
+ return VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+
+ case HapticFeedbackConstants.SAFE_MODE_ENABLED:
+ return mSafeModeEnabledVibrationEffect;
+
+ case HapticFeedbackConstants.ASSISTANT_BUTTON:
+ if (mVibrator.areAllPrimitivesSupported(
+ VibrationEffect.Composition.PRIMITIVE_QUICK_RISE,
+ VibrationEffect.Composition.PRIMITIVE_TICK)) {
+ // quiet ramp, short pause, then sharp tick
+ return VibrationEffect.startComposition()
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.25f)
+ .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1f, 50)
+ .compose();
+ }
+ // fallback for devices without composition support
+ return VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK);
+
+ case HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE:
+ return getScaledPrimitiveOrElseEffect(
+ VibrationEffect.Composition.PRIMITIVE_TICK, 0.4f,
+ VibrationEffect.EFFECT_TEXTURE_TICK);
+
+ case HapticFeedbackConstants.TOGGLE_ON:
+ return getScaledPrimitiveOrElseEffect(
+ VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f,
+ VibrationEffect.EFFECT_TICK);
+
+ case HapticFeedbackConstants.TOGGLE_OFF:
+ return getScaledPrimitiveOrElseEffect(
+ VibrationEffect.Composition.PRIMITIVE_LOW_TICK, 0.2f,
+ VibrationEffect.EFFECT_TEXTURE_TICK);
+
+ case HapticFeedbackConstants.NO_HAPTICS:
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Provides the {@link VibrationAttributes} that should be used for a haptic feedback.
+ *
+ * @param effectId the haptic feedback effect ID whose respective vibration attributes we want
+ * to get.
+ * @param bypassVibrationIntensitySetting {@code true} if the returned attribute should bypass
+ * vibration intensity settings. {@code false} otherwise.
+ * @return the {@link VibrationAttributes} that should be used for the provided haptic feedback.
+ */
+ public VibrationAttributes getVibrationAttributesForHapticFeedback(
+ int effectId, boolean bypassVibrationIntensitySetting) {
+ VibrationAttributes attrs;
+ switch (effectId) {
+ case HapticFeedbackConstants.EDGE_SQUEEZE:
+ case HapticFeedbackConstants.EDGE_RELEASE:
+ attrs = PHYSICAL_EMULATION_VIBRATION_ATTRIBUTES;
+ break;
+ case HapticFeedbackConstants.ASSISTANT_BUTTON:
+ case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
+ case HapticFeedbackConstants.SCROLL_TICK:
+ case HapticFeedbackConstants.SCROLL_ITEM_FOCUS:
+ case HapticFeedbackConstants.SCROLL_LIMIT:
+ attrs = HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES;
+ break;
+ default:
+ attrs = TOUCH_VIBRATION_ATTRIBUTES;
+ }
+ if (bypassVibrationIntensitySetting) {
+ attrs = new VibrationAttributes.Builder(attrs)
+ .setFlags(VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF)
+ .build();
+ }
+ return attrs;
+ }
+
+ /** Dumps relevant state. */
+ public void dump(String prefix, PrintWriter pw) {
+ pw.print("mHapticTextHandleEnabled="); pw.println(mHapticTextHandleEnabled);
+ }
+
+ private VibrationEffect getScaledPrimitiveOrElseEffect(
+ int primitiveId, float scale, int elseEffectId) {
+ if (mVibrator.areAllPrimitivesSupported(primitiveId)) {
+ return VibrationEffect.startComposition()
+ .addPrimitive(primitiveId, scale)
+ .compose();
+ } else {
+ return VibrationEffect.get(elseEffectId);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 4ae7c77..dbd6bf4 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -698,7 +698,23 @@
@Nullable
private VibrationEffect createEffectFromResource(int resId) {
- long[] timings = getLongIntArray(mContext.getResources(), resId);
+ return createEffectFromResource(mContext.getResources(), resId);
+ }
+
+ /**
+ * Provides a {@link VibrationEffect} from a timings-array provided as an int-array resource..
+ *
+ * <p>If the timings array is {@code null} or empty, it returns {@code null}.
+ *
+ * <p>If the timings array has a size of one, it returns a one-shot vibration with duration that
+ * is equal to the single value in the array.
+ *
+ * <p>If the timings array has more than one values, it returns a non-repeating wave-form
+ * vibration with off-on timings as per the provided timings array.
+ */
+ @Nullable
+ static VibrationEffect createEffectFromResource(Resources res, int resId) {
+ long[] timings = getLongIntArray(res, resId);
return createEffectFromTimings(timings);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 7996434..b55af76 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1016,8 +1016,7 @@
&& mWallpaper.userId == mCurrentUserId) {
Slog.w(TAG, "Wallpaper reconnect timed out for " + mWallpaper.wallpaperComponent
+ ", reverting to built-in wallpaper!");
- clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId,
- null);
+ clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null);
}
}
};
@@ -1197,7 +1196,7 @@
} else {
// Timeout
Slog.w(TAG, "Reverting to built-in wallpaper!");
- clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
+ clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null);
final String flattened = wpService.flattenToString();
EventLog.writeEvent(EventLogTags.WP_WALLPAPER_CRASHED,
flattened.substring(0, Math.min(flattened.length(),
@@ -1236,8 +1235,7 @@
} else {
if (mLmkLimitRebindRetries <= 0) {
Slog.w(TAG, "Reverting to built-in wallpaper due to lmk!");
- clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId,
- null);
+ clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null);
mLmkLimitRebindRetries = LMK_RECONNECT_REBIND_RETRIES;
return;
}
@@ -1256,7 +1254,7 @@
&& mWallpaper.lastDiedTime + MIN_WALLPAPER_CRASH_TIME
> SystemClock.uptimeMillis()) {
Slog.w(TAG, "Reverting to built-in wallpaper!");
- clearWallpaperLocked(true, FLAG_SYSTEM, mWallpaper.userId, null);
+ clearWallpaperLocked(FLAG_SYSTEM, mWallpaper.userId, null);
} else {
mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
tryToRebind();
@@ -1497,7 +1495,7 @@
wallpaper, null)) {
Slog.w(TAG, "Wallpaper " + wpService
+ " no longer available; reverting to default");
- clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
}
}
}
@@ -1585,7 +1583,7 @@
if (doit) {
Slog.w(TAG, "Wallpaper uninstalled, removing: "
+ wallpaper.wallpaperComponent);
- clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
}
}
}
@@ -1606,7 +1604,7 @@
} catch (NameNotFoundException e) {
Slog.w(TAG, "Wallpaper component gone, removing: "
+ wallpaper.wallpaperComponent);
- clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
}
}
if (wallpaper.nextWallpaperComponent != null
@@ -1638,7 +1636,8 @@
mShuttingDown = false;
mImageWallpaper = ComponentName.unflattenFromString(
context.getResources().getString(R.string.image_wallpaper_component));
- mDefaultWallpaperComponent = WallpaperManager.getCmfDefaultWallpaperComponent(context);
+ ComponentName defaultComponent = WallpaperManager.getCmfDefaultWallpaperComponent(context);
+ mDefaultWallpaperComponent = defaultComponent == null ? mImageWallpaper : defaultComponent;
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mIPackageManager = AppGlobals.getPackageManager();
@@ -1721,7 +1720,7 @@
if (DEBUG) {
Slog.i(TAG, "Unable to regenerate crop; resetting");
}
- clearWallpaperLocked(false, FLAG_SYSTEM, UserHandle.USER_SYSTEM, null);
+ clearWallpaperLocked(FLAG_SYSTEM, UserHandle.USER_SYSTEM, null);
}
} else {
if (DEBUG) {
@@ -1978,7 +1977,7 @@
}
if (si == null) {
- clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, reply);
+ clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, reply);
} else {
Slog.w(TAG, "Wallpaper isn't direct boot aware; using fallback until unlocked");
// We might end up persisting the current wallpaper data
@@ -2002,10 +2001,10 @@
if (serviceInfo == null) {
if (wallpaper.mWhich == (FLAG_LOCK | FLAG_SYSTEM)) {
- clearWallpaperLocked(false, FLAG_SYSTEM, wallpaper.userId, null);
- clearWallpaperLocked(false, FLAG_LOCK, wallpaper.userId, reply);
+ clearWallpaperLocked(FLAG_SYSTEM, wallpaper.userId, null);
+ clearWallpaperLocked(FLAG_LOCK, wallpaper.userId, reply);
} else {
- clearWallpaperLocked(false, wallpaper.mWhich, wallpaper.userId, reply);
+ clearWallpaperLocked(wallpaper.mWhich, wallpaper.userId, reply);
}
return;
}
@@ -2038,9 +2037,9 @@
WallpaperData data = null;
synchronized (mLock) {
if (mIsLockscreenLiveWallpaperEnabled) {
- clearWallpaperLocked(callingPackage, false, which, userId);
+ clearWallpaperLocked(callingPackage, which, userId);
} else {
- clearWallpaperLocked(false, which, userId, null);
+ clearWallpaperLocked(which, userId, null);
}
if (which == FLAG_LOCK) {
@@ -2058,8 +2057,7 @@
}
}
- private void clearWallpaperLocked(String callingPackage, boolean defaultFailed,
- int which, int userId) {
+ private void clearWallpaperLocked(String callingPackage, int which, int userId) {
// Might need to bring it in the first time to establish our rewrite
if (!mWallpaperMap.contains(userId)) {
@@ -2094,7 +2092,7 @@
component = wallpaper.wallpaperComponent;
finalWhich = FLAG_LOCK | FLAG_SYSTEM;
} else {
- component = defaultFailed ? mImageWallpaper : null;
+ component = null;
finalWhich = which;
}
@@ -2114,8 +2112,7 @@
}
// TODO(b/266818039) remove this version of the method
- private void clearWallpaperLocked(boolean defaultFailed, int which, int userId,
- IRemoteCallback reply) {
+ private void clearWallpaperLocked(int which, int userId, IRemoteCallback reply) {
if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to clear");
}
@@ -2168,9 +2165,7 @@
wallpaper.primaryColors = null;
wallpaper.imageWallpaperPending = false;
if (userId != mCurrentUserId) return;
- if (bindWallpaperComponentLocked(defaultFailed
- ? mImageWallpaper
- : null, true, false, wallpaper, reply)) {
+ if (bindWallpaperComponentLocked(null, true, false, wallpaper, reply)) {
return;
}
} catch (IllegalArgumentException e1) {
@@ -3523,11 +3518,6 @@
try {
if (componentName == null) {
componentName = mDefaultWallpaperComponent;
- if (componentName == null) {
- // Fall back to static image wallpaper
- componentName = mImageWallpaper;
- if (DEBUG_LIVE) Slog.v(TAG, "No default component; using image wallpaper");
- }
}
int serviceUserId = wallpaper.userId;
ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
@@ -3997,7 +3987,8 @@
mFallbackWallpaper = new WallpaperData(systemUserId, FLAG_SYSTEM);
mFallbackWallpaper.allowBackup = false;
mFallbackWallpaper.wallpaperId = makeWallpaperIdLocked();
- bindWallpaperComponentLocked(mImageWallpaper, true, false, mFallbackWallpaper, null);
+ bindWallpaperComponentLocked(mDefaultWallpaperComponent, true, false,
+ mFallbackWallpaper, null);
}
}
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index 56edde0..271d71e 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -24,6 +24,7 @@
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityThread;
@@ -40,7 +41,6 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -48,7 +48,6 @@
import android.util.Slog;
import android.view.Display;
import android.view.Gravity;
-import android.view.IWindowManager;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -56,7 +55,6 @@
import android.view.WindowInsets;
import android.view.WindowInsets.Type;
import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.Button;
@@ -95,6 +93,10 @@
*/
@Nullable
private Context mWindowContext;
+ /**
+ * The root display area feature id that the {@link #mWindowContext} is attaching to.
+ */
+ private int mWindowContextRootDisplayAreaId = FEATURE_UNDEFINED;
// Local copy of vr mode enabled state, to avoid calling into VrManager with
// the lock held.
private boolean mVrModeEnabled;
@@ -206,12 +208,15 @@
private void handleHide() {
if (mClingWindow != null) {
if (DEBUG) Slog.d(TAG, "Hiding immersive mode confirmation");
- // We don't care which root display area the window manager is specifying for removal.
- try {
- getWindowManager(FEATURE_UNDEFINED).removeView(mClingWindow);
- } catch (WindowManager.InvalidDisplayException e) {
- Slog.w(TAG, "Fail to hide the immersive confirmation window because of " + e);
- return;
+ if (mWindowManager != null) {
+ try {
+ mWindowManager.removeView(mClingWindow);
+ } catch (WindowManager.InvalidDisplayException e) {
+ Slog.w(TAG, "Fail to hide the immersive confirmation window because of "
+ + e);
+ }
+ mWindowManager = null;
+ mWindowContext = null;
}
mClingWindow = null;
}
@@ -394,26 +399,18 @@
* @return the WindowManager specifying with the {@code rootDisplayAreaId} to attach the
* confirmation window.
*/
- private WindowManager getWindowManager(int rootDisplayAreaId) {
- if (mWindowManager == null || mWindowContext == null) {
- // Create window context to specify the RootDisplayArea
- final Bundle options = getOptionsForWindowContext(rootDisplayAreaId);
- mWindowContext = mContext.createWindowContext(
- IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options);
- mWindowManager = mWindowContext.getSystemService(WindowManager.class);
- return mWindowManager;
+ @NonNull
+ private WindowManager createWindowManager(int rootDisplayAreaId) {
+ if (mWindowManager != null) {
+ throw new IllegalStateException(
+ "Must not create a new WindowManager while there is an existing one");
}
-
- // Update the window context and window manager to specify the RootDisplayArea
+ // Create window context to specify the RootDisplayArea
final Bundle options = getOptionsForWindowContext(rootDisplayAreaId);
- final IWindowManager wms = WindowManagerGlobal.getWindowManagerService();
- try {
- wms.attachWindowContextToDisplayArea(mWindowContext.getWindowContextToken(),
- IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, mContext.getDisplayId(), options);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
-
+ mWindowContextRootDisplayAreaId = rootDisplayAreaId;
+ mWindowContext = mContext.createWindowContext(
+ IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options);
+ mWindowManager = mWindowContext.getSystemService(WindowManager.class);
return mWindowManager;
}
@@ -434,14 +431,23 @@
}
private void handleShow(int rootDisplayAreaId) {
+ if (mClingWindow != null) {
+ if (rootDisplayAreaId == mWindowContextRootDisplayAreaId) {
+ if (DEBUG) Slog.d(TAG, "Immersive mode confirmation has already been shown");
+ return;
+ } else {
+ // Hide the existing confirmation before show a new one in the new root.
+ if (DEBUG) Slog.d(TAG, "Immersive mode confirmation was shown in a different root");
+ handleHide();
+ }
+ }
+
if (DEBUG) Slog.d(TAG, "Showing immersive mode confirmation");
-
mClingWindow = new ClingWindowView(mContext, mConfirm);
-
// show the confirmation
- WindowManager.LayoutParams lp = getClingWindowLayoutParams();
+ final WindowManager.LayoutParams lp = getClingWindowLayoutParams();
try {
- getWindowManager(rootDisplayAreaId).addView(mClingWindow, lp);
+ createWindowManager(rootDisplayAreaId).addView(mClingWindow, lp);
} catch (WindowManager.InvalidDisplayException e) {
Slog.w(TAG, "Fail to show the immersive confirmation window because of " + e);
}
diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING
index be7feb5..648b904 100644
--- a/services/incremental/TEST_MAPPING
+++ b/services/incremental/TEST_MAPPING
@@ -9,6 +9,9 @@
]
},
{
+ "name": "CtsPackageManagerIncrementalStatsHostTestCases"
+ },
+ {
"name": "CtsIncrementalInstallHostTestCases"
},
{
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 32d0c98..989aee0 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -33,6 +33,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@@ -42,6 +43,8 @@
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
import android.os.Handler;
import android.os.Message;
import android.os.UserHandle;
@@ -67,11 +70,13 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.List;
@@ -123,9 +128,10 @@
public static final int STATE_SHORTCUT_TRIGGERED_ZOOMED_TMP = 8;
public static final int STATE_PANNING = 9;
public static final int STATE_SCALING_AND_PANNING = 10;
+ public static final int STATE_SINGLE_PANNING = 11;
public static final int FIRST_STATE = STATE_IDLE;
- public static final int LAST_STATE = STATE_SCALING_AND_PANNING;
+ public static final int LAST_STATE = STATE_SINGLE_PANNING;
// Co-prime x and y, to potentially catch x-y-swapped errors
public static final float DEFAULT_X = 301;
@@ -155,6 +161,10 @@
private float mOriginalMagnificationPersistedScale;
+ static final Rect INITIAL_MAGNIFICATION_BOUNDS = new Rect(0, 0, 800, 800);
+
+ static final Region INITIAL_MAGNIFICATION_REGION = new Region(INITIAL_MAGNIFICATION_BOUNDS);
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -182,11 +192,19 @@
new MagnificationScaleProvider(mContext),
() -> null,
ConcurrentUtils.DIRECT_EXECUTOR) {
- @Override
- public boolean magnificationRegionContains(int displayId, float x, float y) {
- return true;
- }
+ @Override
+ public boolean magnificationRegionContains(int displayId, float x, float y) {
+ return true;
+ }
};
+
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ Object[] args = invocationOnMock.getArguments();
+ Region regionArg = (Region) args[1];
+ regionArg.set(new Rect(INITIAL_MAGNIFICATION_BOUNDS));
+ return null;
+ }).when(mockWindowManager).getMagnificationRegion(anyInt(), any(Region.class));
+
mFullScreenMagnificationController.register(DISPLAY_0);
mFullScreenMagnificationController.setAlwaysOnMagnificationEnabled(true);
mClock = new OffsettableClock.Stopped();
@@ -214,6 +232,7 @@
mContext, mFullScreenMagnificationController, mMockTraceManager, mMockCallback,
detectTripleTap, detectShortcutTrigger,
mWindowMagnificationPromptController, DISPLAY_0);
+ h.setSinglePanningEnabled(true);
mHandler = new TestHandler(h.mDetectingState, mClock) {
@Override
protected String messageToString(Message m) {
@@ -239,6 +258,7 @@
* {@link #returnToNormalFrom} (for navigating back to {@link #STATE_IDLE})
*/
@Test
+ @Ignore("b/291925580")
public void testEachState_isReachableAndRecoverable() {
forEachState(state -> {
goFromStateIdleTo(state);
@@ -526,6 +546,75 @@
}
@Test
+ public void testActionUpNotAtEdge_singlePanningState_detectingState() {
+ goFromStateIdleTo(STATE_SINGLE_PANNING);
+
+ send(upEvent());
+
+ check(mMgh.mCurrentState == mMgh.mDetectingState, STATE_IDLE);
+ assertTrue(isZoomed());
+ }
+
+ @Test
+ public void testScroll_SinglePanningDisabled_delegatingState() {
+ mMgh.setSinglePanningEnabled(false);
+
+ goFromStateIdleTo(STATE_ACTIVATED);
+ allowEventDelegation();
+ swipeAndHold();
+
+ assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+ }
+
+ @Test
+ public void testScroll_zoomedStateAndAtEdge_delegatingState() {
+ goFromStateIdleTo(STATE_ACTIVATED);
+ mFullScreenMagnificationController.setCenter(
+ DISPLAY_0,
+ INITIAL_MAGNIFICATION_BOUNDS.left,
+ INITIAL_MAGNIFICATION_BOUNDS.top / 2,
+ false,
+ 1);
+ final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
+ PointF initCoords =
+ new PointF(
+ mFullScreenMagnificationController.getCenterX(DISPLAY_0),
+ mFullScreenMagnificationController.getCenterY(DISPLAY_0));
+ PointF endCoords = new PointF(initCoords.x, initCoords.y);
+ endCoords.offset(swipeMinDistance, 0);
+ allowEventDelegation();
+
+ swipeAndHold(initCoords, endCoords);
+
+ assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+ assertTrue(isZoomed());
+ }
+
+ @Test
+ public void testScroll_singlePanningAndAtEdge_delegatingState() {
+ goFromStateIdleTo(STATE_SINGLE_PANNING);
+ mFullScreenMagnificationController.setCenter(
+ DISPLAY_0,
+ INITIAL_MAGNIFICATION_BOUNDS.left,
+ INITIAL_MAGNIFICATION_BOUNDS.top / 2,
+ false,
+ 1);
+ final float swipeMinDistance = ViewConfiguration.get(mContext).getScaledTouchSlop() + 1;
+ PointF initCoords =
+ new PointF(
+ mFullScreenMagnificationController.getCenterX(DISPLAY_0),
+ mFullScreenMagnificationController.getCenterY(DISPLAY_0));
+ PointF endCoords = new PointF(initCoords.x, initCoords.y);
+ endCoords.offset(swipeMinDistance, 0);
+ allowEventDelegation();
+
+ swipeAndHold(initCoords, endCoords);
+
+ assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+ assertTrue(isZoomed());
+ }
+
+ @Test
public void testShortcutTriggered_invokeShowWindowPromptAction() {
goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
@@ -740,6 +829,10 @@
state);
check(mMgh.mPanningScalingState.mScaling, state);
} break;
+ case STATE_SINGLE_PANNING: {
+ check(isZoomed(), state);
+ check(mMgh.mCurrentState == mMgh.mSinglePanningState, state);
+ } break;
default: throw new IllegalArgumentException("Illegal state: " + state);
}
}
@@ -803,6 +896,10 @@
send(pointerEvent(ACTION_MOVE, DEFAULT_X * 2, DEFAULT_Y * 4));
send(pointerEvent(ACTION_MOVE, DEFAULT_X * 2, DEFAULT_Y * 5));
} break;
+ case STATE_SINGLE_PANNING: {
+ goFromStateIdleTo(STATE_ACTIVATED);
+ swipeAndHold();
+ } break;
default:
throw new IllegalArgumentException("Illegal state: " + state);
}
@@ -859,6 +956,10 @@
case STATE_SCALING_AND_PANNING: {
returnToNormalFrom(STATE_PANNING);
} break;
+ case STATE_SINGLE_PANNING: {
+ send(upEvent());
+ returnToNormalFrom(STATE_ACTIVATED);
+ } break;
default: throw new IllegalArgumentException("Illegal state: " + state);
}
}
@@ -906,6 +1007,11 @@
send(moveEvent(DEFAULT_X * 2, DEFAULT_Y * 2));
}
+ private void swipeAndHold(PointF start, PointF end) {
+ send(downEvent(start.x, start.y));
+ send(moveEvent(end.x, end.y));
+ }
+
private void longTap() {
send(downEvent());
fastForward(2000);
diff --git a/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING
new file mode 100644
index 0000000..0ffa891
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/contentcapture/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.contentcapture"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING
new file mode 100644
index 0000000..419508c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/contentprotection/TEST_MAPPING
@@ -0,0 +1,18 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.contentprotection"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ]
+ }
+ ]
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index a109d5c..f552ab2 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -406,7 +406,6 @@
UriGrantsManagerInternal mUgmInternal;
@Mock
AppOpsManager mAppOpsManager;
- private AppOpsManager.OnOpChangedListener mOnPermissionChangeListener;
@Mock
private TestableNotificationManagerService.NotificationAssistantAccessGrantedCallback
mNotificationAssistantAccessGrantedCallback;
@@ -606,12 +605,6 @@
tr.addOverride(com.android.internal.R.string.config_defaultSearchSelectorPackageName,
SEARCH_SELECTOR_PKG);
- doAnswer(invocation -> {
- mOnPermissionChangeListener = invocation.getArgument(2);
- return null;
- }).when(mAppOpsManager).startWatchingMode(eq(AppOpsManager.OP_POST_NOTIFICATION), any(),
- any());
-
mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper()));
mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient,
mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr,
@@ -3224,6 +3217,48 @@
}
@Test
+ public void testUpdateAppNotifyCreatorBlock() throws Exception {
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(true);
+
+ mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
+ Thread.sleep(500);
+ waitForIdle();
+
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
+
+ assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
+ captor.getValue().getAction());
+ assertEquals(PKG, captor.getValue().getPackage());
+ assertTrue(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
+ }
+
+ @Test
+ public void testUpdateAppNotifyCreatorBlock_notIfMatchesExistingSetting() throws Exception {
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+
+ mBinderService.setNotificationsEnabledForPackage(PKG, 0, false);
+ verify(mContext, never()).sendBroadcastAsUser(any(), any(), eq(null));
+ }
+
+ @Test
+ public void testUpdateAppNotifyCreatorUnblock() throws Exception {
+ when(mPermissionHelper.hasPermission(mUid)).thenReturn(false);
+
+ mBinderService.setNotificationsEnabledForPackage(PKG, mUid, true);
+ Thread.sleep(500);
+ waitForIdle();
+
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null));
+
+ assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED,
+ captor.getValue().getAction());
+ assertEquals(PKG, captor.getValue().getPackage());
+ assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true));
+ }
+
+ @Test
public void testUpdateChannelNotifyCreatorBlock() throws Exception {
mService.setPreferencesHelper(mPreferencesHelper);
when(mPreferencesHelper.getNotificationChannel(eq(PKG), anyInt(),
@@ -12139,134 +12174,6 @@
any(), eq(FLAG_ACTIVITY_SENDER | FLAG_BROADCAST_SENDER | FLAG_SERVICE_SENDER));
}
- @Test
- public void onOpChanged_permissionRevoked_cancelsAllNotificationsFromPackage()
- throws RemoteException {
- // Have preexisting posted notifications from revoked package and other packages.
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("revoked", 1001, 1, 0), mTestNotificationChannel));
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 2, 0), mTestNotificationChannel));
- // Have preexisting enqueued notifications from revoked package and other packages.
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("revoked", 1001, 3, 0), mTestNotificationChannel));
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 4, 0), mTestNotificationChannel));
- assertThat(mService.mNotificationList).hasSize(2);
- assertThat(mService.mEnqueuedNotifications).hasSize(2);
-
- when(mPackageManagerInternal.getPackageUid("revoked", 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(false);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, "revoked", 0);
- waitForIdle();
-
- assertThat(mService.mNotificationList).hasSize(1);
- assertThat(mService.mNotificationList.get(0).getSbn().getPackageName()).isEqualTo("other");
- assertThat(mService.mEnqueuedNotifications).hasSize(1);
- assertThat(mService.mEnqueuedNotifications.get(0).getSbn().getPackageName()).isEqualTo(
- "other");
- }
-
- @Test
- public void onOpChanged_permissionStillGranted_notificationsAreNotAffected()
- throws RemoteException {
- // NOTE: This combination (receiving the onOpChanged broadcast for a package, the permission
- // being now granted, AND having previously posted notifications from said package) should
- // never happen (if we trust the broadcasts are correct). So this test is for a what-if
- // scenario, to verify we still handle it reasonably.
-
- // Have preexisting posted notifications from specific package and other packages.
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("granted", 1001, 1, 0), mTestNotificationChannel));
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 2, 0), mTestNotificationChannel));
- // Have preexisting enqueued notifications from specific package and other packages.
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("granted", 1001, 3, 0), mTestNotificationChannel));
- mService.addEnqueuedNotification(new NotificationRecord(mContext,
- generateSbn("other", 1002, 4, 0), mTestNotificationChannel));
- assertThat(mService.mNotificationList).hasSize(2);
- assertThat(mService.mEnqueuedNotifications).hasSize(2);
-
- when(mPackageManagerInternal.getPackageUid("granted", 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(true);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, "granted", 0);
- waitForIdle();
-
- assertThat(mService.mNotificationList).hasSize(2);
- assertThat(mService.mEnqueuedNotifications).hasSize(2);
- }
-
- @Test
- public void onOpChanged_permissionGranted_notifiesAppUnblocked() throws Exception {
- when(mPackageManagerInternal.getPackageUid(PKG, 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(true);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0);
- waitForIdle();
- mTestableLooper.moveTimeForward(500);
- waitForIdle();
-
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext).sendBroadcastAsUser(captor.capture(), any(), eq(null));
- assertThat(captor.getValue().getAction()).isEqualTo(
- NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED);
- assertThat(captor.getValue().getPackage()).isEqualTo(PKG);
- assertThat(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true)).isFalse();
- }
-
- @Test
- public void onOpChanged_permissionRevoked_notifiesAppBlocked() throws Exception {
- when(mPackageManagerInternal.getPackageUid(PKG, 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasPermission(eq(1001))).thenReturn(false);
-
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0);
- waitForIdle();
- mTestableLooper.moveTimeForward(500);
- waitForIdle();
-
- ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext).sendBroadcastAsUser(captor.capture(), any(), eq(null));
- assertThat(captor.getValue().getAction()).isEqualTo(
- NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED);
- assertThat(captor.getValue().getPackage()).isEqualTo(PKG);
- assertThat(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, false)).isTrue();
- }
-
- @Test
- public void setNotificationsEnabledForPackage_disabling_clearsNotifications() throws Exception {
- mService.addNotification(new NotificationRecord(mContext,
- generateSbn("package", 1001, 1, 0), mTestNotificationChannel));
- assertThat(mService.mNotificationList).hasSize(1);
- when(mPackageManagerInternal.getPackageUid("package", 0, 0)).thenReturn(1001);
- when(mPermissionHelper.hasRequestedPermission(any(), eq("package"), anyInt())).thenReturn(
- true);
-
- // Start with granted permission and simulate effect of revoking it.
- when(mPermissionHelper.hasPermission(1001)).thenReturn(true);
- doAnswer(invocation -> {
- when(mPermissionHelper.hasPermission(1001)).thenReturn(false);
- mOnPermissionChangeListener.onOpChanged(
- AppOpsManager.OPSTR_POST_NOTIFICATION, "package", 0);
- return null;
- }).when(mPermissionHelper).setNotificationPermission("package", 0, false, true);
-
- mBinderService.setNotificationsEnabledForPackage("package", 1001, false);
- waitForIdle();
-
- assertThat(mService.mNotificationList).hasSize(0);
-
- mTestableLooper.moveTimeForward(500);
- waitForIdle();
- verify(mContext).sendBroadcastAsUser(any(), eq(UserHandle.of(0)), eq(null));
- }
-
private static <T extends Parcelable> T parcelAndUnparcel(T source,
Parcelable.Creator<T> creator) {
Parcel parcel = Parcel.obtain();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index dedb8f1..3ee75de 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -771,7 +771,7 @@
mZenModeHelper.mConfig = null; // will evaluate config to zen mode off
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -798,7 +798,7 @@
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -825,7 +825,7 @@
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
for (int i = 0; i < 3; i++) {
// if zen doesn't change, zen should not reapply itself to the ringer
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
}
verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL,
mZenModeHelper.TAG);
@@ -2269,7 +2269,7 @@
// Artificially turn zen mode "on". Re-evaluating zen mode should cause it to turn back off
// given that we don't have any zen rules active.
mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS;
- mZenModeHelper.evaluateZenMode("test", true);
+ mZenModeHelper.evaluateZenModeLocked("test", true);
// Check that the change actually took: zen mode should be off now
assertEquals(Global.ZEN_MODE_OFF, mZenModeHelper.mZenMode);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
index b181213..fb95748 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
@@ -91,7 +91,7 @@
}
@Test
- public void testScreenshotSecureLayers() {
+ public void testScreenshotSecureLayers() throws InterruptedException {
SurfaceControl secureSC = new SurfaceControl.Builder()
.setName("SecureChildSurfaceControl")
.setBLASTLayer()
@@ -197,6 +197,8 @@
private static final long WAIT_TIMEOUT_S = 5;
private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private final CountDownLatch mAttachedLatch = new CountDownLatch(1);
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -204,7 +206,16 @@
PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL));
}
- SurfaceControl.Transaction addChildSc(SurfaceControl surfaceControl) {
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mAttachedLatch.countDown();
+ }
+
+ SurfaceControl.Transaction addChildSc(SurfaceControl surfaceControl)
+ throws InterruptedException {
+ assertTrue("Failed to wait for onAttachedToWindow",
+ mAttachedLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
CountDownLatch countDownLatch = new CountDownLatch(1);
mHandler.post(() -> {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
index 0f406fd..845e649 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
@@ -70,6 +70,9 @@
@Ignore("Not applicable to this CUJ.") override fun navBarWindowIsVisibleAtStartAndEnd() {}
+ @FlakyTest(bugId = 291575593)
+ override fun entireScreenCovered() {}
+
@Ignore("Not applicable to this CUJ.") override fun statusBarWindowIsAlwaysVisible() {}
@Ignore("Not applicable to this CUJ.") override fun statusBarLayerPositionAtStartAndEnd() {}