Merge "Add the LoudnessCodecApi tests to presubmit" into main
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
index e5a06c9..3c361d7 100644
--- a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java
@@ -16,12 +16,14 @@
package android.graphics.perftests;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Color;
+import android.graphics.ColorSpace;
import android.graphics.ImageDecoder;
import android.graphics.Paint;
import android.graphics.RecordingCanvas;
@@ -104,15 +106,36 @@
}
@Test
- public void testCreateScaledBitmap() throws IOException {
+ public void testCreateScaledSrgbBitmap() throws IOException {
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
final Context context = InstrumentationRegistry.getContext();
Bitmap source = ImageDecoder.decodeBitmap(
ImageDecoder.createSource(context.getResources(), R.drawable.fountain_night),
(decoder, info, source1) -> {
decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ decoder.setTargetColorSpace(ColorSpace.get(ColorSpace.Named.SRGB));
});
source.setGainmap(null);
+ assertEquals(source.getColorSpace().getId(), ColorSpace.Named.SRGB.ordinal());
+
+ while (state.keepRunning()) {
+ Bitmap.createScaledBitmap(source, source.getWidth() / 2, source.getHeight() / 2, true)
+ .recycle();
+ }
+ }
+
+ @Test
+ public void testCreateScaledP3Bitmap() throws IOException {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ final Context context = InstrumentationRegistry.getContext();
+ Bitmap source = ImageDecoder.decodeBitmap(
+ ImageDecoder.createSource(context.getResources(), R.drawable.fountain_night),
+ (decoder, info, source1) -> {
+ decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
+ decoder.setTargetColorSpace(ColorSpace.get(ColorSpace.Named.DISPLAY_P3));
+ });
+ source.setGainmap(null);
+ assertEquals(source.getColorSpace().getId(), ColorSpace.Named.DISPLAY_P3.ordinal());
while (state.keepRunning()) {
Bitmap.createScaledBitmap(source, source.getWidth() / 2, source.getHeight() / 2, true)
diff --git a/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
index 4352c8a..ea10690 100644
--- a/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
+++ b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
@@ -24,11 +24,11 @@
import android.content.res.TypedArray
import android.perftests.utils.BenchmarkState
import android.perftests.utils.PerfStatusReporter
-import android.util.ArraySet
import androidx.test.filters.LargeTest
import com.android.internal.pm.parsing.pkg.PackageImpl
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils
import com.android.internal.util.ConcurrentUtils
+import com.android.server.SystemConfig
import java.io.File
import java.io.FileOutputStream
import java.util.concurrent.ArrayBlockingQueue
@@ -217,8 +217,10 @@
isCoreApp,
this,
)
- override fun getHiddenApiWhitelistedApps() = ArraySet<String>()
- override fun getInstallConstraintsAllowlist() = ArraySet<String>()
+ override fun getHiddenApiWhitelistedApps() =
+ SystemConfig.getInstance().hiddenApiWhitelistedApps
+ override fun getInstallConstraintsAllowlist() =
+ SystemConfig.getInstance().installConstraintsAllowlist
})
override fun parseImpl(file: File) =
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 385fd50..14195c4 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -241,6 +241,23 @@
public static final int CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE = 3;
/**
+ * Broadcast action: sent to apps that hold the status bar permission when
+ * KeyguardManager#setPrivateNotificationsAllowed() is changed.
+ *
+ * Extras: #EXTRA_KM_PRIVATE_NOTIFS_ALLOWED
+ * @hide
+ */
+ public static final String ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED
+ = "android.app.action.KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED";
+
+ /**
+ * Boolean, the latest value of KeyguardManager#getPrivateNotificationsAllowed()
+ * @hide
+ */
+ public static final String EXTRA_KM_PRIVATE_NOTIFS_ALLOWED
+ = "android.app.extra.KM_PRIVATE_NOTIFS_ALLOWED";
+
+ /**
* Session flag for {@link #registerSessionListener} indicating the listener
* is interested in sessions on the keygaurd.
* Keyguard Session Boundaries:
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index fb0edb9..d11c6c5 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -36,3 +36,10 @@
description: "Guards the security fix that ensures all URIs in intents and Person.java are valid"
bug: "281044385"
}
+
+flag {
+ name: "keyguard_private_notifications"
+ namespace: "systemui"
+ description: "Fixes the behavior of KeyguardManager#setPrivateNotificationsAllowed()"
+ bug: "309920145"
+}
diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
index 3ce094e..cbb0ae7 100644
--- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java
+++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java
@@ -23,6 +23,7 @@
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.ResultInfo;
+import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.os.IBinder;
import android.os.Parcel;
@@ -85,6 +86,12 @@
client.reportRelaunch(r);
}
+ @Nullable
+ @Override
+ public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
+ return client.getActivity(getActivityToken());
+ }
+
// ObjectPoolItem implementation
private ActivityRelaunchItem() {}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index d2ef65a..1190bf6 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -24,12 +24,14 @@
import android.annotation.Nullable;
import android.app.ActivityClient;
import android.app.ActivityOptions;
+import android.app.ActivityThread;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
import android.app.IActivityClientController;
import android.app.ProfilerInfo;
import android.app.ResultInfo;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
@@ -115,6 +117,13 @@
client.countLaunchingActivities(-1);
}
+ @Nullable
+ @Override
+ public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
+ // LaunchActivityItem may update the global config with #mCurConfig.
+ return ActivityThread.currentApplication();
+ }
+
// ObjectPoolItem implementation
private LaunchActivityItem() {}
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index 961da19..1353d16 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.app.ActivityThread.ActivityClientRecord;
import android.app.ClientTransactionHandler;
+import android.content.Context;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.os.IBinder;
@@ -55,6 +56,12 @@
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
+ @Nullable
+ @Override
+ public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
+ return client.getActivity(getActivityToken());
+ }
+
// ObjectPoolItem implementation
private MoveToDisplayItem() {}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index ee48e43..5dd4eb7 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -41,6 +41,7 @@
import android.content.res.Configuration;
import android.os.IBinder;
import android.os.Process;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;
import android.util.Slog;
@@ -62,8 +63,11 @@
private final PendingTransactionActions mPendingActions = new PendingTransactionActions();
private final TransactionExecutorHelper mHelper = new TransactionExecutorHelper();
- /** Keeps track of display ids whose Configuration got updated within a transaction. */
- private final ArraySet<Integer> mConfigUpdatedDisplayIds = new ArraySet<>();
+ /**
+ * Keeps track of the Context whose Configuration got updated within a transaction, mapping to
+ * the config before the transaction.
+ */
+ private final ArrayMap<Context, Configuration> mContextToPreChangedConfigMap = new ArrayMap<>();
/** Initialize an instance with transaction handler, that will execute all requested actions. */
public TransactionExecutor(@NonNull ClientTransactionHandler clientTransactionHandler) {
@@ -91,16 +95,35 @@
executeLifecycleState(transaction);
}
- if (!mConfigUpdatedDisplayIds.isEmpty()) {
+ if (!mContextToPreChangedConfigMap.isEmpty()) {
// Whether this transaction should trigger DisplayListener#onDisplayChanged.
- final ClientTransactionListenerController controller =
- ClientTransactionListenerController.getInstance();
- final int displayCount = mConfigUpdatedDisplayIds.size();
- for (int i = 0; i < displayCount; i++) {
- final int displayId = mConfigUpdatedDisplayIds.valueAt(i);
- controller.onDisplayChanged(displayId);
+ try {
+ // Calculate display ids that have config changed.
+ final ArraySet<Integer> configUpdatedDisplayIds = new ArraySet<>();
+ final int contextCount = mContextToPreChangedConfigMap.size();
+ for (int i = 0; i < contextCount; i++) {
+ final Context context = mContextToPreChangedConfigMap.keyAt(i);
+ final Configuration preTransactionConfig =
+ mContextToPreChangedConfigMap.valueAt(i);
+ final Configuration postTransactionConfig = context.getResources()
+ .getConfiguration();
+ if (!areConfigurationsEqualForDisplay(
+ postTransactionConfig, preTransactionConfig)) {
+ configUpdatedDisplayIds.add(context.getDisplayId());
+ }
+ }
+
+ // Dispatch the display changed callbacks.
+ final ClientTransactionListenerController controller =
+ ClientTransactionListenerController.getInstance();
+ final int displayCount = configUpdatedDisplayIds.size();
+ for (int i = 0; i < displayCount; i++) {
+ final int displayId = configUpdatedDisplayIds.valueAt(i);
+ controller.onDisplayChanged(displayId);
+ }
+ } finally {
+ mContextToPreChangedConfigMap.clear();
}
- mConfigUpdatedDisplayIds.clear();
}
mPendingActions.clear();
@@ -182,26 +205,24 @@
}
}
- // Can't read flag from isolated process.
- final boolean isBundleClientTransactionFlagEnabled = !Process.isIsolated()
- && bundleClientTransactionFlag();
- final Context configUpdatedContext = isBundleClientTransactionFlagEnabled
+ final boolean shouldTrackConfigUpdatedContext =
+ // No configuration change for local transaction.
+ !mTransactionHandler.isExecutingLocalTransaction()
+ // Can't read flag from isolated process.
+ && !Process.isIsolated()
+ && bundleClientTransactionFlag();
+ final Context configUpdatedContext = shouldTrackConfigUpdatedContext
? item.getContextToUpdate(mTransactionHandler)
: null;
- final Configuration preExecutedConfig = configUpdatedContext != null
- ? new Configuration(configUpdatedContext.getResources().getConfiguration())
- : null;
+ if (configUpdatedContext != null
+ && !mContextToPreChangedConfigMap.containsKey(configUpdatedContext)) {
+ // Keep track of the first pre-executed config of each changed Context.
+ mContextToPreChangedConfigMap.put(configUpdatedContext,
+ new Configuration(configUpdatedContext.getResources().getConfiguration()));
+ }
item.execute(mTransactionHandler, mPendingActions);
- if (configUpdatedContext != null) {
- final Configuration postExecutedConfig = configUpdatedContext.getResources()
- .getConfiguration();
- if (!areConfigurationsEqualForDisplay(postExecutedConfig, preExecutedConfig)) {
- mConfigUpdatedDisplayIds.add(configUpdatedContext.getDisplayId());
- }
- }
-
item.postExecute(mTransactionHandler, mPendingActions);
if (r == null) {
// Launch activity request will create an activity record.
diff --git a/core/java/android/app/servertransaction/WindowStateResizeItem.java b/core/java/android/app/servertransaction/WindowStateResizeItem.java
index 7d3eb87..193b03c 100644
--- a/core/java/android/app/servertransaction/WindowStateResizeItem.java
+++ b/core/java/android/app/servertransaction/WindowStateResizeItem.java
@@ -22,9 +22,12 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityThread;
import android.app.ClientTransactionHandler;
+import android.content.Context;
import android.os.Parcel;
import android.os.RemoteException;
+import android.os.Trace;
import android.util.MergedConfiguration;
import android.view.IWindow;
import android.view.InsetsState;
@@ -52,6 +55,11 @@
@Override
public void execute(@NonNull ClientTransactionHandler client,
@NonNull PendingTransactionActions pendingActions) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
+ mReportDraw ? "windowResizedReport" : "windowResized");
+ if (mWindow instanceof ResizeListener listener) {
+ listener.onExecutingWindowStateResizeItem();
+ }
try {
mWindow.resized(mFrames, mReportDraw, mConfiguration, mInsetsState, mForceLayout,
mAlwaysConsumeSystemBars, mDisplayId, mSyncSeqId, mDragResizing);
@@ -59,6 +67,14 @@
// Should be a local call.
throw new RuntimeException(e);
}
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+ }
+
+ @Nullable
+ @Override
+ public Context getContextToUpdate(@NonNull ClientTransactionHandler client) {
+ // WindowStateResizeItem may update the global config with #mConfiguration.
+ return ActivityThread.currentApplication();
}
// ObjectPoolItem implementation
@@ -80,7 +96,7 @@
instance.mFrames = new ClientWindowFrames(frames);
instance.mReportDraw = reportDraw;
instance.mConfiguration = new MergedConfiguration(configuration);
- instance.mInsetsState = new InsetsState(insetsState);
+ instance.mInsetsState = new InsetsState(insetsState, true /* copySources */);
instance.mForceLayout = forceLayout;
instance.mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
instance.mDisplayId = displayId;
@@ -190,4 +206,10 @@
+ ", configuration=" + mConfiguration
+ "}";
}
+
+ /** The interface for IWindow to perform resize directly if possible. */
+ public interface ResizeListener {
+ /** Notifies that IWindow#resized is going to be called from WindowStateResizeItem. */
+ void onExecutingWindowStateResizeItem();
+ }
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b75c64d..fa76e39 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3547,6 +3547,8 @@
*
* @param receiver The BroadcastReceiver to unregister.
*
+ * @throws IllegalArgumentException if the {@code receiver} was not previously registered or
+ * already unregistered.
* @see #registerReceiver
*/
public abstract void unregisterReceiver(BroadcastReceiver receiver);
diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java
index b69410c..a86396c 100644
--- a/core/java/android/net/LocalSocket.java
+++ b/core/java/android/net/LocalSocket.java
@@ -196,7 +196,8 @@
}
/**
- * Retrieves the input stream for this instance.
+ * Retrieves the input stream for this instance. Closing this stream is equivalent to closing
+ * the entire socket and its associated streams using {@link #close()}.
*
* @return input stream
* @throws IOException if socket has been closed or cannot be created.
@@ -207,7 +208,8 @@
}
/**
- * Retrieves the output stream for this instance.
+ * Retrieves the output stream for this instance. Closing this stream is equivalent to closing
+ * the entire socket and its associated streams using {@link #close()}.
*
* @return output stream
* @throws IOException if socket has been closed or cannot be created.
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index 78248d9..e1965ef 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -53,7 +53,6 @@
import android.view.contentcapture.DataRemovalRequest;
import android.view.contentcapture.DataShareRequest;
import android.view.contentcapture.IContentCaptureDirectManager;
-import android.view.contentcapture.MainContentCaptureSession;
import com.android.internal.os.IResultReceiver;
import com.android.internal.util.FrameworkStatsLog;
@@ -724,7 +723,7 @@
final Bundle extras;
if (binder != null) {
extras = new Bundle();
- extras.putBinder(MainContentCaptureSession.EXTRA_BINDER, binder);
+ extras.putBinder(ContentCaptureSession.EXTRA_BINDER, binder);
} else {
extras = null;
}
diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java
index f7b9aa2..079991a 100644
--- a/core/java/android/view/InsetsAnimationThreadControlRunner.java
+++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java
@@ -76,7 +76,7 @@
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW,
"InsetsAsyncAnimation: " + WindowInsets.Type.toString(runner.getTypes()),
runner.getTypes());
- releaseControls(mControl.getControls());
+ InsetsController.releaseControls(mControl.getControls());
mMainThreadHandler.post(() ->
mOuterCallbacks.notifyFinished(InsetsAnimationThreadControlRunner.this, shown));
}
@@ -130,12 +130,6 @@
});
}
- private void releaseControls(SparseArray<InsetsSourceControl> controls) {
- for (int i = controls.size() - 1; i >= 0; i--) {
- controls.valueAt(i).release(SurfaceControl::release);
- }
- }
-
@Override
@UiThread
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 147c15b..dd09157 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1353,6 +1353,9 @@
});
}
+ // The leashes are copied, but they won't be used.
+ releaseControls(controls);
+
// The requested visibilities should be delayed as well. Otherwise, we might override
// the insets visibility before playing animation.
setRequestedVisibleTypes(mReportedRequestedVisibleTypes, types);
@@ -1422,6 +1425,12 @@
}
}
+ static void releaseControls(SparseArray<InsetsSourceControl> controls) {
+ for (int i = controls.size() - 1; i >= 0; i--) {
+ controls.valueAt(i).release(SurfaceControl::release);
+ }
+ }
+
// TODO(b/242962223): Make this setter restrictive.
@Override
public void setSystemDrivenInsetsAnimationLoggingListener(
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ec99459..442ea66 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10760,11 +10760,11 @@
return;
}
- session.internalNotifyViewTreeEvent(/* started= */ true);
+ session.notifyViewTreeEvent(/* started= */ true);
try {
dispatchProvideContentCaptureStructure();
} finally {
- session.internalNotifyViewTreeEvent(/* started= */ false);
+ session.notifyViewTreeEvent(/* started= */ false);
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1f81a64..32afe06 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -111,6 +111,7 @@
import android.app.ResourcesManager;
import android.app.WindowConfiguration;
import android.app.compat.CompatChanges;
+import android.app.servertransaction.WindowStateResizeItem;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
import android.content.ClipDescription;
@@ -208,7 +209,6 @@
import android.view.autofill.AutofillManager;
import android.view.contentcapture.ContentCaptureManager;
import android.view.contentcapture.ContentCaptureSession;
-import android.view.contentcapture.MainContentCaptureSession;
import android.view.inputmethod.ImeTracker;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
@@ -2012,26 +2012,24 @@
}
/** Handles messages {@link #MSG_RESIZED} and {@link #MSG_RESIZED_REPORT}. */
- private void handleResized(int msg, SomeArgs args) {
+ private void handleResized(ClientWindowFrames frames, boolean reportDraw,
+ MergedConfiguration mergedConfiguration, InsetsState insetsState, boolean forceLayout,
+ boolean alwaysConsumeSystemBars, int displayId, int syncSeqId, boolean dragResizing) {
if (!mAdded) {
return;
}
- final ClientWindowFrames frames = (ClientWindowFrames) args.arg1;
- final MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg2;
CompatibilityInfo.applyOverrideScaleIfNeeded(mergedConfiguration);
- final boolean forceNextWindowRelayout = args.argi1 != 0;
- final int displayId = args.argi3;
- final boolean dragResizing = args.argi5 != 0;
-
final Rect frame = frames.frame;
final Rect displayFrame = frames.displayFrame;
final Rect attachedFrame = frames.attachedFrame;
if (mTranslator != null) {
+ mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
mTranslator.translateRectInScreenToAppWindow(frame);
mTranslator.translateRectInScreenToAppWindow(displayFrame);
mTranslator.translateRectInScreenToAppWindow(attachedFrame);
}
+ mInsetsController.onStateChanged(insetsState);
final float compatScale = frames.compatScale;
final boolean frameChanged = !mWinFrame.equals(frame);
final boolean configChanged = !mLastReportedMergedConfiguration.equals(mergedConfiguration);
@@ -2040,8 +2038,8 @@
final boolean displayChanged = mDisplay.getDisplayId() != displayId;
final boolean compatScaleChanged = mTmpFrames.compatScale != compatScale;
final boolean dragResizingChanged = mPendingDragResizing != dragResizing;
- if (msg == MSG_RESIZED && !frameChanged && !configChanged && !attachedFrameChanged
- && !displayChanged && !forceNextWindowRelayout
+ if (!reportDraw && !frameChanged && !configChanged && !attachedFrameChanged
+ && !displayChanged && !forceLayout
&& !compatScaleChanged && !dragResizingChanged) {
return;
}
@@ -2073,11 +2071,11 @@
}
}
- mForceNextWindowRelayout |= forceNextWindowRelayout;
- mPendingAlwaysConsumeSystemBars = args.argi2 != 0;
- mSyncSeqId = args.argi4 > mSyncSeqId ? args.argi4 : mSyncSeqId;
+ mForceNextWindowRelayout |= forceLayout;
+ mPendingAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
+ mSyncSeqId = syncSeqId > mSyncSeqId ? syncSeqId : mSyncSeqId;
- if (msg == MSG_RESIZED_REPORT) {
+ if (reportDraw) {
reportNextDraw("resized");
}
@@ -4104,7 +4102,7 @@
final ContentCaptureManager manager = mAttachInfo.mContentCaptureManager;
if (manager != null && mAttachInfo.mContentCaptureEvents != null) {
- final MainContentCaptureSession session = manager.getMainContentCaptureSession();
+ final ContentCaptureSession session = manager.getMainContentCaptureSession();
session.notifyContentCaptureEvents(mAttachInfo.mContentCaptureEvents);
}
mAttachInfo.mContentCaptureEvents = null;
@@ -5021,7 +5019,7 @@
// Initial dispatch of window bounds to content capture
if (mAttachInfo.mContentCaptureManager != null) {
- MainContentCaptureSession session =
+ ContentCaptureSession session =
mAttachInfo.mContentCaptureManager.getMainContentCaptureSession();
session.notifyWindowBoundsChanged(session.getId(),
getConfiguration().windowConfiguration.getBounds());
@@ -6232,8 +6230,17 @@
case MSG_RESIZED:
case MSG_RESIZED_REPORT: {
final SomeArgs args = (SomeArgs) msg.obj;
- mInsetsController.onStateChanged((InsetsState) args.arg3);
- handleResized(msg.what, args);
+ final ClientWindowFrames frames = (ClientWindowFrames) args.arg1;
+ final boolean reportDraw = msg.what == MSG_RESIZED_REPORT;
+ final MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg2;
+ final InsetsState insetsState = (InsetsState) args.arg3;
+ final boolean forceLayout = args.argi1 != 0;
+ final boolean alwaysConsumeSystemBars = args.argi2 != 0;
+ final int displayId = args.argi3;
+ final int syncSeqId = args.argi4;
+ final boolean dragResizing = args.argi5 != 0;
+ handleResized(frames, reportDraw, mergedConfiguration, insetsState, forceLayout,
+ alwaysConsumeSystemBars, displayId, syncSeqId, dragResizing);
args.recycle();
break;
}
@@ -8797,7 +8804,7 @@
mSurfaceControl.setTransformHint(transformHint);
if (mAttachInfo.mContentCaptureManager != null) {
- MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
+ ContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
.getMainContentCaptureSession();
mainSession.notifyWindowBoundsChanged(mainSession.getId(),
getConfiguration().windowConfiguration.getBounds());
@@ -9379,20 +9386,8 @@
boolean alwaysConsumeSystemBars, int displayId, int syncSeqId, boolean dragResizing) {
Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED);
SomeArgs args = SomeArgs.obtain();
- final boolean sameProcessCall = (Binder.getCallingPid() == android.os.Process.myPid());
- if (sameProcessCall) {
- insetsState = new InsetsState(insetsState, true /* copySource */);
- }
- if (mTranslator != null) {
- mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
- }
- if (insetsState.isSourceOrDefaultVisible(ID_IME, Type.ime())) {
- ImeTracing.getInstance().triggerClientDump("ViewRootImpl#dispatchResized",
- getInsetsController().getHost().getInputMethodManager(), null /* icProto */);
- }
- args.arg1 = sameProcessCall ? new ClientWindowFrames(frames) : frames;
- args.arg2 = sameProcessCall && mergedConfiguration != null
- ? new MergedConfiguration(mergedConfiguration) : mergedConfiguration;
+ args.arg1 = frames;
+ args.arg2 = mergedConfiguration;
args.arg3 = insetsState;
args.argi1 = forceLayout ? 1 : 0;
args.argi2 = alwaysConsumeSystemBars ? 1 : 0;
@@ -10815,9 +10810,10 @@
}
}
- static class W extends IWindow.Stub {
+ static class W extends IWindow.Stub implements WindowStateResizeItem.ResizeListener {
private final WeakReference<ViewRootImpl> mViewAncestor;
private final IWindowSession mWindowSession;
+ private boolean mIsFromResizeItem;
W(ViewRootImpl viewAncestor) {
mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
@@ -10825,17 +10821,46 @@
}
@Override
+ public void onExecutingWindowStateResizeItem() {
+ mIsFromResizeItem = true;
+ }
+
+ @Override
public void resized(ClientWindowFrames frames, boolean reportDraw,
MergedConfiguration mergedConfiguration, InsetsState insetsState,
boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int syncSeqId,
boolean dragResizing) {
+ final boolean isFromResizeItem = mIsFromResizeItem;
+ mIsFromResizeItem = false;
// Although this is a AIDL method, it will only be triggered in local process through
// either WindowStateResizeItem or WindowlessWindowManager.
final ViewRootImpl viewAncestor = mViewAncestor.get();
- if (viewAncestor != null) {
- viewAncestor.dispatchResized(frames, reportDraw, mergedConfiguration, insetsState,
- forceLayout, alwaysConsumeSystemBars, displayId, syncSeqId, dragResizing);
+ if (viewAncestor == null) {
+ return;
}
+ if (insetsState.isSourceOrDefaultVisible(ID_IME, Type.ime())) {
+ ImeTracing.getInstance().triggerClientDump("ViewRootImpl.W#resized",
+ viewAncestor.getInsetsController().getHost().getInputMethodManager(),
+ null /* icProto */);
+ }
+ // If the UI thread is the same as the current thread that is dispatching
+ // WindowStateResizeItem, then it can run directly.
+ if (isFromResizeItem && viewAncestor.mHandler.getLooper()
+ == ActivityThread.currentActivityThread().getLooper()) {
+ viewAncestor.handleResized(frames, reportDraw, mergedConfiguration, insetsState,
+ forceLayout, alwaysConsumeSystemBars, displayId, syncSeqId, dragResizing);
+ return;
+ }
+ // The the parameters from WindowStateResizeItem are already copied.
+ final boolean needCopy =
+ !isFromResizeItem && (Binder.getCallingPid() == Process.myPid());
+ if (needCopy) {
+ insetsState = new InsetsState(insetsState, true /* copySource */);
+ frames = new ClientWindowFrames(frames);
+ mergedConfiguration = new MergedConfiguration(mergedConfiguration);
+ }
+ viewAncestor.dispatchResized(frames, reportDraw, mergedConfiguration, insetsState,
+ forceLayout, alwaysConsumeSystemBars, displayId, syncSeqId, dragResizing);
}
@Override
@@ -11910,6 +11935,12 @@
if (syncBuffer) {
boolean result = mBlastBufferQueue.syncNextTransaction(transaction -> {
+ Runnable timeoutRunnable = () -> Log.e(mTag,
+ "Failed to submit the sync transaction after 4s. Likely to ANR "
+ + "soon");
+ mHandler.postDelayed(timeoutRunnable, 4L * Build.HW_TIMEOUT_MULTIPLIER);
+ transaction.addTransactionCommittedListener(mSimpleExecutor,
+ () -> mHandler.removeCallbacks(timeoutRunnable));
surfaceSyncGroup.addTransaction(transaction);
surfaceSyncGroup.markSyncReady();
});
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index 44b4353..9efa144 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -17,10 +17,16 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.SparseArray;
import android.view.autofill.AutofillId;
import android.view.contentcapture.ViewNode.ViewStructureImpl;
+import java.util.ArrayList;
+
/**
* A session that is explicitly created by the app (and hence is a descendant of
* {@link MainContentCaptureSession}).
@@ -40,7 +46,7 @@
}
@Override
- MainContentCaptureSession getMainCaptureSession() {
+ ContentCaptureSession getMainCaptureSession() {
if (mParent instanceof MainContentCaptureSession) {
return (MainContentCaptureSession) mParent;
}
@@ -48,9 +54,25 @@
}
@Override
+ void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
+ @NonNull ComponentName component, int flags) {
+ getMainCaptureSession().start(token, shareableActivityToken, component, flags);
+ }
+
+ @Override
+ boolean isDisabled() {
+ return getMainCaptureSession().isDisabled();
+ }
+
+ @Override
+ boolean setDisabled(boolean disabled) {
+ return getMainCaptureSession().setDisabled(disabled);
+ }
+
+ @Override
ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) {
final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext);
- getMainCaptureSession().notifyChildSessionStarted(mId, child.mId, clientContext);
+ internalNotifyChildSessionStarted(mId, child.mId, clientContext);
return child;
}
@@ -61,51 +83,80 @@
@Override
public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
- getMainCaptureSession().notifyContextUpdated(mId, context);
+ internalNotifyContextUpdated(mId, context);
}
@Override
void onDestroy() {
- getMainCaptureSession().notifyChildSessionFinished(mParent.mId, mId);
+ internalNotifyChildSessionFinished(mParent.mId, mId);
}
@Override
- void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) {
- getMainCaptureSession().notifyViewAppeared(mId, node);
+ void internalNotifyChildSessionStarted(int parentSessionId, int childSessionId,
+ @NonNull ContentCaptureContext clientContext) {
+ getMainCaptureSession()
+ .internalNotifyChildSessionStarted(parentSessionId, childSessionId, clientContext);
}
@Override
- void internalNotifyViewDisappeared(@NonNull AutofillId id) {
- getMainCaptureSession().notifyViewDisappeared(mId, id);
+ void internalNotifyChildSessionFinished(int parentSessionId, int childSessionId) {
+ getMainCaptureSession().internalNotifyChildSessionFinished(parentSessionId, childSessionId);
}
@Override
- void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text) {
- getMainCaptureSession().notifyViewTextChanged(mId, id, text);
+ void internalNotifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
+ getMainCaptureSession().internalNotifyContextUpdated(sessionId, context);
}
@Override
- void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets) {
- getMainCaptureSession().notifyViewInsetsChanged(mId, viewInsets);
+ void internalNotifyViewAppeared(int sessionId, @NonNull ViewStructureImpl node) {
+ getMainCaptureSession().internalNotifyViewAppeared(sessionId, node);
}
@Override
- public void internalNotifyViewTreeEvent(boolean started) {
- getMainCaptureSession().notifyViewTreeEvent(mId, started);
+ void internalNotifyViewDisappeared(int sessionId, @NonNull AutofillId id) {
+ getMainCaptureSession().internalNotifyViewDisappeared(sessionId, id);
+ }
+
+ @Override
+ void internalNotifyViewTextChanged(
+ int sessionId, @NonNull AutofillId id, @Nullable CharSequence text) {
+ getMainCaptureSession().internalNotifyViewTextChanged(sessionId, id, text);
+ }
+
+ @Override
+ void internalNotifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
+ getMainCaptureSession().internalNotifyViewInsetsChanged(mId, viewInsets);
+ }
+
+ @Override
+ public void internalNotifyViewTreeEvent(int sessionId, boolean started) {
+ getMainCaptureSession().internalNotifyViewTreeEvent(sessionId, started);
}
@Override
void internalNotifySessionResumed() {
- getMainCaptureSession().notifySessionResumed();
+ getMainCaptureSession().internalNotifySessionResumed();
}
@Override
void internalNotifySessionPaused() {
- getMainCaptureSession().notifySessionPaused();
+ getMainCaptureSession().internalNotifySessionPaused();
}
@Override
boolean isContentCaptureEnabled() {
return getMainCaptureSession().isContentCaptureEnabled();
}
+
+ @Override
+ public void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds) {
+ getMainCaptureSession().notifyWindowBoundsChanged(sessionId, bounds);
+ }
+
+ @Override
+ public void notifyContentCaptureEvents(
+ @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
+ getMainCaptureSession().notifyContentCaptureEvents(contentCaptureEvents);
+ }
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index a829747..daf07a3 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -502,7 +502,7 @@
private Handler mHandler;
@GuardedBy("mLock")
- private MainContentCaptureSession mMainSession;
+ private ContentCaptureSession mMainSession;
@Nullable // set on-demand by addDumpable()
private Dumper mDumpable;
@@ -587,7 +587,7 @@
*/
@NonNull
@UiThread
- public MainContentCaptureSession getMainContentCaptureSession() {
+ public ContentCaptureSession getMainContentCaptureSession() {
synchronized (mLock) {
if (mMainSession == null) {
mMainSession = new MainContentCaptureSession(
@@ -726,7 +726,7 @@
public boolean isContentCaptureEnabled() {
if (mOptions.lite) return false;
- final MainContentCaptureSession mainSession;
+ final ContentCaptureSession mainSession;
synchronized (mLock) {
mainSession = mMainSession;
}
@@ -777,7 +777,7 @@
Log.d(TAG, "setContentCaptureEnabled(): setting to " + enabled + " for " + mContext);
}
- MainContentCaptureSession mainSession;
+ ContentCaptureSession mainSession;
synchronized (mLock) {
if (enabled) {
mFlags &= ~ContentCaptureContext.FLAG_DISABLED_BY_APP;
@@ -803,7 +803,7 @@
final boolean flagSecureEnabled =
(params.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0;
- MainContentCaptureSession mainSession;
+ ContentCaptureSession mainSession;
boolean alreadyDisabledByApp;
synchronized (mLock) {
alreadyDisabledByApp = (mFlags & ContentCaptureContext.FLAG_DISABLED_BY_APP) != 0;
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index bb815c0..0ca36ba2 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -27,9 +27,13 @@
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
+import android.content.ComponentName;
import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.IBinder;
import android.util.DebugUtils;
import android.util.Log;
+import android.util.SparseArray;
import android.view.View;
import android.view.ViewStructure;
import android.view.autofill.AutofillId;
@@ -37,6 +41,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.IResultReceiver;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
@@ -60,6 +65,18 @@
private static final SecureRandom ID_GENERATOR = new SecureRandom();
/**
+ * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
+ * @hide
+ */
+ public static final String EXTRA_BINDER = "binder";
+
+ /**
+ * Name of the {@link IResultReceiver} extra used to pass the content capture enabled state.
+ * @hide
+ */
+ public static final String EXTRA_ENABLED_STATE = "enabled";
+
+ /**
* Initial state, when there is no session.
*
* @hide
@@ -262,7 +279,19 @@
/** @hide */
@NonNull
- abstract MainContentCaptureSession getMainCaptureSession();
+ abstract ContentCaptureSession getMainCaptureSession();
+
+ abstract void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
+ @NonNull ComponentName component, int flags);
+
+ abstract boolean isDisabled();
+
+ /**
+ * Sets the disabled state of content capture.
+ *
+ * @return whether disabled state was changed.
+ */
+ abstract boolean setDisabled(boolean disabled);
/**
* Gets the id used to identify this session.
@@ -400,10 +429,11 @@
throw new IllegalArgumentException("Invalid node class: " + node.getClass());
}
- internalNotifyViewAppeared((ViewStructureImpl) node);
+ internalNotifyViewAppeared(mId, (ViewStructureImpl) node);
}
- abstract void internalNotifyViewAppeared(@NonNull ViewNode.ViewStructureImpl node);
+ abstract void internalNotifyViewAppeared(
+ int sessionId, @NonNull ViewNode.ViewStructureImpl node);
/**
* Notifies the Content Capture Service that a node has been removed from the view structure.
@@ -420,10 +450,10 @@
Objects.requireNonNull(id);
if (!isContentCaptureEnabled()) return;
- internalNotifyViewDisappeared(id);
+ internalNotifyViewDisappeared(mId, id);
}
- abstract void internalNotifyViewDisappeared(@NonNull AutofillId id);
+ abstract void internalNotifyViewDisappeared(int sessionId, @NonNull AutofillId id);
/**
* Notifies the Content Capture Service that a list of nodes has appeared in the view structure.
@@ -445,12 +475,12 @@
}
}
- internalNotifyViewTreeEvent(/* started= */ true);
+ internalNotifyViewTreeEvent(mId, /* started= */ true);
for (int i = 0; i < appearedNodes.size(); i++) {
ViewStructure v = appearedNodes.get(i);
- internalNotifyViewAppeared((ViewStructureImpl) v);
+ internalNotifyViewAppeared(mId, (ViewStructureImpl) v);
}
- internalNotifyViewTreeEvent(/* started= */ false);
+ internalNotifyViewTreeEvent(mId, /* started= */ false);
}
/**
@@ -476,15 +506,15 @@
if (!isContentCaptureEnabled()) return;
if (CompatChanges.isChangeEnabled(NOTIFY_NODES_DISAPPEAR_NOW_SENDS_TREE_EVENTS)) {
- internalNotifyViewTreeEvent(/* started= */ true);
+ internalNotifyViewTreeEvent(mId, /* started= */ true);
}
// TODO(b/123036895): use a internalNotifyViewsDisappeared that optimizes how the event is
// parcelized
for (long id : virtualIds) {
- internalNotifyViewDisappeared(new AutofillId(hostId, id, mId));
+ internalNotifyViewDisappeared(mId, new AutofillId(hostId, id, mId));
}
if (CompatChanges.isChangeEnabled(NOTIFY_NODES_DISAPPEAR_NOW_SENDS_TREE_EVENTS)) {
- internalNotifyViewTreeEvent(/* started= */ false);
+ internalNotifyViewTreeEvent(mId, /* started= */ false);
}
}
@@ -499,10 +529,10 @@
if (!isContentCaptureEnabled()) return;
- internalNotifyViewTextChanged(id, text);
+ internalNotifyViewTextChanged(mId, id, text);
}
- abstract void internalNotifyViewTextChanged(@NonNull AutofillId id,
+ abstract void internalNotifyViewTextChanged(int sessionId, @NonNull AutofillId id,
@Nullable CharSequence text);
/**
@@ -513,13 +543,18 @@
if (!isContentCaptureEnabled()) return;
- internalNotifyViewInsetsChanged(viewInsets);
+ internalNotifyViewInsetsChanged(mId, viewInsets);
}
- abstract void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets);
+ abstract void internalNotifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets);
/** @hide */
- public abstract void internalNotifyViewTreeEvent(boolean started);
+ public void notifyViewTreeEvent(boolean started) {
+ internalNotifyViewTreeEvent(mId, started);
+ }
+
+ /** @hide */
+ abstract void internalNotifyViewTreeEvent(int sessionId, boolean started);
/**
* Notifies the Content Capture Service that a session has resumed.
@@ -543,6 +578,21 @@
abstract void internalNotifySessionPaused();
+ abstract void internalNotifyChildSessionStarted(int parentSessionId, int childSessionId,
+ @NonNull ContentCaptureContext clientContext);
+
+ abstract void internalNotifyChildSessionFinished(int parentSessionId, int childSessionId);
+
+ abstract void internalNotifyContextUpdated(
+ int sessionId, @Nullable ContentCaptureContext context);
+
+ /** @hide */
+ public abstract void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds);
+
+ /** @hide */
+ public abstract void notifyContentCaptureEvents(
+ @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents);
+
/**
* Creates a {@link ViewStructure} for a "standard" view.
*
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 19ba316..9e604d0 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -82,6 +82,7 @@
*
* @hide
*/
+@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public final class MainContentCaptureSession extends ContentCaptureSession {
private static final String TAG = MainContentCaptureSession.class.getSimpleName();
@@ -97,18 +98,6 @@
*/
private static final int MSG_FLUSH = 1;
- /**
- * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service.
- * @hide
- */
- public static final String EXTRA_BINDER = "binder";
-
- /**
- * Name of the {@link IResultReceiver} extra used to pass the content capture enabled state.
- * @hide
- */
- public static final String EXTRA_ENABLED_STATE = "enabled";
-
@NonNull
private final AtomicBoolean mDisabled = new AtomicBoolean(false);
@@ -254,20 +243,21 @@
}
@Override
- MainContentCaptureSession getMainCaptureSession() {
+ ContentCaptureSession getMainCaptureSession() {
return this;
}
@Override
ContentCaptureSession newChild(@NonNull ContentCaptureContext clientContext) {
final ContentCaptureSession child = new ChildContentCaptureSession(this, clientContext);
- notifyChildSessionStarted(mId, child.mId, clientContext);
+ internalNotifyChildSessionStarted(mId, child.mId, clientContext);
return child;
}
/**
* Starts this session.
*/
+ @Override
void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
@NonNull ComponentName component, int flags) {
if (runOnBackgroundThreadEnabled()) {
@@ -617,7 +607,8 @@
+ flushFrequencyMs + "ms: " + TimeUtils.logTimeOfDay(mNextFlush));
}
// Post using a Runnable directly to trim a few μs from PooledLambda.obtainMessage()
- mHandler.postDelayed(() -> flushIfNeeded(reason), MSG_FLUSH, flushFrequencyMs);
+ mHandler.postDelayed(() ->
+ flushIfNeeded(reason), MSG_FLUSH, flushFrequencyMs);
}
private void flushIfNeeded(@FlushReason int reason) {
@@ -703,7 +694,7 @@
@Override
public void updateContentCaptureContext(@Nullable ContentCaptureContext context) {
- notifyContextUpdated(mId, context);
+ internalNotifyContextUpdated(mId, context);
}
/**
@@ -781,91 +772,22 @@
}
@Override
- void internalNotifyViewAppeared(@NonNull ViewStructureImpl node) {
- notifyViewAppeared(mId, node);
- }
-
- @Override
- void internalNotifyViewDisappeared(@NonNull AutofillId id) {
- notifyViewDisappeared(mId, id);
- }
-
- @Override
- void internalNotifyViewTextChanged(@NonNull AutofillId id, @Nullable CharSequence text) {
- notifyViewTextChanged(mId, id, text);
- }
-
- @Override
- void internalNotifyViewInsetsChanged(@NonNull Insets viewInsets) {
- notifyViewInsetsChanged(mId, viewInsets);
- }
-
- @Override
- public void internalNotifyViewTreeEvent(boolean started) {
- notifyViewTreeEvent(mId, started);
- }
-
- @Override
- public void internalNotifySessionResumed() {
- notifySessionResumed(mId);
- }
-
- @Override
- public void internalNotifySessionPaused() {
- notifySessionPaused(mId);
- }
-
- @Override
- boolean isContentCaptureEnabled() {
- return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled();
- }
-
- // Called by ContentCaptureManager.isContentCaptureEnabled
- boolean isDisabled() {
- return mDisabled.get();
- }
-
- /**
- * Sets the disabled state of content capture.
- *
- * @return whether disabled state was changed.
- */
- boolean setDisabled(boolean disabled) {
- return mDisabled.compareAndSet(!disabled, disabled);
- }
-
- // TODO(b/122454205): refactor "notifyXXXX" methods below to a common "Buffer" object that is
- // shared between ActivityContentCaptureSession and ChildContentCaptureSession objects. Such
- // change should also get get rid of the "internalNotifyXXXX" methods above
- void notifyChildSessionStarted(int parentSessionId, int childSessionId,
- @NonNull ContentCaptureContext clientContext) {
- final ContentCaptureEvent event =
- new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
- .setParentSessionId(parentSessionId)
- .setClientContext(clientContext);
- enqueueEvent(event, FORCE_FLUSH);
- }
-
- void notifyChildSessionFinished(int parentSessionId, int childSessionId) {
- final ContentCaptureEvent event =
- new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
- .setParentSessionId(parentSessionId);
- enqueueEvent(event, FORCE_FLUSH);
- }
-
- void notifyViewAppeared(int sessionId, @NonNull ViewStructureImpl node) {
+ void internalNotifyViewAppeared(int sessionId, @NonNull ViewStructureImpl node) {
final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED)
.setViewNode(node.mNode);
enqueueEvent(event);
}
- void notifyViewDisappeared(int sessionId, @NonNull AutofillId id) {
+ @Override
+ void internalNotifyViewDisappeared(int sessionId, @NonNull AutofillId id) {
final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED)
.setAutofillId(id);
enqueueEvent(event);
}
- void notifyViewTextChanged(int sessionId, @NonNull AutofillId id, @Nullable CharSequence text) {
+ @Override
+ void internalNotifyViewTextChanged(
+ int sessionId, @NonNull AutofillId id, @Nullable CharSequence text) {
// Since the same CharSequence instance may be reused in the TextView, we need to make
// a copy of its content so that its value will not be changed by subsequent updates
// in the TextView.
@@ -894,14 +816,16 @@
enqueueEvent(event);
}
- void notifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
+ @Override
+ void internalNotifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
final ContentCaptureEvent event =
new ContentCaptureEvent(sessionId, TYPE_VIEW_INSETS_CHANGED)
.setInsets(viewInsets);
enqueueEvent(event);
}
- void notifyViewTreeEvent(int sessionId, boolean started) {
+ @Override
+ public void internalNotifyViewTreeEvent(int sessionId, boolean started) {
final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED;
final boolean disableFlush = mManager.getFlushViewTreeAppearingEventDisabled();
final boolean forceFlush = disableFlush ? !started : FORCE_FLUSH;
@@ -910,23 +834,59 @@
enqueueEvent(event, forceFlush);
}
- void notifySessionResumed(int sessionId) {
- final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_SESSION_RESUMED);
+ @Override
+ public void internalNotifySessionResumed() {
+ final ContentCaptureEvent event = new ContentCaptureEvent(mId, TYPE_SESSION_RESUMED);
enqueueEvent(event, FORCE_FLUSH);
}
- void notifySessionPaused(int sessionId) {
- final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_SESSION_PAUSED);
+ @Override
+ public void internalNotifySessionPaused() {
+ final ContentCaptureEvent event = new ContentCaptureEvent(mId, TYPE_SESSION_PAUSED);
enqueueEvent(event, FORCE_FLUSH);
}
- void notifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
+ @Override
+ boolean isContentCaptureEnabled() {
+ return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled();
+ }
+
+ @Override
+ boolean isDisabled() {
+ return mDisabled.get();
+ }
+
+ @Override
+ boolean setDisabled(boolean disabled) {
+ return mDisabled.compareAndSet(!disabled, disabled);
+ }
+
+ @Override
+ void internalNotifyChildSessionStarted(int parentSessionId, int childSessionId,
+ @NonNull ContentCaptureContext clientContext) {
+ final ContentCaptureEvent event =
+ new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
+ .setParentSessionId(parentSessionId)
+ .setClientContext(clientContext);
+ enqueueEvent(event, FORCE_FLUSH);
+ }
+
+ @Override
+ void internalNotifyChildSessionFinished(int parentSessionId, int childSessionId) {
+ final ContentCaptureEvent event =
+ new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
+ .setParentSessionId(parentSessionId);
+ enqueueEvent(event, FORCE_FLUSH);
+ }
+
+ @Override
+ void internalNotifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_CONTEXT_UPDATED)
.setClientContext(context);
enqueueEvent(event, FORCE_FLUSH);
}
- /** public because is also used by ViewRootImpl */
+ @Override
public void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds) {
final ContentCaptureEvent event =
new ContentCaptureEvent(sessionId, TYPE_WINDOW_BOUNDS_CHANGED)
@@ -972,11 +932,12 @@
}
}
- /** public because is also used by ViewRootImpl */
+ @Override
public void notifyContentCaptureEvents(
@NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
if (runOnBackgroundThreadEnabled()) {
- runOnContentCaptureThread(() -> notifyContentCaptureEventsImpl(contentCaptureEvents));
+ runOnContentCaptureThread(() ->
+ notifyContentCaptureEventsImpl(contentCaptureEvents));
} else {
// Preserve the control arm behaviour.
notifyContentCaptureEventsImpl(contentCaptureEvents);
@@ -992,12 +953,12 @@
}
for (int i = 0; i < contentCaptureEvents.size(); i++) {
int sessionId = contentCaptureEvents.keyAt(i);
- notifyViewTreeEvent(sessionId, /* started= */ true);
+ internalNotifyViewTreeEvent(sessionId, /* started= */ true);
ArrayList<Object> events = contentCaptureEvents.valueAt(i);
for_each_event: for (int j = 0; j < events.size(); j++) {
Object event = events.get(j);
if (event instanceof AutofillId) {
- notifyViewDisappeared(sessionId, (AutofillId) event);
+ internalNotifyViewDisappeared(sessionId, (AutofillId) event);
} else if (event instanceof View) {
View view = (View) event;
ContentCaptureSession session = view.getContentCaptureSession();
@@ -1015,12 +976,12 @@
view.onProvideContentCaptureStructure(structure, /* flags= */ 0);
session.notifyViewAppeared(structure);
} else if (event instanceof Insets) {
- notifyViewInsetsChanged(sessionId, (Insets) event);
+ internalNotifyViewInsetsChanged(sessionId, (Insets) event);
} else {
Log.w(TAG, "invalid content capture event: " + event);
}
}
- notifyViewTreeEvent(sessionId, /* started= */ false);
+ internalNotifyViewTreeEvent(sessionId, /* started= */ false);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
diff --git a/core/java/com/android/internal/pm/parsing/PackageInfoCommonUtils.java b/core/java/com/android/internal/pm/parsing/PackageInfoCommonUtils.java
index f05d9cb..983658a 100644
--- a/core/java/com/android/internal/pm/parsing/PackageInfoCommonUtils.java
+++ b/core/java/com/android/internal/pm/parsing/PackageInfoCommonUtils.java
@@ -96,8 +96,10 @@
info.baseRevisionCode = pkg.getBaseRevisionCode();
info.splitRevisionCodes = pkg.getSplitRevisionCodes();
info.versionName = pkg.getVersionName();
- info.sharedUserId = pkg.getSharedUserId();
- info.sharedUserLabel = pkg.getSharedUserLabelResourceId();
+ if (!pkg.isLeavingSharedUser()) {
+ info.sharedUserId = pkg.getSharedUserId();
+ info.sharedUserLabel = pkg.getSharedUserLabelResourceId();
+ }
info.applicationInfo = applicationInfo;
info.installLocation = pkg.getInstallLocation();
if ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c6209dd..232a36f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -578,6 +578,7 @@
<protected-broadcast android:name="com.android.settings.network.SWITCH_TO_SUBSCRIPTION" />
<protected-broadcast android:name="com.android.settings.wifi.action.NETWORK_REQUEST" />
+ <protected-broadcast android:name="android.app.action.KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED" />
<protected-broadcast android:name="NotificationManagerService.TIMEOUT" />
<protected-broadcast android:name="NotificationHistoryDatabase.CLEANUP" />
<protected-broadcast android:name="ScheduleConditionProvider.EVALUATE" />
diff --git a/core/tests/coretests/src/android/app/servertransaction/ActivityConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ActivityConfigurationChangeItemTest.java
deleted file mode 100644
index 785a8a1..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/ActivityConfigurationChangeItemTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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.junit.Assert.assertEquals;
-import static org.mockito.Mockito.doReturn;
-
-import android.app.Activity;
-import android.app.ClientTransactionHandler;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.IBinder;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link ActivityConfigurationChangeItem}.
- *
- * Build/Install/Run:
- * atest FrameworksCoreTests:ActivityConfigurationChangeItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class ActivityConfigurationChangeItemTest {
-
- @Mock
- private ClientTransactionHandler mHandler;
- @Mock
- private IBinder mToken;
- @Mock
- private Activity mActivity;
- // Can't mock final class.
- private final Configuration mConfiguration = new Configuration();
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void testGetContextToUpdate() {
- doReturn(mActivity).when(mHandler).getActivity(mToken);
-
- final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem
- .obtain(mToken, mConfiguration);
- final Context context = item.getContextToUpdate(mHandler);
-
- assertEquals(mActivity, context);
- }
-}
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
new file mode 100644
index 0000000..b5e8203
--- /dev/null
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
@@ -0,0 +1,239 @@
+/*
+ * 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.content.Context.DEVICE_ID_DEFAULT;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.app.ActivityThread;
+import android.app.ClientTransactionHandler;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
+import android.util.MergedConfiguration;
+import android.view.IWindow;
+import android.view.InsetsState;
+import android.window.ClientWindowFrames;
+import android.window.WindowContext;
+import android.window.WindowContextInfo;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for subtypes of {@link ClientTransactionItem}.
+ *
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:ClientTransactionItemTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class ClientTransactionItemTest {
+
+ @Mock
+ private ClientTransactionHandler mHandler;
+ @Mock
+ private IBinder mActivityToken;
+ @Mock
+ private Activity mActivity;
+ @Mock
+ private PendingTransactionActions mPendingActions;
+ @Mock
+ private IBinder mWindowClientToken;
+ @Mock
+ private WindowContext mWindowContext;
+ @Mock
+ private IWindow mWindow;
+
+ // Can't mock final class.
+ private Configuration mGlobalConfig;
+ private Configuration mConfiguration;
+ private ActivityThread.ActivityClientRecord mActivityClientRecord;
+ private ArrayMap<IBinder, DestroyActivityItem> mActivitiesToBeDestroyed;
+ private InsetsState mInsetsState;
+ private ClientWindowFrames mFrames;
+ private MergedConfiguration mMergedConfiguration;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mGlobalConfig = new Configuration();
+ mConfiguration = new Configuration();
+ mActivitiesToBeDestroyed = new ArrayMap<>();
+ mActivityClientRecord = new ActivityThread.ActivityClientRecord();
+ mInsetsState = new InsetsState();
+ mFrames = new ClientWindowFrames();
+ mMergedConfiguration = new MergedConfiguration(mGlobalConfig, mConfiguration);
+
+ doReturn(mActivity).when(mHandler).getActivity(mActivityToken);
+ doReturn(mActivitiesToBeDestroyed).when(mHandler).getActivitiesToBeDestroyed();
+ }
+
+ @Test
+ public void testActivityConfigurationChangeItem_getContextToUpdate() {
+ final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem
+ .obtain(mActivityToken, mConfiguration);
+ final Context context = item.getContextToUpdate(mHandler);
+
+ assertEquals(mActivity, context);
+ }
+
+ @Test
+ public void testActivityRelaunchItem_getContextToUpdate() {
+ final ActivityRelaunchItem item = ActivityRelaunchItem
+ .obtain(mActivityToken, null /* pendingResults */, null /* pendingNewIntents */,
+ 0 /* configChange */, mMergedConfiguration, false /* preserveWindow */);
+ final Context context = item.getContextToUpdate(mHandler);
+
+ assertEquals(mActivity, context);
+ }
+
+ @Test
+ public void testConfigurationChangeItem_getContextToUpdate() {
+ final ConfigurationChangeItem item = ConfigurationChangeItem
+ .obtain(mConfiguration, DEVICE_ID_DEFAULT);
+ final Context context = item.getContextToUpdate(mHandler);
+
+ assertEquals(ActivityThread.currentApplication(), context);
+ }
+
+ @Test
+ public void testDestroyActivityItem_preExecute() {
+ final DestroyActivityItem item = DestroyActivityItem
+ .obtain(mActivityToken, false /* finished */, 123 /* configChanges */);
+ item.preExecute(mHandler);
+
+ assertEquals(1, mActivitiesToBeDestroyed.size());
+ assertEquals(item, mActivitiesToBeDestroyed.get(mActivityToken));
+ }
+
+ @Test
+ public void testDestroyActivityItem_postExecute() {
+ final DestroyActivityItem item = DestroyActivityItem
+ .obtain(mActivityToken, false /* finished */, 123 /* configChanges */);
+ item.preExecute(mHandler);
+ item.postExecute(mHandler, mPendingActions);
+
+ assertTrue(mActivitiesToBeDestroyed.isEmpty());
+ }
+
+ @Test
+ public void testDestroyActivityItem_execute() {
+ final DestroyActivityItem item = DestroyActivityItem
+ .obtain(mActivityToken, false /* finished */, 123 /* configChanges */);
+ item.execute(mHandler, mActivityClientRecord, mPendingActions);
+
+ verify(mHandler).handleDestroyActivity(eq(mActivityClientRecord), eq(false) /* finishing */,
+ eq(123) /* configChanges */, eq(false) /* getNonConfigInstance */, any());
+ }
+
+ @Test
+ public void testLaunchActivityItem_getContextToUpdate() {
+ final LaunchActivityItem item = new TestUtils.LaunchActivityItemBuilder(
+ mActivityToken, new Intent(), new ActivityInfo())
+ .build();
+
+ final Context context = item.getContextToUpdate(mHandler);
+
+ assertEquals(ActivityThread.currentApplication(), context);
+ }
+
+ @Test
+ public void testMoveToDisplayItem_getContextToUpdate() {
+ final MoveToDisplayItem item = MoveToDisplayItem
+ .obtain(mActivityToken, DEFAULT_DISPLAY, mConfiguration);
+ final Context context = item.getContextToUpdate(mHandler);
+
+ assertEquals(mActivity, context);
+ }
+
+ @Test
+ public void testWindowContextInfoChangeItem_execute() {
+ final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
+ .obtain(mWindowClientToken, mConfiguration, DEFAULT_DISPLAY);
+ item.execute(mHandler, mPendingActions);
+
+ verify(mHandler).handleWindowContextInfoChanged(mWindowClientToken,
+ new WindowContextInfo(mConfiguration, DEFAULT_DISPLAY));
+ }
+
+ @Test
+ public void testWindowContextInfoChangeItem_getContextToUpdate() {
+ doReturn(mWindowContext).when(mHandler).getWindowContext(mWindowClientToken);
+
+ final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
+ .obtain(mWindowClientToken, mConfiguration, DEFAULT_DISPLAY);
+ final Context context = item.getContextToUpdate(mHandler);
+
+ assertEquals(mWindowContext, context);
+ }
+
+ @Test
+ public void testWindowContextWindowRemovalItem_execute() {
+ final WindowContextWindowRemovalItem item = WindowContextWindowRemovalItem.obtain(
+ mWindowClientToken);
+ item.execute(mHandler, mPendingActions);
+
+ verify(mHandler).handleWindowContextWindowRemoval(mWindowClientToken);
+ }
+
+ @Test
+ public void testWindowStateResizeItem_execute() throws RemoteException {
+ final WindowStateResizeItem item = WindowStateResizeItem.obtain(mWindow, mFrames,
+ true /* reportDraw */, mMergedConfiguration, mInsetsState, true /* forceLayout */,
+ true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
+ true /* dragResizing */);
+ item.execute(mHandler, mPendingActions);
+
+ verify(mWindow).resized(mFrames,
+ true /* reportDraw */, mMergedConfiguration, mInsetsState, true /* forceLayout */,
+ true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
+ true /* dragResizing */);
+ }
+
+ @Test
+ public void testWindowStateResizeItem_getContextToUpdate() {
+ final WindowStateResizeItem item = WindowStateResizeItem.obtain(mWindow, mFrames,
+ true /* reportDraw */, mMergedConfiguration, mInsetsState, true /* forceLayout */,
+ true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
+ true /* dragResizing */);
+ final Context context = item.getContextToUpdate(mHandler);
+
+ assertEquals(ActivityThread.currentApplication(), context);
+ }
+
+}
diff --git a/core/tests/coretests/src/android/app/servertransaction/ConfigurationChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ConfigurationChangeItemTest.java
deleted file mode 100644
index d9f5523..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/ConfigurationChangeItemTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.content.Context.DEVICE_ID_DEFAULT;
-
-import static org.junit.Assert.assertEquals;
-
-import android.app.ActivityThread;
-import android.app.ClientTransactionHandler;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link ConfigurationChangeItem}.
- *
- * Build/Install/Run:
- * atest FrameworksCoreTests:ConfigurationChangeItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class ConfigurationChangeItemTest {
-
- @Mock
- private ClientTransactionHandler mHandler;
- // Can't mock final class.
- private final Configuration mConfiguration = new Configuration();
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void testGetContextToUpdate() {
- final ConfigurationChangeItem item = ConfigurationChangeItem
- .obtain(mConfiguration, DEVICE_ID_DEFAULT);
- final Context context = item.getContextToUpdate(mHandler);
-
- assertEquals(ActivityThread.currentApplication(), context);
- }
-}
diff --git a/core/tests/coretests/src/android/app/servertransaction/DestroyActivityItemTest.java b/core/tests/coretests/src/android/app/servertransaction/DestroyActivityItemTest.java
deleted file mode 100644
index ecd75a8..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/DestroyActivityItemTest.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
-
-import android.app.ActivityThread.ActivityClientRecord;
-import android.app.ClientTransactionHandler;
-import android.os.IBinder;
-import android.platform.test.annotations.Presubmit;
-import android.util.ArrayMap;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link DestroyActivityItem}.
- *
- * Build/Install/Run:
- * atest FrameworksCoreTests:DestroyActivityItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class DestroyActivityItemTest {
-
- @Mock
- private ClientTransactionHandler mHandler;
- @Mock
- private PendingTransactionActions mPendingActions;
- @Mock
- private IBinder mActivityToken;
-
- // Can't mock final class.
- private ActivityClientRecord mActivityClientRecord;
-
- private ArrayMap<IBinder, DestroyActivityItem> mActivitiesToBeDestroyed;
- private DestroyActivityItem mItem;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mItem = DestroyActivityItem.obtain(
- mActivityToken, false /* finished */, 123 /* configChanges */);
- mActivityClientRecord = new ActivityClientRecord();
- mActivitiesToBeDestroyed = new ArrayMap<>();
-
- doReturn(mActivitiesToBeDestroyed).when(mHandler).getActivitiesToBeDestroyed();
- }
-
- @Test
- public void testPreExecute() {
- mItem.preExecute(mHandler);
-
- assertEquals(1, mActivitiesToBeDestroyed.size());
- assertEquals(mItem, mActivitiesToBeDestroyed.get(mActivityToken));
- }
-
- @Test
- public void testPostExecute() {
- mItem.preExecute(mHandler);
- mItem.postExecute(mHandler, mPendingActions);
-
- assertTrue(mActivitiesToBeDestroyed.isEmpty());
- }
-
- @Test
- public void testExecute() {
- mItem.execute(mHandler, mActivityClientRecord, mPendingActions);
-
- verify(mHandler).handleDestroyActivity(eq(mActivityClientRecord), eq(false) /* finishing */,
- eq(123) /* configChanges */, eq(false) /* getNonConfigInstance */, any());
- }
-}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
deleted file mode 100644
index a801a76..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/WindowContextInfoChangeItemTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * 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.junit.Assert.assertEquals;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.verify;
-
-import android.app.ClientTransactionHandler;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.os.IBinder;
-import android.platform.test.annotations.Presubmit;
-import android.window.WindowContext;
-import android.window.WindowContextInfo;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link WindowContextInfoChangeItem}.
- *
- * Build/Install/Run:
- * atest FrameworksCoreTests:WindowContextInfoChangeItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class WindowContextInfoChangeItemTest {
-
- @Mock
- private ClientTransactionHandler mHandler;
- @Mock
- private PendingTransactionActions mPendingActions;
- @Mock
- private IBinder mClientToken;
- @Mock
- private WindowContext mWindowContext;
- // Can't mock final class.
- private final Configuration mConfiguration = new Configuration();
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- public void testExecute() {
- final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
- .obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
- item.execute(mHandler, mPendingActions);
-
- verify(mHandler).handleWindowContextInfoChanged(mClientToken,
- new WindowContextInfo(mConfiguration, DEFAULT_DISPLAY));
- }
-
- @Test
- public void testGetContextToUpdate() {
- doReturn(mWindowContext).when(mHandler).getWindowContext(mClientToken);
-
- final WindowContextInfoChangeItem item = WindowContextInfoChangeItem
- .obtain(mClientToken, mConfiguration, DEFAULT_DISPLAY);
- final Context context = item.getContextToUpdate(mHandler);
-
- assertEquals(mWindowContext, context);
- }
-}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
deleted file mode 100644
index cf9935f..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/WindowContextWindowRemovalItemTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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 android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link WindowContextWindowRemovalItem}.
- *
- * Build/Install/Run:
- * atest FrameworksCoreTests:WindowContextWindowRemovalItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class WindowContextWindowRemovalItemTest {
-
- @Mock
- private ClientTransactionHandler mHandler;
- @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, mPendingActions);
-
- verify(mHandler).handleWindowContextWindowRemoval(mClientToken);
- }
-}
diff --git a/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java b/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java
deleted file mode 100644
index 4d45daf..0000000
--- a/core/tests/coretests/src/android/app/servertransaction/WindowStateResizeItemTest.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.RemoteException;
-import android.platform.test.annotations.Presubmit;
-import android.util.MergedConfiguration;
-import android.view.IWindow;
-import android.view.InsetsState;
-import android.window.ClientWindowFrames;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-/**
- * Tests for {@link WindowStateResizeItem}.
- *
- * Build/Install/Run:
- * atest FrameworksCoreTests:WindowStateResizeItemTest
- */
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class WindowStateResizeItemTest {
-
- @Mock
- private ClientTransactionHandler mHandler;
- @Mock
- private PendingTransactionActions mPendingActions;
- @Mock
- private IWindow mWindow;
-
- private InsetsState mInsetsState;
- private ClientWindowFrames mFrames;
- private MergedConfiguration mConfiguration;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
-
- mInsetsState = new InsetsState();
- mFrames = new ClientWindowFrames();
- mConfiguration = new MergedConfiguration();
- }
-
- @Test
- public void testExecute() throws RemoteException {
- final WindowStateResizeItem item = WindowStateResizeItem.obtain(mWindow, mFrames,
- true /* reportDraw */, mConfiguration, mInsetsState, true /* forceLayout */,
- true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
- true /* dragResizing */);
- item.execute(mHandler, mPendingActions);
-
- verify(mWindow).resized(mFrames,
- true /* reportDraw */, mConfiguration, mInsetsState, true /* forceLayout */,
- true /* alwaysConsumeSystemBars */, 123 /* displayId */, 321 /* syncSeqId */,
- true /* dragResizing */);
- }
-}
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
index 35ddfdb..e52aa1b 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java
@@ -153,7 +153,7 @@
final ContentCaptureManager manager =
new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS);
// Ensure main session is created.
- final MainContentCaptureSession unused = manager.getMainContentCaptureSession();
+ final ContentCaptureSession unused = manager.getMainContentCaptureSession();
final WindowManager.LayoutParams initialParam = new WindowManager.LayoutParams();
initialParam.flags |= WindowManager.LayoutParams.FLAG_SECURE;
@@ -167,7 +167,7 @@
final ContentCaptureManager manager =
new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS);
// Ensure main session is created.
- final MainContentCaptureSession unused = manager.getMainContentCaptureSession();
+ final ContentCaptureSession unused = manager.getMainContentCaptureSession();
final WindowManager.LayoutParams initialParam = new WindowManager.LayoutParams();
initialParam.flags |= WindowManager.LayoutParams.FLAG_SECURE;
// Default param does not have FLAG_SECURE set.
@@ -184,7 +184,7 @@
final ContentCaptureManager manager =
new ContentCaptureManager(mMockContext, mMockContentCaptureManager, EMPTY_OPTIONS);
// Ensure main session is created.
- final MainContentCaptureSession unused = manager.getMainContentCaptureSession();
+ final ContentCaptureSession unused = manager.getMainContentCaptureSession();
// Default param does not have FLAG_SECURE set.
final WindowManager.LayoutParams resetParam = new WindowManager.LayoutParams();
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 23b9b9b..4a4c693 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -21,12 +21,19 @@
import static org.testng.Assert.assertThrows;
import android.compat.testing.PlatformCompatChangeRule;
+import android.content.ComponentName;
import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.SparseArray;
import android.view.View;
import android.view.ViewStructure;
import android.view.autofill.AutofillId;
import android.view.contentcapture.ViewNode.ViewStructureImpl;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import com.google.common.collect.ImmutableMap;
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
@@ -40,6 +47,7 @@
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
+import java.util.ArrayList;
import java.util.Map;
/**
@@ -195,6 +203,22 @@
}
@Override
+ void start(@NonNull IBinder token, @NonNull IBinder shareableActivityToken,
+ @NonNull ComponentName component, int flags) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ boolean isDisabled() {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ boolean setDisabled(boolean disabled) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
ContentCaptureSession newChild(ContentCaptureContext context) {
throw new UnsupportedOperationException("should not have been called");
}
@@ -210,20 +234,20 @@
}
@Override
- void internalNotifyViewAppeared(ViewStructureImpl node) {
+ void internalNotifyViewAppeared(final int sessionId, ViewStructureImpl node) {
throw new UnsupportedOperationException("should not have been called");
}
@Override
- void internalNotifyViewDisappeared(AutofillId id) {}
+ void internalNotifyViewDisappeared(final int sessionId, AutofillId id) {}
@Override
- void internalNotifyViewTextChanged(AutofillId id, CharSequence text) {
+ void internalNotifyViewTextChanged(final int sessionId, AutofillId id, CharSequence text) {
throw new UnsupportedOperationException("should not have been called");
}
@Override
- public void internalNotifyViewTreeEvent(boolean started) {
+ public void internalNotifyViewTreeEvent(final int sessionId, boolean started) {
if (started) {
mInternalNotifyViewTreeEventStartedCount += 1;
} else {
@@ -242,7 +266,34 @@
}
@Override
- void internalNotifyViewInsetsChanged(Insets viewInsets) {
+ void internalNotifyChildSessionStarted(int parentSessionId, int childSessionId,
+ @NonNull ContentCaptureContext clientContext) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ void internalNotifyChildSessionFinished(int parentSessionId, int childSessionId) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ void internalNotifyContextUpdated(int sessionId, @Nullable ContentCaptureContext context) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ public void notifyWindowBoundsChanged(int sessionId, @NonNull Rect bounds) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
+
+ @Override
+ public void notifyContentCaptureEvents(
+ @NonNull SparseArray<ArrayList<Object>> contentCaptureEvents) {
+
+ }
+
+ @Override
+ void internalNotifyViewInsetsChanged(final int sessionId, Insets viewInsets) {
throw new UnsupportedOperationException("should not have been called");
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 15ee4e1..65597de 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -47,6 +47,7 @@
import static androidx.window.extensions.embedding.SplitPresenter.getMinDimensions;
import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSplit;
+import android.annotation.CallbackExecutor;
import android.app.Activity;
import android.app.ActivityClient;
import android.app.ActivityOptions;
@@ -63,6 +64,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.SystemProperties;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
@@ -85,6 +87,7 @@
import androidx.window.extensions.WindowExtensionsImpl;
import androidx.window.extensions.core.util.function.Consumer;
import androidx.window.extensions.core.util.function.Function;
+import androidx.window.extensions.core.util.function.Predicate;
import androidx.window.extensions.embedding.TransactionManager.TransactionRecord;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
@@ -158,8 +161,20 @@
/** Callback to Jetpack to notify about changes to split states. */
@GuardedBy("mLock")
@Nullable
- private Consumer<List<SplitInfo>> mEmbeddingCallback;
+ private Consumer<List<SplitInfo>> mSplitInfoCallback;
private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
+
+ /**
+ * Stores callbacks to Jetpack to notify about changes to {@link ActivityStack activityStacks}
+ * and corresponding {@link Executor executors} to dispatch the callback.
+ */
+ @GuardedBy("mLock")
+ @NonNull
+ private final ArrayMap<Consumer<List<ActivityStack>>, Executor> mActivityStackCallbacks =
+ new ArrayMap<>();
+
+ private final List<ActivityStack> mLastReportedActivityStacks = new ArrayList<>();
+
private final Handler mHandler;
final Object mLock = new Object();
private final ActivityStartMonitor mActivityStartMonitor;
@@ -283,7 +298,7 @@
}
@Override
- public void unpinTopActivityStack(int taskId){
+ public void unpinTopActivityStack(int taskId) {
synchronized (mLock) {
Log.i(TAG, "Request to unpin top activity stack.");
final TaskContainer task = getTaskContainer(taskId);
@@ -333,6 +348,9 @@
public void setActivityStackAttributesCalculator(
@NonNull Function<ActivityStackAttributesCalculatorParams, ActivityStackAttributes>
calculator) {
+ if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
+ return;
+ }
synchronized (mLock) {
mActivityStackAttributesCalculator = calculator;
}
@@ -340,6 +358,9 @@
@Override
public void clearActivityStackAttributesCalculator() {
+ if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
+ return;
+ }
synchronized (mLock) {
mActivityStackAttributesCalculator = null;
}
@@ -351,7 +372,7 @@
return mSplitAttributesCalculator;
}
- @Override
+ // TODO(b/295993745): remove after we migrate to the bundle approach.
@NonNull
public ActivityOptions setLaunchingActivityStack(@NonNull ActivityOptions options,
@NonNull IBinder token) {
@@ -368,6 +389,7 @@
/**
* Registers the split organizer callback to notify about changes to active splits.
+ *
* @deprecated Use {@link #setSplitInfoCallback(Consumer)} starting with
* {@link WindowExtensionsImpl#getVendorApiLevel()} 2.
*/
@@ -381,12 +403,14 @@
/**
* Registers the split organizer callback to notify about changes to active splits.
+ *
* @since {@link WindowExtensionsImpl#getVendorApiLevel()} 2
*/
+ @Override
public void setSplitInfoCallback(Consumer<List<SplitInfo>> callback) {
synchronized (mLock) {
- mEmbeddingCallback = callback;
- updateCallbackIfNecessary();
+ mSplitInfoCallback = callback;
+ updateSplitInfoCallbackIfNecessary();
}
}
@@ -396,7 +420,35 @@
@Override
public void clearSplitInfoCallback() {
synchronized (mLock) {
- mEmbeddingCallback = null;
+ mSplitInfoCallback = null;
+ }
+ }
+
+ /**
+ * Registers the callback for the {@link ActivityStack} state change.
+ *
+ * @param executor The executor to dispatch the callback.
+ * @param callback The callback for this {@link ActivityStack} state change.
+ */
+ @Override
+ public void registerActivityStackCallback(@NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<List<ActivityStack>> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ synchronized (mLock) {
+ mActivityStackCallbacks.put(callback, executor);
+ updateActivityStackCallbackIfNecessary();
+ }
+ }
+
+ /** @see #registerActivityStackCallback(Executor, Consumer) */
+ @Override
+ public void unregisterActivityStackCallback(@NonNull Consumer<List<ActivityStack>> callback) {
+ Objects.requireNonNull(callback);
+
+ synchronized (mLock) {
+ mActivityStackCallbacks.remove(callback);
}
}
@@ -408,13 +460,11 @@
synchronized (mLock) {
// Translate ActivityStack to TaskFragmentContainer.
final List<TaskFragmentContainer> pendingFinishingContainers =
- activityStackTokens.stream()
- .map(token -> {
+ activityStackTokens.stream().map(token -> {
synchronized (mLock) {
return getContainer(token);
}
- }).filter(Objects::nonNull)
- .toList();
+ }).filter(Objects::nonNull).toList();
if (pendingFinishingContainers.isEmpty()) {
return;
@@ -497,6 +547,68 @@
}
}
+ @Override
+ public void updateActivityStackAttributes(@NonNull IBinder activityStackToken,
+ @NonNull ActivityStackAttributes attributes) {
+ if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
+ return;
+ }
+ Objects.requireNonNull(activityStackToken);
+ Objects.requireNonNull(attributes);
+
+ synchronized (mLock) {
+ final TaskFragmentContainer container = getContainer(activityStackToken);
+ if (container == null) {
+ Log.w(TAG, "Cannot find TaskFragmentContainer for token:" + activityStackToken);
+ return;
+ }
+ if (!container.isOverlay()) {
+ Log.w(TAG, "Updating non-overlay container has not supported yet!");
+ return;
+ }
+
+ final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction();
+ final WindowContainerTransaction wct = transactionRecord.getTransaction();
+ mPresenter.applyActivityStackAttributes(wct, container, attributes);
+ transactionRecord.apply(false /* shouldApplyIndependently */);
+ }
+ }
+
+ @Override
+ @Nullable
+ public ParentContainerInfo getParentContainerInfo(@NonNull IBinder activityStackToken) {
+ if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
+ return null;
+ }
+ Objects.requireNonNull(activityStackToken);
+ synchronized (mLock) {
+ final TaskFragmentContainer container = getContainer(activityStackToken);
+ if (container == null) {
+ return null;
+ }
+ final TaskContainer.TaskProperties properties = container.getTaskContainer()
+ .getTaskProperties();
+ return mPresenter.createParentContainerInfoFromTaskProperties(properties);
+ }
+ }
+
+ @Override
+ @Nullable
+ public IBinder getActivityStackToken(@NonNull String tag) {
+ if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
+ return null;
+ }
+ Objects.requireNonNull(tag);
+ synchronized (mLock) {
+ final TaskFragmentContainer taskFragmentContainer =
+ getContainer(container -> tag.equals(container.getOverlayTag()));
+ if (taskFragmentContainer == null) {
+ return null;
+ }
+ return taskFragmentContainer.getTaskFragmentToken();
+ }
+ }
+
/**
* Called when the transaction is ready so that the organizer can update the TaskFragments based
* on the changes in transaction.
@@ -565,8 +677,9 @@
/**
* Called when a TaskFragment is created and organized by this organizer.
*
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
- * @param taskFragmentInfo Info of the TaskFragment that is created.
+ * @param wct The {@link WindowContainerTransaction} to make any changes with if
+ * needed.
+ * @param taskFragmentInfo Info of the TaskFragment that is created.
*/
// Suppress GuardedBy warning because lint ask to mark this method as
// @GuardedBy(container.mController.mLock), which is mLock itself
@@ -574,7 +687,7 @@
@VisibleForTesting
@GuardedBy("mLock")
void onTaskFragmentAppeared(@NonNull WindowContainerTransaction wct,
- @NonNull TaskFragmentInfo taskFragmentInfo) {
+ @NonNull TaskFragmentInfo taskFragmentInfo) {
final TaskFragmentContainer container = getContainer(taskFragmentInfo.getFragmentToken());
if (container == null) {
return;
@@ -594,8 +707,9 @@
/**
* Called when the status of an organized TaskFragment is changed.
*
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
- * @param taskFragmentInfo Info of the TaskFragment that is changed.
+ * @param wct The {@link WindowContainerTransaction} to make any changes with if
+ * needed.
+ * @param taskFragmentInfo Info of the TaskFragment that is changed.
*/
// Suppress GuardedBy warning because lint ask to mark this method as
// @GuardedBy(container.mController.mLock), which is mLock itself
@@ -665,8 +779,9 @@
/**
* Called when an organized TaskFragment is removed.
*
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
- * @param taskFragmentInfo Info of the TaskFragment that is removed.
+ * @param wct The {@link WindowContainerTransaction} to make any changes with if
+ * needed.
+ * @param taskFragmentInfo Info of the TaskFragment that is removed.
*/
@VisibleForTesting
@GuardedBy("mLock")
@@ -686,14 +801,14 @@
* Called when the parent leaf Task of organized TaskFragments is changed.
* When the leaf Task is changed, the organizer may want to update the TaskFragments in one
* transaction.
- *
+ * <p>
* For case like screen size change, it will trigger {@link #onTaskFragmentParentInfoChanged}
* with new Task bounds, but may not trigger {@link #onTaskFragmentInfoChanged} because there
* can be an override bounds.
*
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
- * @param taskId Id of the parent Task that is changed.
- * @param parentInfo {@link TaskFragmentParentInfo} of the parent Task.
+ * @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
+ * @param taskId Id of the parent Task that is changed.
+ * @param parentInfo {@link TaskFragmentParentInfo} of the parent Task.
*/
@VisibleForTesting
@GuardedBy("mLock")
@@ -746,20 +861,20 @@
* original Task. In this case, we need to notify the organizer so that it can check if the
* Activity matches any split rule.
*
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
- * @param taskId The Task that the activity is reparented to.
- * @param activityIntent The intent that the activity is original launched with.
- * @param activityToken If the activity belongs to the same process as the organizer, this
- * will be the actual activity token; if the activity belongs to a
- * different process, the server will generate a temporary token that
- * the organizer can use to reparent the activity through
- * {@link WindowContainerTransaction} if needed.
+ * @param wct The {@link WindowContainerTransaction} to make any changes with if
+ * needed.
+ * @param taskId The Task that the activity is reparented to.
+ * @param activityIntent The intent that the activity is original launched with.
+ * @param activityToken If the activity belongs to the same process as the organizer, this
+ * will be the actual activity token; if the activity belongs to a
+ * different process, the server will generate a temporary token that
+ * the organizer can use to reparent the activity through
+ * {@link WindowContainerTransaction} if needed.
*/
@VisibleForTesting
@GuardedBy("mLock")
void onActivityReparentedToTask(@NonNull WindowContainerTransaction wct,
- int taskId, @NonNull Intent activityIntent,
- @NonNull IBinder activityToken) {
+ int taskId, @NonNull Intent activityIntent, @NonNull IBinder activityToken) {
// If the activity belongs to the current app process, we treat it as a new activity
// launch.
final Activity activity = getActivity(activityToken);
@@ -807,14 +922,15 @@
* Called when the {@link WindowContainerTransaction} created with
* {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
*
- * @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
- * @param errorCallbackToken token set in
- * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)}
- * @param taskFragmentInfo The {@link TaskFragmentInfo}. This could be {@code null} if no
- * TaskFragment created.
- * @param opType The {@link WindowContainerTransaction.HierarchyOp} of the failed
- * transaction operation.
- * @param exception exception from the server side.
+ * @param wct The {@link WindowContainerTransaction} to make any changes with if
+ * needed.
+ * @param errorCallbackToken token set in
+ * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)}
+ * @param taskFragmentInfo The {@link TaskFragmentInfo}. This could be {@code null} if no
+ * TaskFragment created.
+ * @param opType The {@link WindowContainerTransaction.HierarchyOp} of the failed
+ * transaction operation.
+ * @param exception exception from the server side.
*/
// Suppress GuardedBy warning because lint ask to mark this method as
// @GuardedBy(container.mController.mLock), which is mLock itself
@@ -854,7 +970,9 @@
}
}
- /** Called on receiving {@link #onTaskFragmentVanished} for cleanup. */
+ /**
+ * Called on receiving {@link #onTaskFragmentVanished} for cleanup.
+ */
@GuardedBy("mLock")
private void cleanupTaskFragment(@NonNull IBinder taskFragmentToken) {
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
@@ -881,11 +999,12 @@
/**
* Checks if the new added activity should be routed to a particular container. It can create a
* new container for the activity and a new split container if necessary.
- * @param activity the activity that is newly added to the Task.
- * @param isOnReparent whether the activity is reparented to the Task instead of new launched.
- * We only support to split as primary for reparented activity for now.
+ *
+ * @param activity the activity that is newly added to the Task.
+ * @param isOnReparent whether the activity is reparented to the Task instead of new launched.
+ * We only support to split as primary for reparented activity for now.
* @return {@code true} if the activity has been handled, such as placed in a TaskFragment, or
- * in a state that the caller shouldn't handle.
+ * in a state that the caller shouldn't handle.
*/
@VisibleForTesting
@GuardedBy("mLock")
@@ -918,7 +1037,7 @@
final TaskContainer taskContainer = container != null ? container.getTaskContainer() : null;
if (!isOnReparent && taskContainer != null
&& taskContainer.getTopNonFinishingTaskFragmentContainer(false /* includePin */)
- != container) {
+ != container) {
// Do not resolve if the launched activity is not the top-most container (excludes
// the pinned and overlay container) in the Task.
return true;
@@ -943,6 +1062,7 @@
/**
* Resolves the activity to a {@link TaskFragmentContainer} according to the Split-rules.
*/
+ @GuardedBy("mLock")
boolean resolveActivityToContainerByRule(@NonNull WindowContainerTransaction wct,
@NonNull Activity activity, @Nullable TaskFragmentContainer container,
boolean isOnReparent) {
@@ -1027,7 +1147,7 @@
@GuardedBy("mLock")
@VisibleForTesting
void placeActivityInTopContainer(@NonNull WindowContainerTransaction wct,
- @NonNull Activity activity) {
+ @NonNull Activity activity) {
if (getContainerWithActivity(activity) != null) {
// The activity has already been put in a TaskFragment. This is likely to be done by
// the server when the activity is started.
@@ -1077,7 +1197,7 @@
*/
@GuardedBy("mLock")
private void expandActivity(@NonNull WindowContainerTransaction wct,
- @NonNull Activity activity) {
+ @NonNull Activity activity) {
final TaskFragmentContainer container = getContainerWithActivity(activity);
if (shouldContainerBeExpanded(container)) {
// Make sure that the existing container is expanded.
@@ -1089,7 +1209,9 @@
}
}
- /** Whether the given new launched activity is in a split with a rule matched. */
+ /**
+ * Whether the given new launched activity is in a split with a rule matched.
+ */
// Suppress GuardedBy warning because lint asks to mark this method as
// @GuardedBy(mPresenter.mController.mLock), which is mLock itself
@SuppressWarnings("GuardedBy")
@@ -1147,7 +1269,9 @@
return getSplitRule(primaryActivity, launchedActivity) != null;
}
- /** Finds the activity below the given activity. */
+ /**
+ * Finds the activity below the given activity.
+ */
@VisibleForTesting
@Nullable
@GuardedBy("mLock")
@@ -1198,8 +1322,8 @@
getActivitiesMinDimensionsPair(primaryActivity, secondaryActivity));
if (splitContainer != null && primaryContainer == splitContainer.getPrimaryContainer()
&& canReuseContainer(splitRule, splitContainer.getSplitRule(),
- taskProperties.getTaskMetrics(),
- calculatedSplitAttributes, splitContainer.getCurrentSplitAttributes())) {
+ taskProperties.getTaskMetrics(),
+ calculatedSplitAttributes, splitContainer.getCurrentSplitAttributes())) {
// Can launch in the existing secondary container if the rules share the same
// presentation.
final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
@@ -1333,7 +1457,7 @@
* prioritize to split the new activity with it if it is not
* {@code null}.
* @return the {@link TaskFragmentContainer} to start the new activity in. {@code null} if there
- * is no embedding rule matched.
+ * is no embedding rule matched.
*/
@VisibleForTesting
@Nullable
@@ -1478,7 +1602,7 @@
final Rect taskBounds = taskContainer.getTaskProperties().getTaskMetrics().getBounds();
final Rect sanitizedBounds = sanitizeBounds(bounds, intent, taskBounds);
final int windowingMode = taskContainer
- .getWindowingModeForSplitTaskFragment(sanitizedBounds);
+ .getWindowingModeForTaskFragment(sanitizedBounds);
mPresenter.createTaskFragment(wct, taskFragmentToken, activityInTask.getActivityToken(),
sanitizedBounds, windowingMode);
mPresenter.updateAnimationParams(wct, taskFragmentToken,
@@ -1495,7 +1619,7 @@
*/
@NonNull
private static Rect sanitizeBounds(@NonNull Rect bounds, @NonNull Intent intent,
- @NonNull Rect taskBounds) {
+ @NonNull Rect taskBounds) {
if (bounds.isEmpty()) {
// Don't need to check if the bounds follows the task bounds.
return bounds;
@@ -1534,11 +1658,11 @@
getActivityIntentMinDimensionsPair(primaryActivity, intent));
if (splitContainer != null && existingContainer == splitContainer.getPrimaryContainer()
&& (canReuseContainer(splitRule, splitContainer.getSplitRule(), taskWindowMetrics,
- calculatedSplitAttributes, splitContainer.getCurrentSplitAttributes())
+ calculatedSplitAttributes, splitContainer.getCurrentSplitAttributes())
// TODO(b/231845476) we should always respect clearTop.
|| !respectClearTop)
&& mPresenter.expandSplitContainerIfNeeded(wct, splitContainer, primaryActivity,
- null /* secondaryActivity */, intent) != RESULT_EXPAND_FAILED_NO_TF_INFO) {
+ null /* secondaryActivity */, intent) != RESULT_EXPAND_FAILED_NO_TF_INFO) {
// Can launch in the existing secondary container if the rules share the same
// presentation.
return splitContainer.getSecondaryContainer();
@@ -1563,29 +1687,15 @@
TaskFragmentContainer getContainerWithActivity(@NonNull IBinder activityToken) {
// Check pending appeared activity first because there can be a delay for the server
// update.
- for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
- final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
- .getTaskFragmentContainers();
- for (int j = containers.size() - 1; j >= 0; j--) {
- final TaskFragmentContainer container = containers.get(j);
- if (container.hasPendingAppearedActivity(activityToken)) {
- return container;
- }
- }
+ TaskFragmentContainer taskFragmentContainer =
+ getContainer(container -> container.hasPendingAppearedActivity(activityToken));
+ if (taskFragmentContainer != null) {
+ return taskFragmentContainer;
}
+
// Check appeared activity if there is no such pending appeared activity.
- for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
- final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
- .getTaskFragmentContainers();
- for (int j = containers.size() - 1; j >= 0; j--) {
- final TaskFragmentContainer container = containers.get(j);
- if (container.hasAppearedActivity(activityToken)) {
- return container;
- }
- }
- }
- return null;
+ return getContainer(container -> container.hasAppearedActivity(activityToken));
}
@GuardedBy("mLock")
@@ -1611,8 +1721,8 @@
@GuardedBy("mLock")
TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent,
- @NonNull Activity activityInTask, int taskId,
- @NonNull TaskFragmentContainer pairedPrimaryContainer) {
+ @NonNull Activity activityInTask, int taskId,
+ @NonNull TaskFragmentContainer pairedPrimaryContainer) {
return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
activityInTask, taskId, pairedPrimaryContainer, null /* tag */,
null /* launchOptions */);
@@ -1622,18 +1732,18 @@
* Creates and registers a new organized container with an optional activity that will be
* re-parented to it in a WCT.
*
- * @param pendingAppearedActivity the activity that will be reparented to the TaskFragment.
- * @param pendingAppearedIntent the Intent that will be started in the TaskFragment.
- * @param activityInTask activity in the same Task so that we can get the Task bounds
- * if needed.
- * @param taskId parent Task of the new TaskFragment.
- * @param pairedPrimaryContainer the paired primary {@link TaskFragmentContainer}. When it is
- * set, the new container will be added right above it.
- * @param overlayTag The tag for the new created overlay container. It must be
- * needed if {@code isOverlay} is {@code true}. Otherwise,
- * it should be {@code null}.
- * @param launchOptions The launch options bundle to create a container. Must be
- * specified for overlay container.
+ * @param pendingAppearedActivity the activity that will be reparented to the TaskFragment.
+ * @param pendingAppearedIntent the Intent that will be started in the TaskFragment.
+ * @param activityInTask activity in the same Task so that we can get the Task bounds
+ * if needed.
+ * @param taskId parent Task of the new TaskFragment.
+ * @param pairedPrimaryContainer the paired primary {@link TaskFragmentContainer}. When it is
+ * set, the new container will be added right above it.
+ * @param overlayTag The tag for the new created overlay container. It must be
+ * needed if {@code isOverlay} is {@code true}. Otherwise,
+ * it should be {@code null}.
+ * @param launchOptions The launch options bundle to create a container. Must be
+ * specified for overlay container.
*/
@GuardedBy("mLock")
TaskFragmentContainer newContainer(@Nullable Activity pendingAppearedActivity,
@@ -1674,7 +1784,9 @@
primaryContainer.getTaskContainer().addSplitContainer(splitContainer);
}
- /** Cleanups all the dependencies when the TaskFragment is entering PIP. */
+ /**
+ * Cleanups all the dependencies when the TaskFragment is entering PIP.
+ */
@GuardedBy("mLock")
private void cleanupForEnterPip(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer container) {
@@ -1833,16 +1945,27 @@
@SuppressWarnings("GuardedBy")
@GuardedBy("mLock")
void updateOverlayContainer(@NonNull WindowContainerTransaction wct,
- @NonNull TaskFragmentContainer container) {
+ @NonNull TaskFragmentContainer container) {
final TaskContainer taskContainer = container.getTaskContainer();
// Dismiss the overlay container if it's the only container in the task and there's no
// direct activity in the parent task.
if (taskContainer.getTaskFragmentContainers().size() == 1
&& !taskContainer.hasDirectActivity()) {
container.finish(false /* shouldFinishDependent */, mPresenter, wct, this);
+ return;
}
- // TODO(b/295805054): Add the logic to update overlay container
+ if (mActivityStackAttributesCalculator != null) {
+ final ActivityStackAttributesCalculatorParams params =
+ new ActivityStackAttributesCalculatorParams(
+ mPresenter.createParentContainerInfoFromTaskProperties(
+ taskContainer.getTaskProperties()),
+ container.getOverlayTag(),
+ container.getLaunchOptions());
+ final ActivityStackAttributes attributes = mActivityStackAttributesCalculator
+ .apply(params);
+ mPresenter.applyActivityStackAttributes(wct, container, attributes);
+ }
}
/**
@@ -1851,11 +1974,10 @@
* are {@code null}, the {@link SplitAttributes} will be calculated with
* {@link SplitPresenter#computeSplitAttributes}.
*
- * @param splitContainer The {@link SplitContainer} to update
+ * @param splitContainer The {@link SplitContainer} to update
* @param splitAttributes Update with this {@code splitAttributes} if it is not {@code null}.
* Otherwise, use the value calculated by
* {@link SplitPresenter#computeSplitAttributes}
- *
* @return {@code true} if the update succeed. Otherwise, returns {@code false}.
*/
@VisibleForTesting
@@ -1890,7 +2012,9 @@
return true;
}
- /** Whether the given split is the topmost split in the Task. */
+ /**
+ * Whether the given split is the topmost split in the Task.
+ */
private boolean isTopMostSplit(@NonNull SplitContainer splitContainer) {
final List<SplitContainer> splitContainers = splitContainer.getPrimaryContainer()
.getTaskContainer().getSplitContainers();
@@ -1997,7 +2121,9 @@
return true;
}
- /** Whether or not to allow activity in this container to launch placeholder. */
+ /**
+ * Whether or not to allow activity in this container to launch placeholder.
+ */
@GuardedBy("mLock")
private boolean allowLaunchPlaceholder(@NonNull TaskFragmentContainer container) {
final TaskFragmentContainer topContainer = container.getTaskContainer()
@@ -2031,8 +2157,9 @@
/**
* Gets the activity options for starting the placeholder activity. In case the placeholder is
* launched when the Task is in the background, we don't want to bring the Task to the front.
- * @param primaryActivity the primary activity to launch the placeholder from.
- * @param isOnCreated whether this happens during the primary activity onCreated.
+ *
+ * @param primaryActivity the primary activity to launch the placeholder from.
+ * @param isOnCreated whether this happens during the primary activity onCreated.
*/
@VisibleForTesting
@GuardedBy("mLock")
@@ -2104,7 +2231,16 @@
@VisibleForTesting
@GuardedBy("mLock")
void updateCallbackIfNecessary() {
- if (mEmbeddingCallback == null || !readyToReportToClient()) {
+ updateSplitInfoCallbackIfNecessary();
+ updateActivityStackCallbackIfNecessary();
+ }
+
+ /**
+ * Notifies callbacks about changes to split states if necessary.
+ */
+ @GuardedBy("mLock")
+ private void updateSplitInfoCallbackIfNecessary() {
+ if (!readyToReportToClient() || mSplitInfoCallback == null) {
return;
}
final List<SplitInfo> currentSplitStates = getActiveSplitStatesIfStable();
@@ -2113,7 +2249,32 @@
}
mLastReportedSplitStates.clear();
mLastReportedSplitStates.addAll(currentSplitStates);
- mEmbeddingCallback.accept(currentSplitStates);
+ mSplitInfoCallback.accept(currentSplitStates);
+ }
+
+ /**
+ * Notifies callbacks about changes to {@link ActivityStack} states if necessary.
+ */
+ @GuardedBy("mLock")
+ private void updateActivityStackCallbackIfNecessary() {
+ if (!readyToReportToClient() || mActivityStackCallbacks.isEmpty()) {
+ return;
+ }
+ final List<ActivityStack> currentActivityStacks = getActivityStacksIfStable();
+ if (currentActivityStacks == null
+ || mLastReportedActivityStacks.equals(currentActivityStacks)) {
+ return;
+ }
+ mLastReportedActivityStacks.clear();
+ mLastReportedActivityStacks.addAll(currentActivityStacks);
+ // Copy the map in case a callback is removed during the for-loop.
+ final ArrayMap<Consumer<List<ActivityStack>>, Executor> callbacks =
+ new ArrayMap<>(mActivityStackCallbacks);
+ for (int i = callbacks.size() - 1; i >= 0; --i) {
+ final Executor executor = callbacks.valueAt(i);
+ final Consumer<List<ActivityStack>> callback = callbacks.keyAt(i);
+ executor.execute(() -> callback.accept(currentActivityStacks));
+ }
}
/**
@@ -2138,6 +2299,27 @@
}
/**
+ * Returns a list of currently active {@link ActivityStack activityStacks}.
+ *
+ * @return a list of {@link ActivityStack activityStacks} if all the containers are in
+ * a stable state, or {@code null} otherwise.
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ private List<ActivityStack> getActivityStacksIfStable() {
+ final List<ActivityStack> activityStacks = new ArrayList<>();
+ for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
+ final List<ActivityStack> taskActivityStacks =
+ mTaskContainers.valueAt(i).getActivityStacksIfStable();
+ if (taskActivityStacks == null) {
+ return null;
+ }
+ activityStacks.addAll(taskActivityStacks);
+ }
+ return activityStacks;
+ }
+
+ /**
* Whether we can now report the split states to the client.
*/
@GuardedBy("mLock")
@@ -2207,11 +2389,18 @@
@Nullable
@GuardedBy("mLock")
TaskFragmentContainer getContainer(@NonNull IBinder fragmentToken) {
+ return getContainer(container -> fragmentToken.equals(container.getTaskFragmentToken()));
+ }
+
+ @Nullable
+ @GuardedBy("mLock")
+ TaskFragmentContainer getContainer(@NonNull Predicate<TaskFragmentContainer> predicate) {
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
final List<TaskFragmentContainer> containers = mTaskContainers.valueAt(i)
.getTaskFragmentContainers();
- for (TaskFragmentContainer container : containers) {
- if (container.getTaskFragmentToken().equals(fragmentToken)) {
+ for (int j = containers.size() - 1; j >= 0; j--) {
+ final TaskFragmentContainer container = containers.get(j);
+ if (predicate.test(container)) {
return container;
}
}
@@ -2304,6 +2493,7 @@
* container. There is a case when primary containers for placeholders should be retained
* despite the rule configuration to finish primary with secondary - if they are marked as
* 'sticky' and the placeholder was finished when fully overlapping the primary container.
+ *
* @return {@code true} if the associated container should be retained (and not be finished).
*/
// Suppress GuardedBy warning because lint ask to mark this method as
@@ -2388,8 +2578,9 @@
// If the requested bounds of OverlayCreateParams are smaller than minimum dimensions
// specified by Intent, expand the overlay container to fill the parent task instead.
final ActivityStackAttributesCalculatorParams params =
- new ActivityStackAttributesCalculatorParams(mPresenter.toParentContainerInfo(
- mPresenter.getTaskProperties(launchActivity)), overlayTag, options);
+ new ActivityStackAttributesCalculatorParams(
+ mPresenter.createParentContainerInfoFromTaskProperties(
+ mPresenter.getTaskProperties(launchActivity)), overlayTag, options);
// Fallback to expand the bounds if there's no activityStackAttributes calculator.
final Rect relativeBounds = mActivityStackAttributesCalculator != null
? new Rect(mActivityStackAttributesCalculator.apply(params).getRelativeBounds())
@@ -2407,26 +2598,31 @@
&& taskId == overlayContainer.getTaskId()) {
// If there's an overlay container with different tag shown in the same
// task, dismiss the existing overlay container.
- overlayContainer.finish(false /* shouldFinishDependant */, mPresenter,
- wct, SplitController.this);
+ mPresenter.cleanupContainer(wct, overlayContainer,
+ false /* shouldFinishDependant */);
}
if (overlayTag.equals(overlayContainer.getOverlayTag())
&& taskId != overlayContainer.getTaskId()) {
// If there's an overlay container with same tag in a different task,
// dismiss the overlay container since the tag must be unique per process.
- overlayContainer.finish(false /* shouldFinishDependant */, mPresenter,
- wct, SplitController.this);
+ mPresenter.cleanupContainer(wct, overlayContainer,
+ false /* shouldFinishDependant */);
}
if (overlayTag.equals(overlayContainer.getOverlayTag())
&& taskId == overlayContainer.getTaskId()) {
// If there's an overlay container with the same tag and task ID, we treat
// the OverlayCreateParams as the update to the container.
- final Rect taskBounds = overlayContainer.getTaskContainer().getTaskProperties()
- .getTaskMetrics().getBounds();
final IBinder overlayToken = overlayContainer.getTaskFragmentToken();
+ final TaskContainer taskContainer = overlayContainer.getTaskContainer();
+ final Rect taskBounds = taskContainer.getTaskProperties().getTaskMetrics()
+ .getBounds();
final Rect sanitizedBounds = sanitizeBounds(relativeBounds, intent, taskBounds);
+
mPresenter.resizeTaskFragment(wct, overlayToken, sanitizedBounds);
- mPresenter.setTaskFragmentIsolatedNavigation(wct, overlayToken,
+ final int windowingMode = taskContainer
+ .getWindowingModeForTaskFragment(sanitizedBounds);
+ mPresenter.updateWindowingMode(wct, overlayToken, windowingMode);
+ mPresenter.setTaskFragmentIsolatedNavigation(wct, overlayContainer,
!sanitizedBounds.isEmpty());
// We can just return the updated overlay container and don't need to
// check other condition since we only have one OverlayCreateParams, and
@@ -2539,7 +2735,9 @@
}
}
- /** Executor that posts on the main application thread. */
+ /**
+ * Executor that posts on the main application thread.
+ */
private static class MainThreadExecutor implements Executor {
private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -2687,7 +2885,9 @@
&& calculatedSplitAttributes.equals(containerSplitAttributes);
}
- /** Whether the two rules have the same presentation. */
+ /**
+ * Whether the two rules have the same presentation.
+ */
@VisibleForTesting
static boolean areRulesSamePresentation(@NonNull SplitPairRule rule1,
@NonNull SplitPairRule rule2, @NonNull WindowMetrics parentWindowMetrics) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index acfd8e4..543570c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -16,6 +16,8 @@
package androidx.window.extensions.embedding;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.pm.PackageManager.MATCH_ALL;
import android.app.Activity;
@@ -187,7 +189,7 @@
final Rect secondaryRelBounds = getRelBoundsForPosition(POSITION_END, taskProperties,
splitAttributes);
final int windowingMode = mController.getTaskContainer(taskId)
- .getWindowingModeForSplitTaskFragment(secondaryRelBounds);
+ .getWindowingModeForTaskFragment(secondaryRelBounds);
createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
primaryActivity.getActivityToken(), secondaryRelBounds, windowingMode);
updateAnimationParams(wct, secondaryContainer.getTaskFragmentToken(), splitAttributes);
@@ -259,7 +261,7 @@
if (container == null || container == containerToAvoid) {
container = mController.newContainer(activity, taskId);
final int windowingMode = mController.getTaskContainer(taskId)
- .getWindowingModeForSplitTaskFragment(relBounds);
+ .getWindowingModeForTaskFragment(relBounds);
final IBinder reparentActivityToken = activity.getActivityToken();
createTaskFragment(wct, container.getTaskFragmentToken(), reparentActivityToken,
relBounds, windowingMode, reparentActivityToken);
@@ -268,7 +270,7 @@
} else {
resizeTaskFragmentIfRegistered(wct, container, relBounds);
final int windowingMode = mController.getTaskContainer(taskId)
- .getWindowingModeForSplitTaskFragment(relBounds);
+ .getWindowingModeForTaskFragment(relBounds);
updateTaskFragmentWindowingModeIfRegistered(wct, container, windowingMode);
}
updateAnimationParams(wct, container.getTaskFragmentToken(), splitAttributes);
@@ -310,7 +312,7 @@
// Pass in the primary container to make sure it is added right above the primary.
primaryContainer);
final TaskContainer taskContainer = mController.getTaskContainer(taskId);
- final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment(
+ final int windowingMode = taskContainer.getWindowingModeForTaskFragment(
primaryRelBounds);
mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer,
rule, splitAttributes);
@@ -347,6 +349,7 @@
&& secondaryContainer.areLastRequestedBoundsEqual(null /* bounds */)
&& !secondaryRelBounds.isEmpty();
+ // TODO(b/243518738): remove usages of XXXIfRegistered.
// If the task fragments are not registered yet, the positions will be updated after they
// are created again.
resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRelBounds);
@@ -357,7 +360,7 @@
// When placeholder is shown in split, we should keep the focus on the primary.
wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken());
}
- final int windowingMode = taskContainer.getWindowingModeForSplitTaskFragment(
+ final int windowingMode = taskContainer.getWindowingModeForTaskFragment(
primaryRelBounds);
updateTaskFragmentWindowingModeIfRegistered(wct, primaryContainer, windowingMode);
updateTaskFragmentWindowingModeIfRegistered(wct, secondaryContainer, windowingMode);
@@ -398,13 +401,13 @@
* Sets whether to enable isolated navigation for this {@link TaskFragmentContainer}
*/
void setTaskFragmentIsolatedNavigation(@NonNull WindowContainerTransaction wct,
- @NonNull TaskFragmentContainer taskFragmentContainer,
+ @NonNull TaskFragmentContainer container,
boolean isolatedNavigationEnabled) {
- if (taskFragmentContainer.isIsolatedNavigationEnabled() == isolatedNavigationEnabled) {
+ if (container.isIsolatedNavigationEnabled() == isolatedNavigationEnabled) {
return;
}
- taskFragmentContainer.setIsolatedNavigationEnabled(isolatedNavigationEnabled);
- setTaskFragmentIsolatedNavigation(wct, taskFragmentContainer.getTaskFragmentToken(),
+ container.setIsolatedNavigationEnabled(isolatedNavigationEnabled);
+ setTaskFragmentIsolatedNavigation(wct, container.getTaskFragmentToken(),
isolatedNavigationEnabled);
}
@@ -566,6 +569,15 @@
super.setCompanionTaskFragment(wct, primary, secondary);
}
+ void applyActivityStackAttributes(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskFragmentContainer container, @NonNull ActivityStackAttributes attributes) {
+ final Rect bounds = attributes.getRelativeBounds();
+
+ resizeTaskFragment(wct, container.getTaskFragmentToken(), bounds);
+ updateWindowingMode(wct, container.getTaskFragmentToken(),
+ bounds.isEmpty() ? WINDOWING_MODE_FULLSCREEN : WINDOWING_MODE_MULTI_WINDOW);
+ }
+
/**
* Expands the split container if the current split bounds are smaller than the Activity or
* Intent that is added to the container.
@@ -1086,7 +1098,8 @@
}
@NonNull
- ParentContainerInfo toParentContainerInfo(@NonNull TaskProperties taskProperties) {
+ ParentContainerInfo createParentContainerInfoFromTaskProperties(
+ @NonNull TaskProperties taskProperties) {
final Configuration configuration = taskProperties.getConfiguration();
final WindowLayoutInfo windowLayoutInfo = mWindowLayoutComponent
.getCurrentWindowLayoutInfo(taskProperties.getDisplayId(),
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index 028e75f..64ad4fa 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -145,7 +145,7 @@
* the pair of TaskFragments are stacked due to the limited space.
*/
@WindowingMode
- int getWindowingModeForSplitTaskFragment(@Nullable Rect taskFragmentBounds) {
+ int getWindowingModeForTaskFragment(@Nullable Rect taskFragmentBounds) {
// Only set to multi-windowing mode if the pair are showing side-by-side. Otherwise, it
// will be set to UNDEFINED which will then inherit the Task windowing mode.
if (taskFragmentBounds == null || taskFragmentBounds.isEmpty() || isInPictureInPicture()) {
@@ -443,6 +443,26 @@
return splitStates;
}
+ // TODO(b/317358445): Makes ActivityStack and SplitInfo callback more stable.
+ /**
+ * Returns a list of currently active {@link ActivityStack activityStacks}.
+ *
+ * @return a list of {@link ActivityStack activityStacks} if all the containers are in
+ * a stable state, or {@code null} otherwise.
+ */
+ @Nullable
+ List<ActivityStack> getActivityStacksIfStable() {
+ final List<ActivityStack> activityStacks = new ArrayList<>();
+ for (TaskFragmentContainer container : mContainers) {
+ final ActivityStack activityStack = container.toActivityStackIfStable();
+ if (activityStack == null) {
+ return null;
+ }
+ activityStacks.add(activityStack);
+ }
+ return activityStacks;
+ }
+
/** A wrapper class which contains the information of {@link TaskContainer} */
static final class TaskProperties {
private final int mDisplayId;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index afd554b..810bded 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -107,11 +107,11 @@
private final String mOverlayTag;
/**
- * The launch options that was used to create this container. Must not be {@code null} for
- * {@link #isOverlay()} container.
+ * The launch options that was used to create this container. Must not {@link Bundle#isEmpty()}
+ * for {@link #isOverlay()} container.
*/
- @Nullable
- private final Bundle mLaunchOptions;
+ @NonNull
+ private final Bundle mLaunchOptions = new Bundle();
/** Indicates whether the container was cleaned up after the last activity was removed. */
private boolean mIsFinished;
@@ -210,7 +210,9 @@
if (overlayTag != null) {
Objects.requireNonNull(launchOptions);
}
- mLaunchOptions = launchOptions;
+ if (launchOptions != null) {
+ mLaunchOptions.putAll(launchOptions);
+ }
if (pairedPrimaryContainer != null) {
// The TaskFragment will be positioned right above the paired container.
@@ -925,6 +927,17 @@
return mOverlayTag;
}
+ /**
+ * Returns the options that was used to launch this {@link TaskFragmentContainer}.
+ * {@link Bundle#isEmpty()} means there's no launch option for this container.
+ * <p>
+ * Note that WM Jetpack owns the logic. The WM Extension library must not modify this object.
+ */
+ @NonNull
+ Bundle getLaunchOptions() {
+ return mLaunchOptions;
+ }
+
@Override
public String toString() {
return toString(true /* includeContainersToFinishOnExit */);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 678bdef..5ef6a52 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -16,6 +16,7 @@
package androidx.window.extensions.embedding;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import static androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_OVERLAY_TAG;
@@ -287,10 +288,10 @@
createOrUpdateOverlayTaskFragmentIfNeeded("test");
verify(mSplitPresenter).resizeTaskFragment(mTransaction, overlayToken, new Rect());
- verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayToken,
+ verify(mSplitPresenter).updateWindowingMode(mTransaction, overlayToken,
+ WINDOWING_MODE_UNDEFINED);
+ verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayContainer,
false);
- assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
- .containsExactly(overlayContainer);
}
@Test
@@ -315,8 +316,10 @@
createOrUpdateOverlayTaskFragmentIfNeeded("test");
verify(mSplitPresenter).resizeTaskFragment(mTransaction, overlayToken, new Rect());
- verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction, overlayToken,
- false);
+ verify(mSplitPresenter).updateWindowingMode(mTransaction,
+ overlayToken, WINDOWING_MODE_UNDEFINED);
+ verify(mSplitPresenter).setTaskFragmentIsolatedNavigation(mTransaction,
+ overlayContainer, false);
assertThat(mSplitController.getAllOverlayTaskFragmentContainers())
.containsExactly(overlayContainer);
}
@@ -425,6 +428,50 @@
.that(taskContainer.getTaskFragmentContainers()).isEmpty();
}
+ @Test
+ public void testUpdateActivityStackAttributes_nullParams_throwException() {
+ assertThrows(NullPointerException.class, () ->
+ mSplitController.updateActivityStackAttributes(null,
+ new ActivityStackAttributes.Builder().build()));
+
+ assertThrows(NullPointerException.class, () ->
+ mSplitController.updateActivityStackAttributes(new Binder(), null));
+
+ verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any());
+ }
+
+ @Test
+ public void testUpdateActivityStackAttributes_nullContainer_earlyReturn() {
+ final TaskFragmentContainer container = mSplitController.newContainer(mActivity,
+ mActivity.getTaskId());
+ mSplitController.updateActivityStackAttributes(container.getTaskFragmentToken(),
+ new ActivityStackAttributes.Builder().build());
+
+ verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any());
+ }
+
+ @Test
+ public void testUpdateActivityStackAttributes_notOverlay_earlyReturn() {
+ final TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity);
+
+ mSplitController.updateActivityStackAttributes(container.getTaskFragmentToken(),
+ new ActivityStackAttributes.Builder().build());
+
+ verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any());
+ }
+
+ @Test
+ public void testUpdateActivityStackAttributes() {
+ final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, "test");
+ doNothing().when(mSplitPresenter).applyActivityStackAttributes(any(), any(), any());
+ final ActivityStackAttributes attrs = new ActivityStackAttributes.Builder().build();
+ final IBinder token = container.getTaskFragmentToken();
+
+ mSplitController.updateActivityStackAttributes(token, attrs);
+
+ verify(mSplitPresenter).applyActivityStackAttributes(any(), eq(container), eq(attrs));
+ }
+
/**
* A simplified version of {@link SplitController.ActivityStartMonitor
* #createOrUpdateOverlayTaskFragmentIfNeeded}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index bab4e91..b60943a 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -354,7 +354,7 @@
bundle.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN,
container.getTaskFragmentToken());
monitor.mCurrentIntent = intent;
- doReturn(container).when(mSplitController).getContainer(any());
+ doReturn(container).when(mSplitController).getContainer(any(IBinder.class));
monitor.onStartActivityResult(START_CANCELED, bundle);
assertNull(container.getPendingAppearedIntent());
@@ -1642,7 +1642,7 @@
// We need to set those in case we are not respecting clear top.
// TODO(b/231845476) we should always respect clearTop.
final int windowingMode = mSplitController.getTaskContainer(primaryContainer.getTaskId())
- .getWindowingModeForSplitTaskFragment(TASK_BOUNDS);
+ .getWindowingModeForTaskFragment(TASK_BOUNDS);
primaryContainer.setLastRequestedWindowingMode(windowingMode);
secondaryContainer.setLastRequestedWindowingMode(windowingMode);
primaryContainer.setLastRequestedBounds(getSplitBounds(true /* isPrimary */));
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index 7b77235..a5995a3 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -75,7 +75,7 @@
final Configuration configuration = new Configuration();
assertEquals(WINDOWING_MODE_MULTI_WINDOW,
- taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
+ taskContainer.getWindowingModeForTaskFragment(splitBounds));
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
@@ -83,7 +83,7 @@
null /* decorSurface */));
assertEquals(WINDOWING_MODE_MULTI_WINDOW,
- taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
+ taskContainer.getWindowingModeForTaskFragment(splitBounds));
configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
@@ -91,12 +91,12 @@
null /* decorSurface */));
assertEquals(WINDOWING_MODE_FREEFORM,
- taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
+ taskContainer.getWindowingModeForTaskFragment(splitBounds));
// Empty bounds means the split pair are stacked, so it should be UNDEFINED which will then
// inherit the Task windowing mode
assertEquals(WINDOWING_MODE_UNDEFINED,
- taskContainer.getWindowingModeForSplitTaskFragment(new Rect()));
+ taskContainer.getWindowingModeForTaskFragment(new Rect()));
}
@Test
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 662a5c4..a76bd26 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -95,7 +95,6 @@
private int mMinimumFlyoutWidthLargeScreen;
private PointF mRestingStackPosition;
- private int[] mPaddings = new int[4];
private boolean mShowingInBubbleBar;
private final Point mBubbleBarPosition = new Point();
@@ -344,46 +343,44 @@
final int pointerTotalHeight = getPointerSize();
final int expandedViewLargeScreenInsetFurthestEdge =
getExpandedViewLargeScreenInsetFurthestEdge(isOverflow);
+ int[] paddings = new int[4];
if (mDeviceConfig.isLargeScreen()) {
// Note:
// If we're in portrait OR if we're a small tablet, then the two insets values will
// be equal. If we're landscape and a large tablet, the two values will be different.
// [left, top, right, bottom]
- mPaddings[0] = onLeft
+ paddings[0] = onLeft
? mExpandedViewLargeScreenInsetClosestEdge - pointerTotalHeight
: expandedViewLargeScreenInsetFurthestEdge;
- mPaddings[1] = 0;
- mPaddings[2] = onLeft
+ paddings[1] = 0;
+ paddings[2] = onLeft
? expandedViewLargeScreenInsetFurthestEdge
: mExpandedViewLargeScreenInsetClosestEdge - pointerTotalHeight;
// Overflow doesn't show manage button / get padding from it so add padding here
- mPaddings[3] = isOverflow ? mExpandedViewPadding : 0;
- return mPaddings;
+ paddings[3] = isOverflow ? mExpandedViewPadding : 0;
+ return paddings;
} else {
int leftPadding = mInsets.left + mExpandedViewPadding;
int rightPadding = mInsets.right + mExpandedViewPadding;
- final float expandedViewWidth = isOverflow
- ? mOverflowWidth
- : mExpandedViewLargeScreenWidth;
if (showBubblesVertically()) {
if (!onLeft) {
rightPadding += mBubbleSize - pointerTotalHeight;
leftPadding += isOverflow
- ? (mPositionRect.width() - rightPadding - expandedViewWidth)
+ ? (mPositionRect.width() - rightPadding - mOverflowWidth)
: 0;
} else {
leftPadding += mBubbleSize - pointerTotalHeight;
rightPadding += isOverflow
- ? (mPositionRect.width() - leftPadding - expandedViewWidth)
+ ? (mPositionRect.width() - leftPadding - mOverflowWidth)
: 0;
}
}
// [left, top, right, bottom]
- mPaddings[0] = leftPadding;
- mPaddings[1] = showBubblesVertically() ? 0 : mPointerMargin;
- mPaddings[2] = rightPadding;
- mPaddings[3] = 0;
- return mPaddings;
+ paddings[0] = leftPadding;
+ paddings[1] = showBubblesVertically() ? 0 : mPointerMargin;
+ paddings[2] = rightPadding;
+ paddings[3] = 0;
+ return paddings;
}
}
@@ -395,7 +392,7 @@
}
/** Gets the y position of the expanded view if it was top-aligned. */
- public float getExpandedViewYTopAligned() {
+ public int getExpandedViewYTopAligned() {
final int top = getAvailableRect().top;
if (showBubblesVertically()) {
return top - mPointerWidth + mExpandedViewPadding;
@@ -413,7 +410,7 @@
return getExpandedViewHeightForLargeScreen();
}
// Subtract top insets because availableRect.height would account for that
- int expandedContainerY = (int) getExpandedViewYTopAligned() - getInsets().top;
+ int expandedContainerY = getExpandedViewYTopAligned() - getInsets().top;
int paddingTop = showBubblesVertically()
? 0
: mPointerHeight;
@@ -474,11 +471,11 @@
public float getExpandedViewY(BubbleViewProvider bubble, float bubblePosition) {
boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey());
float expandedViewHeight = getExpandedViewHeight(bubble);
- float topAlignment = getExpandedViewYTopAligned();
+ int topAlignment = getExpandedViewYTopAligned();
int manageButtonHeight =
isOverflow ? mExpandedViewPadding : mManageButtonHeightIncludingMargins;
- // On largescreen portrait bubbles are bottom aligned.
+ // On large screen portrait bubbles are bottom aligned.
if (areBubblesBottomAligned() && expandedViewHeight == MAX_HEIGHT) {
return mPositionRect.bottom - manageButtonHeight
- getExpandedViewHeightForLargeScreen() - mPointerWidth;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
index 02af2d0..7798aa7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java
@@ -420,7 +420,7 @@
bubbleView.setTranslationY(y);
}
- final float expandedY = mPositioner.getExpandedViewYTopAligned();
+ final int expandedY = mPositioner.getExpandedViewYTopAligned();
final boolean draggedOutEnough =
y > expandedY + mBubbleSizePx || y < expandedY - mBubbleSizePx;
if (draggedOutEnough != mBubbleDraggedOutEnough) {
diff --git a/libs/incident/libincident.map.txt b/libs/incident/libincident.map.txt
index f75ccea..d8650e1 100644
--- a/libs/incident/libincident.map.txt
+++ b/libs/incident/libincident.map.txt
@@ -1,15 +1,15 @@
LIBINCIDENT {
global:
- AIncidentReportArgs_init; # systemapi # introduced=30
- AIncidentReportArgs_clone; # systemapi # introduced=30
- AIncidentReportArgs_delete; # systemapi # introduced=30
- AIncidentReportArgs_setAll; # systemapi # introduced=30
- AIncidentReportArgs_setPrivacyPolicy; # systemapi # introduced=30
- AIncidentReportArgs_addSection; # systemapi # introduced=30
- AIncidentReportArgs_setReceiverPackage; # systemapi # introduced=30
- AIncidentReportArgs_setReceiverClass; # systemapi # introduced=30
- AIncidentReportArgs_addHeader; # systemapi # introduced=30
- AIncidentReportArgs_takeReport; # systemapi # introduced=30
+ AIncidentReportArgs_init; # systemapi introduced=30
+ AIncidentReportArgs_clone; # systemapi introduced=30
+ AIncidentReportArgs_delete; # systemapi introduced=30
+ AIncidentReportArgs_setAll; # systemapi introduced=30
+ AIncidentReportArgs_setPrivacyPolicy; # systemapi introduced=30
+ AIncidentReportArgs_addSection; # systemapi introduced=30
+ AIncidentReportArgs_setReceiverPackage; # systemapi introduced=30
+ AIncidentReportArgs_setReceiverClass; # systemapi introduced=30
+ AIncidentReportArgs_addHeader; # systemapi introduced=30
+ AIncidentReportArgs_takeReport; # systemapi introduced=30
local:
*;
};
diff --git a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
index 3b15632..ce1004c 100644
--- a/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
+++ b/media/tests/LoudnessCodecApiTest/src/com/android/loudnesscodecapitest/LoudnessCodecConfiguratorTest.java
@@ -95,12 +95,17 @@
@RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
public void setAudioTrack_callsAudioServiceStart() throws Exception {
final AudioTrack track = createAudioTrack();
+ final MediaCodec mediaCodec = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(createAndConfigureMediaCodec());
- mLcc.setAudioTrack(track);
+ try {
+ mLcc.addMediaCodec(mediaCodec);
+ mLcc.setAudioTrack(track);
- verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
- anyList());
+ verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
+ anyList());
+ } finally {
+ mediaCodec.release();
+ }
}
@Test
@@ -108,10 +113,15 @@
public void getLoudnessCodecParams_callsAudioServiceGetLoudness() throws Exception {
when(mAudioService.getLoudnessParams(anyInt(), any())).thenReturn(new PersistableBundle());
final AudioTrack track = createAudioTrack();
+ final MediaCodec mediaCodec = createAndConfigureMediaCodec();
- mLcc.getLoudnessCodecParams(track, createAndConfigureMediaCodec());
+ try {
+ mLcc.getLoudnessCodecParams(track, mediaCodec);
- verify(mAudioService).getLoudnessParams(eq(track.getPlayerIId()), any());
+ verify(mAudioService).getLoudnessParams(eq(track.getPlayerIId()), any());
+ } finally {
+ mediaCodec.release();
+ }
}
@Test
@@ -120,10 +130,14 @@
final AudioTrack track = createAudioTrack();
final MediaCodec mediaCodec = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(mediaCodec);
- mLcc.setAudioTrack(track);
+ try {
+ mLcc.addMediaCodec(mediaCodec);
+ mLcc.setAudioTrack(track);
- verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
+ verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
+ } finally {
+ mediaCodec.release();
+ }
}
@Test
@@ -132,24 +146,33 @@
final AudioTrack track = createAudioTrack();
final MediaCodec mediaCodec = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(mediaCodec);
- mLcc.setAudioTrack(track);
- mLcc.setAudioTrack(track);
+ try {
+ mLcc.addMediaCodec(mediaCodec);
+ mLcc.setAudioTrack(track);
+ mLcc.setAudioTrack(track);
- verify(mAudioService, times(1)).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
- anyList());
+ verify(mAudioService, times(1)).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
+ anyList());
+ } finally {
+ mediaCodec.release();
+ }
}
@Test
@RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
public void setTrackNull_stopCodecUpdates() throws Exception {
final AudioTrack track = createAudioTrack();
+ final MediaCodec mediaCodec = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(createAndConfigureMediaCodec());
- mLcc.setAudioTrack(track);
+ try {
+ mLcc.addMediaCodec(mediaCodec);
+ mLcc.setAudioTrack(track);
- mLcc.setAudioTrack(null); // stops updates
- verify(mAudioService).stopLoudnessCodecUpdates(eq(track.getPlayerIId()));
+ mLcc.setAudioTrack(null); // stops updates
+ verify(mAudioService).stopLoudnessCodecUpdates(eq(track.getPlayerIId()));
+ } finally {
+ mediaCodec.release();
+ }
}
@Test
@@ -157,27 +180,37 @@
public void addMediaCodecTwice_triggersIAE() throws Exception {
final MediaCodec mediaCodec = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(mediaCodec);
+ try {
+ mLcc.addMediaCodec(mediaCodec);
- assertThrows(IllegalArgumentException.class, () -> mLcc.addMediaCodec(mediaCodec));
+ assertThrows(IllegalArgumentException.class, () -> mLcc.addMediaCodec(mediaCodec));
+ } finally {
+ mediaCodec.release();
+ }
}
@Test
@RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
public void setClearTrack_removeAllAudioServicePiidCodecs() throws Exception {
final ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
-
final AudioTrack track = createAudioTrack();
+ final MediaCodec mediaCodec1 = createAndConfigureMediaCodec();
+ final MediaCodec mediaCodec2 = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(createAndConfigureMediaCodec());
- mLcc.setAudioTrack(track);
- verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
- argument.capture());
- assertEquals(argument.getValue().size(), 1);
+ try {
+ mLcc.addMediaCodec(mediaCodec1);
+ mLcc.setAudioTrack(track);
+ verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()),
+ argument.capture());
+ assertEquals(argument.getValue().size(), 1);
- mLcc.addMediaCodec(createAndConfigureMediaCodec());
- mLcc.setAudioTrack(null);
- verify(mAudioService).stopLoudnessCodecUpdates(eq(track.getPlayerIId()));
+ mLcc.addMediaCodec(mediaCodec2);
+ mLcc.setAudioTrack(null);
+ verify(mAudioService).stopLoudnessCodecUpdates(eq(track.getPlayerIId()));
+ } finally {
+ mediaCodec1.release();
+ mediaCodec2.release();
+ }
}
@Test
@@ -186,24 +219,35 @@
final AudioTrack track = createAudioTrack();
final MediaCodec mediaCodec = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(mediaCodec);
- mLcc.setAudioTrack(track);
- mLcc.removeMediaCodec(mediaCodec);
+ try {
+ mLcc.addMediaCodec(mediaCodec);
+ mLcc.setAudioTrack(track);
+ mLcc.removeMediaCodec(mediaCodec);
- verify(mAudioService).removeLoudnessCodecInfo(eq(track.getPlayerIId()), any());
+ verify(mAudioService).removeLoudnessCodecInfo(eq(track.getPlayerIId()), any());
+ } finally {
+ mediaCodec.release();
+ }
}
@Test
@RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
public void addMediaCodecAfterSetTrack_callsAudioServiceAdd() throws Exception {
final AudioTrack track = createAudioTrack();
+ final MediaCodec mediaCodec1 = createAndConfigureMediaCodec();
+ final MediaCodec mediaCodec2 = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(createAndConfigureMediaCodec());
- mLcc.setAudioTrack(track);
- verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
+ try {
+ mLcc.addMediaCodec(mediaCodec1);
+ mLcc.setAudioTrack(track);
+ verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
- mLcc.addMediaCodec(createAndConfigureMediaCodec());
- verify(mAudioService).addLoudnessCodecInfo(eq(track.getPlayerIId()), anyInt(), any());
+ mLcc.addMediaCodec(mediaCodec2);
+ verify(mAudioService).addLoudnessCodecInfo(eq(track.getPlayerIId()), anyInt(), any());
+ } finally {
+ mediaCodec1.release();
+ mediaCodec2.release();
+ }
}
@Test
@@ -212,25 +256,36 @@
final AudioTrack track = createAudioTrack();
final MediaCodec mediaCodec = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(mediaCodec);
- mLcc.setAudioTrack(track);
- verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
+ try {
+ mLcc.addMediaCodec(mediaCodec);
+ mLcc.setAudioTrack(track);
+ verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
- mLcc.removeMediaCodec(mediaCodec);
- verify(mAudioService).removeLoudnessCodecInfo(eq(track.getPlayerIId()), any());
+ mLcc.removeMediaCodec(mediaCodec);
+ verify(mAudioService).removeLoudnessCodecInfo(eq(track.getPlayerIId()), any());
+ } finally {
+ mediaCodec.release();
+ }
}
@Test
@RequiresFlagsEnabled(FLAG_LOUDNESS_CONFIGURATOR_API)
public void removeWrongMediaCodecAfterSetTrack_triggersIAE() throws Exception {
final AudioTrack track = createAudioTrack();
+ final MediaCodec mediaCodec1 = createAndConfigureMediaCodec();
+ final MediaCodec mediaCodec2 = createAndConfigureMediaCodec();
- mLcc.addMediaCodec(createAndConfigureMediaCodec());
- mLcc.setAudioTrack(track);
- verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
+ try {
+ mLcc.addMediaCodec(mediaCodec1);
+ mLcc.setAudioTrack(track);
+ verify(mAudioService).startLoudnessCodecUpdates(eq(track.getPlayerIId()), anyList());
- assertThrows(IllegalArgumentException.class,
- () -> mLcc.removeMediaCodec(createAndConfigureMediaCodec()));
+ assertThrows(IllegalArgumentException.class,
+ () -> mLcc.removeMediaCodec(mediaCodec2));
+ } finally {
+ mediaCodec1.release();
+ mediaCodec2.release();
+ }
}
private static AudioTrack createAudioTrack() {
@@ -250,19 +305,21 @@
MediaExtractor extractor;
extractor = new MediaExtractor();
- extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+ try {
+ extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
testFd.getLength());
- testFd.close();
+ assertEquals("wrong number of tracks", 1, extractor.getTrackCount());
+ MediaFormat format = extractor.getTrackFormat(0);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ assertTrue("not an audio file", mime.startsWith(TEST_MEDIA_AUDIO_CODEC_PREFIX));
+ final MediaCodec mediaCodec = MediaCodec.createDecoderByType(mime);
- assertEquals("wrong number of tracks", 1, extractor.getTrackCount());
- MediaFormat format = extractor.getTrackFormat(0);
- String mime = format.getString(MediaFormat.KEY_MIME);
- assertTrue("not an audio file", mime.startsWith(TEST_MEDIA_AUDIO_CODEC_PREFIX));
- final MediaCodec mediaCodec = MediaCodec.createDecoderByType(mime);
-
- Log.v(TAG, "configuring with " + format);
- mediaCodec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
-
- return mediaCodec;
+ Log.v(TAG, "configuring with " + format);
+ mediaCodec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
+ return mediaCodec;
+ } finally {
+ testFd.close();
+ extractor.release();
+ }
}
}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 9f2a9ac..9605108 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -1,9 +1,9 @@
LIBANDROID {
global:
- AActivityManager_addUidImportanceListener; # systemapi # introduced=31
- AActivityManager_removeUidImportanceListener; # systemapi # introduced=31
- AActivityManager_isUidActive; # systemapi # introduced=31
- AActivityManager_getUidImportance; # systemapi # introduced=31
+ AActivityManager_addUidImportanceListener; # systemapi introduced=31
+ AActivityManager_removeUidImportanceListener; # systemapi introduced=31
+ AActivityManager_isUidActive; # systemapi introduced=31
+ AActivityManager_getUidImportance; # systemapi introduced=31
AAssetDir_close;
AAssetDir_getNextFileName;
AAssetDir_rewind;
diff --git a/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml b/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml
new file mode 100644
index 0000000..9d16f32d
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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.
+ -->
+
+<!-- Copied from //frameworks/base/core/res/res/drawable/item_background_material.xml -->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/autofill_light_colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <color android:color="@android:color/white"/>
+ </item>
+</ripple>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml
new file mode 100644
index 0000000..2f0c83b
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools" tools:ignore="NewApi"
+ android:color="@android:color/transparent">
+ <item
+ android:bottom="1dp"
+ android:shape="rectangle"
+ android:top="1dp">
+ <shape>
+ <corners android:radius="28dp" />
+ <solid android:color="@android:color/system_surface_container_high_light" />
+ </shape>
+ </item>
+</ripple>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one_dark.xml b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one_dark.xml
new file mode 100644
index 0000000..39f49ca
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one_dark.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools" tools:ignore="NewApi"
+ android:color="@android:color/transparent">
+ <item
+ android:bottom="1dp"
+ android:shape="rectangle"
+ android:top="1dp">
+ <shape>
+ <corners android:radius="28dp" />
+ <solid android:color="@android:color/system_surface_container_high_dark" />
+ </shape>
+ </item>
+</ripple>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml b/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml
new file mode 100644
index 0000000..e4e9f7a
--- /dev/null
+++ b/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml
@@ -0,0 +1,37 @@
+<!--
+ ~ 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.
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/autofill.Dataset">
+ <ImageView
+ android:id="@android:id/icon1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_alignParentStart="true"
+ android:background="@null"/>
+ <TextView
+ android:id="@android:id/text1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_toEndOf="@android:id/icon1"
+ style="@style/autofill.TextAppearance"/>
+
+</RelativeLayout>
diff --git a/packages/CredentialManager/res/values/colors.xml b/packages/CredentialManager/res/values/colors.xml
new file mode 100644
index 0000000..63b9f24
--- /dev/null
+++ b/packages/CredentialManager/res/values/colors.xml
@@ -0,0 +1,38 @@
+<!--
+ ~ 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.
+ -->
+
+<!-- Color palette -->
+<resources>
+ <color name="autofill_light_colorPrimary">@color/primary_material_light</color>
+ <color name="autofill_light_colorAccent">@color/accent_material_light</color>
+ <color name="autofill_light_colorControlHighlight">@color/ripple_material_light</color>
+ <color name="autofill_light_colorButtonNormal">@color/button_material_light</color>
+
+ <!-- Text colors -->
+ <color name="autofill_light_textColorPrimary">@color/abc_primary_text_material_light</color>
+ <color name="autofill_light_textColorSecondary">@color/abc_secondary_text_material_light</color>
+ <color name="autofill_light_textColorHint">@color/abc_hint_foreground_material_light</color>
+ <color name="autofill_light_textColorHintInverse">@color/abc_hint_foreground_material_dark
+ </color>
+ <color name="autofill_light_textColorHighlight">@color/highlighted_text_material_light</color>
+ <color name="autofill_light_textColorLink">@color/autofill_light_colorAccent</color>
+
+ <!-- These colors are used for Remote Views. -->
+ <color name="background_dark_mode">#0E0C0B</color>
+ <color name="background">#F1F3F4</color>
+ <color name="text_primary_dark_mode">#DFDEDB</color>
+ <color name="text_primary">#202124</color>
+</resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/values/dimens.xml b/packages/CredentialManager/res/values/dimens.xml
new file mode 100644
index 0000000..67003a3
--- /dev/null
+++ b/packages/CredentialManager/res/values/dimens.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <dimen name="autofill_view_padding">16dp</dimen>
+ <dimen name="autofill_icon_size">16dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/res/values/styles.xml b/packages/CredentialManager/res/values/styles.xml
new file mode 100644
index 0000000..4a5761a
--- /dev/null
+++ b/packages/CredentialManager/res/values/styles.xml
@@ -0,0 +1,38 @@
+<!--
+ ~ 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.
+ -->
+
+<resources>
+ <style name="autofill.TextAppearance.Small" parent="@style/autofill.TextAppearance">
+ <item name="android:textSize">12sp</item>
+ </style>
+
+
+ <style name="autofill.Dataset" parent="">
+ <item name="android:background">@drawable/autofill_light_selectable_item_background</item>
+ </style>
+
+ <style name="autofill.TextAppearance" parent="">
+ <item name="android:textColor">@color/autofill_light_textColorPrimary</item>
+ <item name="android:textColorHint">@color/autofill_light_textColorHint</item>
+ <item name="android:textColorHighlight">@color/autofill_light_textColorHighlight</item>
+ <item name="android:textColorLink">@color/autofill_light_textColorLink</item>
+ <item name="android:textSize">14sp</item>
+ </style>
+
+ <style name="autofill.TextAppearance.Primary">
+ <item name="android:textColor">@color/autofill_light_textColorPrimary</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 20d2f09..0ff1c7f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -16,6 +16,7 @@
package com.android.credentialmanager.autofill
+import android.R
import android.app.assist.AssistStructure
import android.content.Context
import android.credentials.CredentialManager
@@ -41,18 +42,19 @@
import android.service.credentials.CredentialProviderService
import android.util.Log
import android.view.autofill.AutofillId
-import org.json.JSONException
import android.widget.inline.InlinePresentationSpec
import androidx.autofill.inline.v1.InlineSuggestionUi
import androidx.credentials.provider.CustomCredentialEntry
import androidx.credentials.provider.PasswordCredentialEntry
import androidx.credentials.provider.PublicKeyCredentialEntry
import com.android.credentialmanager.GetFlowUtils
-import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.common.ui.RemoteViewsFactory
import com.android.credentialmanager.getflow.ProviderDisplayInfo
-import com.android.credentialmanager.model.get.ProviderInfo
import com.android.credentialmanager.getflow.toProviderDisplayInfo
import com.android.credentialmanager.ktx.credentialEntry
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import com.android.credentialmanager.model.get.ProviderInfo
+import org.json.JSONException
import org.json.JSONObject
import java.util.concurrent.Executors
@@ -127,9 +129,11 @@
is PasswordCredentialEntry -> {
entryIconMap[entry.key + entry.subkey] = credentialEntry.icon
}
+
is PublicKeyCredentialEntry -> {
entryIconMap[entry.key + entry.subkey] = credentialEntry.icon
}
+
is CustomCredentialEntry -> {
entryIconMap[entry.key + entry.subkey] = credentialEntry.icon
}
@@ -172,11 +176,11 @@
}
private fun processProvidersForAutofillId(
- filLRequest: FillRequest,
- autofillId: AutofillId,
- providerList: List<ProviderInfo>,
- entryIconMap: Map<String, Icon>,
- fillResponseBuilder: FillResponse.Builder
+ filLRequest: FillRequest,
+ autofillId: AutofillId,
+ providerList: List<ProviderInfo>,
+ entryIconMap: Map<String, Icon>,
+ fillResponseBuilder: FillResponse.Builder
): Boolean {
if (providerList.isEmpty()) {
return false
@@ -197,7 +201,7 @@
var i = 0
var datasetAdded = false
- providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@ {
+ providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@{
val primaryEntry = it.sortedCredentialEntryList.first()
val pendingIntent = primaryEntry.pendingIntent
val fillInIntent = primaryEntry.fillInIntent
@@ -206,37 +210,48 @@
Log.e(TAG, "PendingIntent was missing from the entry.")
return@usernameLoop
}
- if (inlinePresentationSpecs == null || i >= maxItemCount) {
+ if (inlinePresentationSpecs == null) {
+ Log.i(TAG, "Inline presentation spec is null, " +
+ "building dropdown presentation only")
+ }
+ if (i >= maxItemCount) {
Log.e(TAG, "Skipping because reached the max item count.")
return@usernameLoop
}
- // Create inline presentation
- val spec: InlinePresentationSpec
- if (i < inlinePresentationSpecsCount) {
- spec = inlinePresentationSpecs[i]
- } else {
- spec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1]
- }
- val sliceBuilder = InlineSuggestionUi
- .newContentBuilder(pendingIntent)
- .setTitle(primaryEntry.userName)
- val icon: Icon
- if (primaryEntry.icon == null) {
+ val icon: Icon = if (primaryEntry.icon == null) {
// The empty entry icon has non-null icon reference but null drawable reference.
// If the drawable reference is null, then use the default icon.
- icon = getDefaultIcon()
+ getDefaultIcon()
} else {
- icon = entryIconMap[primaryEntry.entryKey + primaryEntry.entrySubkey]
+ entryIconMap[primaryEntry.entryKey + primaryEntry.entrySubkey]
?: getDefaultIcon()
}
- sliceBuilder.setStartIcon(icon)
- val inlinePresentation = InlinePresentation(
- sliceBuilder.build().slice, spec, /* pinned= */ false)
+ // Create inline presentation
+ var inlinePresentation: InlinePresentation? = null;
+ if (inlinePresentationSpecs != null) {
+ val spec: InlinePresentationSpec
+ if (i < inlinePresentationSpecsCount) {
+ spec = inlinePresentationSpecs[i]
+ } else {
+ spec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1]
+ }
+ val sliceBuilder = InlineSuggestionUi
+ .newContentBuilder(pendingIntent)
+ .setTitle(primaryEntry.userName)
+ sliceBuilder.setStartIcon(icon)
+ inlinePresentation = InlinePresentation(
+ sliceBuilder.build().slice, spec, /* pinned= */ false)
+ }
+ val dropdownPresentation = RemoteViewsFactory.createDropdownPresentation(
+ this, icon, primaryEntry)
i++
val dataSetBuilder = Dataset.Builder()
val presentationBuilder = Presentations.Builder()
- .setInlinePresentation(inlinePresentation)
+ .setMenuPresentation(dropdownPresentation)
+ if (inlinePresentation != null) {
+ presentationBuilder.setInlinePresentation(inlinePresentation)
+ }
fillResponseBuilder.addDataset(
dataSetBuilder
@@ -305,7 +320,7 @@
): MutableMap<AutofillId, MutableList<CredentialEntryInfo>> {
val autofillIdToCredentialEntries:
MutableMap<AutofillId, MutableList<CredentialEntryInfo>> = mutableMapOf()
- credentialEntryList.forEach entryLoop@ { credentialEntry ->
+ credentialEntryList.forEach entryLoop@{ credentialEntry ->
val autofillId: AutofillId? = credentialEntry
.fillInIntent
?.getParcelableExtra(
@@ -323,8 +338,8 @@
}
private fun copyProviderInfo(
- providerInfo: ProviderInfo,
- credentialList: List<CredentialEntryInfo>
+ providerInfo: ProviderInfo,
+ credentialList: List<CredentialEntryInfo>
): ProviderInfo {
return ProviderInfo(
providerInfo.id,
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt
new file mode 100644
index 0000000..4dc7f00
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt
@@ -0,0 +1,88 @@
+/*
+ * 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 com.android.credentialmanager.common.ui
+
+import android.content.Context
+import android.content.res.Configuration
+import android.widget.RemoteViews
+import androidx.core.content.ContextCompat
+import com.android.credentialmanager.model.get.CredentialEntryInfo
+import android.graphics.drawable.Icon
+
+class RemoteViewsFactory {
+
+ companion object {
+ private const val setAdjustViewBoundsMethodName = "setAdjustViewBounds"
+ private const val setMaxHeightMethodName = "setMaxHeight"
+ private const val setBackgroundResourceMethodName = "setBackgroundResource"
+
+ fun createDropdownPresentation(
+ context: Context,
+ icon: Icon,
+ credentialEntryInfo: CredentialEntryInfo
+ ): RemoteViews {
+ val padding = context.resources.getDimensionPixelSize(com.android
+ .credentialmanager.R.dimen.autofill_view_padding)
+ var layoutId: Int = com.android.credentialmanager.R.layout
+ .autofill_dataset_left_with_item_tag_hint
+ val remoteViews = RemoteViews(context.packageName, layoutId)
+ setRemoteViewsPaddings(remoteViews, padding)
+ val textColorPrimary = getTextColorPrimary(isDarkMode(context), context);
+ remoteViews.setTextColor(android.R.id.text1, textColorPrimary);
+ remoteViews.setTextViewText(android.R.id.text1, credentialEntryInfo.userName)
+
+ remoteViews.setImageViewIcon(android.R.id.icon1, icon);
+ remoteViews.setBoolean(
+ android.R.id.icon1, setAdjustViewBoundsMethodName, true);
+ remoteViews.setInt(
+ android.R.id.icon1,
+ setMaxHeightMethodName,
+ context.resources.getDimensionPixelSize(
+ com.android.credentialmanager.R.dimen.autofill_icon_size));
+ val drawableId = if (isDarkMode(context))
+ com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one_dark
+ else com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one
+ remoteViews.setInt(
+ android.R.id.content, setBackgroundResourceMethodName, drawableId);
+ return remoteViews
+ }
+
+ private fun setRemoteViewsPaddings(
+ remoteViews: RemoteViews,
+ padding: Int) {
+ val halfPadding = padding / 2
+ remoteViews.setViewPadding(
+ android.R.id.text1,
+ halfPadding,
+ halfPadding,
+ halfPadding,
+ halfPadding)
+ }
+
+ private fun isDarkMode(context: Context): Boolean {
+ val currentNightMode = context.resources.configuration.uiMode and
+ Configuration.UI_MODE_NIGHT_MASK
+ return currentNightMode == Configuration.UI_MODE_NIGHT_YES
+ }
+
+ private fun getTextColorPrimary(darkMode: Boolean, context: Context): Int {
+ return if (darkMode) ContextCompat.getColor(
+ context, com.android.credentialmanager.R.color.text_primary_dark_mode)
+ else ContextCompat.getColor(context, com.android.credentialmanager.R.color.text_primary)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
index 1ee58de..5e76801 100644
--- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
@@ -41,6 +41,7 @@
import com.android.systemui.util.settings.SecureSettings
import java.io.PrintWriter
import javax.inject.Inject
+import dagger.Lazy
/**
* Handles active unlock settings changes.
@@ -51,6 +52,7 @@
private val secureSettings: SecureSettings,
private val contentResolver: ContentResolver,
private val selectedUserInteractor: SelectedUserInteractor,
+ private val keyguardUpdateMonitor: Lazy<KeyguardUpdateMonitor>,
dumpManager: DumpManager
) : Dumpable {
@@ -96,7 +98,6 @@
UNDER_DISPLAY_FINGERPRINT(3),
}
- var keyguardUpdateMonitor: KeyguardUpdateMonitor? = null
private var requestActiveUnlockOnWakeup = false
private var requestActiveUnlockOnUnlockIntent = false
private var requestActiveUnlockOnBioFail = false
@@ -316,7 +317,7 @@
return false
}
- keyguardUpdateMonitor?.let {
+ keyguardUpdateMonitor.get().let {
val anyFaceEnrolled = it.isFaceEnabledAndEnrolled
val anyFingerprintEnrolled = it.isUnlockWithFingerprintPossible(
selectedUserInteractor.getSelectedUserId())
@@ -369,13 +370,13 @@
}")
pw.println("Current state:")
- keyguardUpdateMonitor?.let {
+ keyguardUpdateMonitor.get().let {
pw.println(" shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment=" +
"${shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()}")
pw.println(" isFaceEnabledAndEnrolled=${it.isFaceEnabledAndEnrolled}")
pw.println(" fpUnlockPossible=${
it.isUnlockWithFingerprintPossible(selectedUserInteractor.getSelectedUserId())}")
pw.println(" udfpsEnrolled=${it.isUdfpsEnrolled}")
- } ?: pw.println(" keyguardUpdateMonitor is uninitialized")
+ }
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9c61a8a..f3cd01f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -111,6 +111,7 @@
import com.android.settingslib.Utils;
import com.android.settingslib.WirelessUtils;
import com.android.settingslib.fuelgauge.BatteryStatus;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.FingerprintInteractiveToAuthProvider;
@@ -175,7 +176,7 @@
* to be updated.
*/
@SysUISingleton
-public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpable {
+public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpable, CoreStartable {
private static final String TAG = "KeyguardUpdateMonitor";
private static final int BIOMETRIC_LOCKOUT_RESET_DELAY_MS = 600;
@@ -347,6 +348,7 @@
private final LatencyTracker mLatencyTracker;
private final StatusBarStateController mStatusBarStateController;
private final Executor mBackgroundExecutor;
+ private final Executor mMainExecutor;
private final SensorPrivacyManager mSensorPrivacyManager;
private final ActiveUnlockConfig mActiveUnlockConfig;
private final IDreamManager mDreamManager;
@@ -354,7 +356,10 @@
@Nullable
private final FingerprintManager mFpm;
@Nullable
+ private final BiometricManager mBiometricManager;
+ @Nullable
private KeyguardFaceAuthInteractor mFaceAuthInteractor;
+ private final DevicePostureController mDevicePostureController;
private final TaskStackChangeListeners mTaskStackChangeListeners;
private final IActivityTaskManager mActivityTaskManager;
private final SelectedUserInteractor mSelectedUserInteractor;
@@ -370,7 +375,7 @@
private boolean mIsDreaming;
private boolean mLogoutEnabled;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- private FingerprintInteractiveToAuthProvider mFingerprintInteractiveToAuthProvider;
+ private final FingerprintInteractiveToAuthProvider mFingerprintInteractiveToAuthProvider;
/**
* Short delay before restarting fingerprint authentication after a successful try. This should
@@ -2105,6 +2110,7 @@
mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
mStrongAuthTracker = new StrongAuthTracker(context);
mBackgroundExecutor = backgroundExecutor;
+ mMainExecutor = mainExecutor;
mBroadcastDispatcher = broadcastDispatcher;
mInteractionJankMonitor = interactionJankMonitor;
mLatencyTracker = latencyTracker;
@@ -2126,7 +2132,7 @@
mDevicePolicyManager = devicePolicyManager;
mPackageManager = packageManager;
mFpm = fingerprintManager;
- mActiveUnlockConfig.setKeyguardUpdateMonitor(this);
+ mBiometricManager = biometricManager;
mConfigFaceAuthSupportedPosture = mContext.getResources().getInteger(
R.integer.config_face_auth_supported_posture);
mFaceWakeUpTriggersConfig = faceWakeUpTriggersConfig;
@@ -2134,10 +2140,14 @@
mContext.getResources().getStringArray(
R.array.config_fingerprint_listen_on_occluding_activity_packages))
.collect(Collectors.toSet());
+ mDevicePostureController = devicePostureController;
mTaskStackChangeListeners = taskStackChangeListeners;
mActivityTaskManager = activityTaskManagerService;
mSelectedUserInteractor = selectedUserInteractor;
+ mFingerprintInteractiveToAuthProvider = interactiveToAuthProvider.orElse(null);
+ mIsSystemUser = mUserManager.isSystemUser();
+
mHandler = new Handler(mainLooper) {
@Override
public void handleMessage(Message msg) {
@@ -2252,6 +2262,20 @@
}
};
+ mTimeFormatChangeObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ mHandler.sendMessage(mHandler.obtainMessage(
+ MSG_TIME_FORMAT_UPDATE,
+ Settings.System.getString(
+ mContext.getContentResolver(),
+ Settings.System.TIME_12_24)));
+ }
+ };
+ }
+
+ @Override
+ public void start() {
// Since device can't be un-provisioned, we only need to register a content observer
// to update mDeviceProvisioned when we are...
if (!mDeviceProvisioned) {
@@ -2297,7 +2321,7 @@
mHandler, UserHandle.ALL);
mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener);
- mUserTracker.addCallback(mUserChangedCallback, mainExecutor);
+ mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
mTrustManager.registerTrustListener(this);
@@ -2318,8 +2342,8 @@
mFpm.addLockoutResetCallback(mFingerprintLockoutResetCallback);
}
- if (biometricManager != null) {
- biometricManager.registerEnabledOnKeyguardCallback(mBiometricEnabledCallback);
+ if (mBiometricManager != null) {
+ mBiometricManager.registerEnabledOnKeyguardCallback(mBiometricEnabledCallback);
}
// in case authenticators aren't registered yet at this point:
@@ -2327,7 +2351,7 @@
@Override
public void onAllAuthenticatorsRegistered(
@BiometricAuthenticator.Modality int modality) {
- mainExecutor.execute(
+ mMainExecutor.execute(
() -> updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE));
}
@@ -2335,7 +2359,7 @@
public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
mHandler.obtainMessage(MSG_BIOMETRIC_ENROLLMENT_STATE_CHANGED, modality, 0)
.sendToTarget();
- mainExecutor.execute(
+ mMainExecutor.execute(
() -> updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE));
}
@@ -2353,12 +2377,11 @@
}
});
if (mConfigFaceAuthSupportedPosture != DEVICE_POSTURE_UNKNOWN) {
- devicePostureController.addCallback(mPostureCallback);
+ mDevicePostureController.addCallback(mPostureCallback);
}
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
mTaskStackChangeListeners.registerTaskStackListener(mTaskStackListener);
- mIsSystemUser = mUserManager.isSystemUser();
int user = mSelectedUserInteractor.getSelectedUserId(true);
mUserIsUnlocked.put(user, mUserManager.isUserUnlocked(user));
mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled();
@@ -2377,22 +2400,9 @@
mTelephonyListenerManager.addActiveDataSubscriptionIdListener(mPhoneStateListener);
initializeSimState();
- mTimeFormatChangeObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange) {
- mHandler.sendMessage(mHandler.obtainMessage(
- MSG_TIME_FORMAT_UPDATE,
- Settings.System.getString(
- mContext.getContentResolver(),
- Settings.System.TIME_12_24)));
- }
- };
-
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.TIME_12_24),
false, mTimeFormatChangeObserver, UserHandle.USER_ALL);
-
- mFingerprintInteractiveToAuthProvider = interactiveToAuthProvider.orElse(null);
}
private void initializeSimState() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 83d415f..ab23564 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -403,13 +403,6 @@
final BiometricPromptLayout view = (BiometricPromptLayout) layoutInflater.inflate(
R.layout.biometric_prompt_layout, null, false);
- /**
- * View is only set visible in BiometricViewSizeBinder once PromptSize is determined
- * that accounts for iconView size, to prevent prompt resizing being visible to the
- * user.
- * TODO(b/288175072): May be able to remove this once constraint layout is implemented
- */
- view.setVisibility(View.INVISIBLE);
mBiometricView = BiometricViewBinder.bind(view, viewModel, mPanelController,
// TODO(b/201510778): This uses the wrong timeout in some cases
getJankListener(view, TRANSIT,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index a7fb6f7..90e4a38 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -97,13 +97,7 @@
val iconOverlayView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon_overlay)
val iconView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon)
- /**
- * View is only set visible in BiometricViewSizeBinder once PromptSize is determined that
- * accounts for iconView size, to prevent prompt resizing being visible to the user.
- *
- * TODO(b/288175072): May be able to remove this once constraint layout is implemented
- */
- iconView.addLottieOnCompositionLoadedListener { viewModel.setIsIconViewLoaded(true) }
+
PromptIconViewBinder.bind(
iconView,
iconOverlayView,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index f340bd8..7e16d1e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -30,6 +30,7 @@
import androidx.core.view.doOnLayout
import androidx.core.view.isGone
import androidx.lifecycle.lifecycleScope
+import com.android.systemui.res.R
import com.android.systemui.biometrics.AuthPanelController
import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.ui.BiometricPromptLayout
@@ -40,8 +41,6 @@
import com.android.systemui.biometrics.ui.viewmodel.isNullOrNotSmall
import com.android.systemui.biometrics.ui.viewmodel.isSmall
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.res.R
-import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
/** Helper for [BiometricViewBinder] to handle resize transitions. */
@@ -93,22 +92,8 @@
// TODO(b/251476085): migrate the legacy panel controller and simplify this
view.repeatWhenAttached {
var currentSize: PromptSize? = null
-
lifecycleScope.launch {
- /**
- * View is only set visible in BiometricViewSizeBinder once PromptSize is
- * determined that accounts for iconView size, to prevent prompt resizing being
- * visible to the user.
- *
- * TODO(b/288175072): May be able to remove isIconViewLoaded once constraint
- * layout is implemented
- */
- combine(viewModel.isIconViewLoaded, viewModel.size, ::Pair).collect {
- (isIconViewLoaded, size) ->
- if (!isIconViewLoaded) {
- return@collect
- }
-
+ viewModel.size.collect { size ->
// prepare for animated size transitions
for (v in viewsToHideWhenSmall) {
v.showTextOrHide(forceHide = size.isSmall)
@@ -211,9 +196,8 @@
}
}
}
+
currentSize = size
- view.visibility = View.VISIBLE
- viewModel.setIsIconViewLoaded(false)
notifyAccessibilityChanged()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index d899827e..6d0a58e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -192,28 +192,6 @@
val iconViewModel: PromptIconViewModel =
PromptIconViewModel(this, displayStateInteractor, promptSelectorInteractor)
- private val _isIconViewLoaded = MutableStateFlow(false)
-
- /**
- * For prompts with an iconView, false until the prompt's iconView animation has been loaded in
- * the view, otherwise true by default. Used for BiometricViewSizeBinder to wait for the icon
- * asset to be loaded before determining the prompt size.
- */
- val isIconViewLoaded: Flow<Boolean> =
- combine(credentialKind, _isIconViewLoaded.asStateFlow()) { credentialKind, isIconViewLoaded
- ->
- if (credentialKind is PromptKind.Biometric) {
- isIconViewLoaded
- } else {
- true
- }
- }
-
- // Sets whether the prompt's iconView animation has been loaded in the view yet.
- fun setIsIconViewLoaded(iconViewLoaded: Boolean) {
- _isIconViewLoaded.value = iconViewLoaded
- }
-
/** Padding for prompt UI elements */
val promptPadding: Flow<Rect> =
combine(size, displayStateInteractor.currentRotation) { size, rotation ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 1f69cc0..0d40511 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -397,8 +397,10 @@
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_DATE_CHANGED);
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
- getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/,
- null /* scheduler */);
+ mBgHandler.post(() -> {
+ getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/,
+ null /* scheduler */);
+ });
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
mRegistered = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 3925dd1..13e3835 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -34,6 +34,7 @@
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.keyguard.mediator.ScreenOnCoordinator;
+import com.android.systemui.CoreStartable;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
@@ -79,9 +80,12 @@
import com.android.systemui.wallpapers.data.repository.WallpaperRepository;
import com.android.wm.shell.keyguard.KeyguardTransitions;
+import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
import java.util.concurrent.Executor;
@@ -105,7 +109,7 @@
StartKeyguardTransitionModule.class,
ResourceTrimmerModule.class,
})
-public class KeyguardModule {
+public interface KeyguardModule {
/**
* Provides our instance of KeyguardViewMediator which is considered optional.
*/
@@ -207,13 +211,19 @@
/** */
@Provides
- public ViewMediatorCallback providesViewMediatorCallback(KeyguardViewMediator viewMediator) {
+ static ViewMediatorCallback providesViewMediatorCallback(KeyguardViewMediator viewMediator) {
return viewMediator.getViewMediatorCallback();
}
/** */
@Provides
- public KeyguardQuickAffordancesMetricsLogger providesKeyguardQuickAffordancesMetricsLogger() {
+ static KeyguardQuickAffordancesMetricsLogger providesKeyguardQuickAffordancesMetricsLogger() {
return new KeyguardQuickAffordancesMetricsLoggerImpl();
}
+
+ /** Binds {@link KeyguardUpdateMonitor} as a {@link CoreStartable}. */
+ @Binds
+ @IntoMap
+ @ClassKey(KeyguardUpdateMonitor.class)
+ CoreStartable bindsKeyguardUpdateMonitor(KeyguardUpdateMonitor keyguardUpdateMonitor);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 633510d..c9df317 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui.statusbar;
+import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
+import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
@@ -22,6 +24,7 @@
import static android.os.UserHandle.USER_NULL;
import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
+import static android.app.Flags.keyguardPrivateNotifications;
import static android.os.Flags.allowPrivateProfile;
import static com.android.systemui.DejankUtils.whitelistIpcs;
@@ -47,7 +50,6 @@
import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
-import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -149,6 +151,25 @@
new ListenerSet<>();
private final Collection<Uri> mLockScreenUris = new ArrayList<>();
+ protected final BroadcastReceiver mKeyguardReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+
+ if (ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED.equals(action)) {
+ if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
+ mKeyguardAllowingNotifications =
+ intent.getBooleanExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, false);
+ if (mCurrentUserId == getSendingUserId()) {
+ boolean changed = updateLockscreenNotificationSetting();
+ if (changed) {
+ notifyNotificationStateChanged();
+ }
+ }
+ }
+ }
+ }
+ };
protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
@Override
@@ -321,11 +342,21 @@
mLockScreenUris.add(SHOW_PRIVATE_LOCKSCREEN);
dumpManager.registerDumpable(this);
+
+ if (keyguardPrivateNotifications()) {
+ init();
+ }
}
public void setUpWithPresenter(NotificationPresenter presenter) {
mPresenter = presenter;
+ if (!keyguardPrivateNotifications()) {
+ init();
+ }
+ }
+
+ private void init() {
mLockscreenSettingsObserver = new ContentObserver(
mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)
? mBackgroundHandler
@@ -408,6 +439,11 @@
new IntentFilter(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)
? mBackgroundExecutor : null, UserHandle.ALL);
+ if (keyguardPrivateNotifications()) {
+ mBroadcastDispatcher.registerReceiver(mKeyguardReceiver,
+ new IntentFilter(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED),
+ mBackgroundExecutor, UserHandle.ALL);
+ }
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_ADDED);
@@ -449,6 +485,10 @@
mLockscreenSettingsObserver.onChange(
false, mLockScreenUris, 0, UserHandle.of(userId));
updateDpcSettings(userId);
+
+ if (keyguardPrivateNotifications()) {
+ updateGlobalKeyguardSettings();
+ }
}
public boolean shouldShowLockscreenNotifications() {
@@ -477,8 +517,12 @@
boolean allowedByDpm;
if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- show = mUsersUsersAllowingNotifications.get(mCurrentUserId)
- && mKeyguardAllowingNotifications;
+ if (keyguardPrivateNotifications()) {
+ show = mUsersUsersAllowingNotifications.get(mCurrentUserId);
+ } else {
+ show = mUsersUsersAllowingNotifications.get(mCurrentUserId)
+ && mKeyguardAllowingNotifications;
+ }
// If DPC never notified us about a user, that means they have no policy for the user,
// and they allow the behavior
allowedByDpm = mUsersDpcAllowingNotifications.get(mCurrentUserId, true);
@@ -521,8 +565,13 @@
1,
userId) != 0;
mUsersUsersAllowingNotifications.put(userId, newAllowLockscreen);
- boolean keyguardChanged = updateGlobalKeyguardSettings();
- return (newAllowLockscreen != originalAllowLockscreen) || keyguardChanged;
+
+ if (keyguardPrivateNotifications()) {
+ return (newAllowLockscreen != originalAllowLockscreen);
+ } else {
+ boolean keyguardChanged = updateGlobalKeyguardSettings();
+ return (newAllowLockscreen != originalAllowLockscreen) || keyguardChanged;
+ }
}
@WorkerThread
@@ -560,8 +609,14 @@
Log.i(TAG, "Asking for redact notifs dpm override too early", new Throwable());
return false;
}
- return mUsersUsersAllowingPrivateNotifications.get(userHandle)
- && mUsersDpcAllowingPrivateNotifications.get(userHandle);
+ if (keyguardPrivateNotifications()) {
+ return mUsersUsersAllowingPrivateNotifications.get(userHandle)
+ && mUsersDpcAllowingPrivateNotifications.get(userHandle)
+ && mKeyguardAllowingNotifications;
+ } else {
+ return mUsersUsersAllowingPrivateNotifications.get(userHandle)
+ && mUsersDpcAllowingPrivateNotifications.get(userHandle);
+ }
} else {
if (userHandle == USER_ALL) {
return true;
@@ -648,9 +703,14 @@
Log.wtf(TAG, "Asking for show notifs dpm override too early", new Throwable());
updateDpcSettings(userHandle);
}
- return mUsersUsersAllowingNotifications.get(userHandle)
- && mUsersDpcAllowingNotifications.get(userHandle)
- && mKeyguardAllowingNotifications;
+ if (keyguardPrivateNotifications()) {
+ return mUsersUsersAllowingNotifications.get(userHandle)
+ && mUsersDpcAllowingNotifications.get(userHandle);
+ } else {
+ return mUsersUsersAllowingNotifications.get(userHandle)
+ && mUsersDpcAllowingNotifications.get(userHandle)
+ && mKeyguardAllowingNotifications;
+ }
} else {
if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) {
return true;
@@ -689,7 +749,12 @@
ent.getSbn().getNotification().visibility == Notification.VISIBILITY_PRIVATE;
boolean userForcesRedaction = packageHasVisibilityOverride(ent.getSbn().getKey());
- return userForcesRedaction || notificationRequestsRedaction && isNotifRedacted;
+ if (keyguardPrivateNotifications()) {
+ return !mKeyguardAllowingNotifications
+ || userForcesRedaction || notificationRequestsRedaction && isNotifRedacted;
+ } else {
+ return userForcesRedaction || notificationRequestsRedaction && isNotifRedacted;
+ }
}
private boolean packageHasVisibilityOverride(String key) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 70ccc4f..80ef14b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.inflation;
+import static com.android.systemui.Flags.screenshareNotificationHiding;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -243,7 +244,11 @@
params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
params.setUseLowPriority(isLowPriority);
- if (mNotificationLockscreenUserManager.needsRedaction(entry)) {
+ // If screenshareNotificationHiding is enabled, both public and private views should be
+ // inflated to avoid any latency associated with reinflating all notification views when
+ // screen share starts and stops
+ if (screenshareNotificationHiding()
+ || mNotificationLockscreenUserManager.needsRedaction(entry)) {
params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC);
} else {
params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
index 3f76d30..8858d13 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
@@ -51,6 +51,7 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
+import dagger.Lazy
@SmallTest
class ActiveUnlockConfigTest : SysuiTestCase() {
@@ -60,6 +61,7 @@
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var selectedUserInteractor: SelectedUserInteractor
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var lazyKeyguardUpdateMonitor: Lazy<KeyguardUpdateMonitor>
@Mock private lateinit var mockPrintWriter: PrintWriter
@Captor private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
@@ -72,6 +74,7 @@
MockitoAnnotations.initMocks(this)
whenever(selectedUserInteractor.getSelectedUserId()).thenReturn(currentUser)
+ whenever(lazyKeyguardUpdateMonitor.get()).thenReturn(keyguardUpdateMonitor)
secureSettings = FakeSettings()
activeUnlockConfig =
ActiveUnlockConfig(
@@ -79,6 +82,7 @@
secureSettings,
contentResolver,
selectedUserInteractor,
+ lazyKeyguardUpdateMonitor,
dumpManager
)
}
@@ -260,7 +264,6 @@
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// GIVEN fingerprint and face are NOT enrolled
- activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
`when`(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(false)
`when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(false)
@@ -290,7 +293,6 @@
updateSetting(secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
// GIVEN fingerprint and face are both enrolled
- activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
`when`(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(true)
`when`(keyguardUpdateMonitor.isUnlockWithFingerprintPossible(0)).thenReturn(true)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 1ab634c..d03a898 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -2283,6 +2283,7 @@
Optional.of(mInteractiveToAuthProvider),
mTaskStackChangeListeners, mSelectedUserInteractor, mActivityTaskManager);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
+ start();
}
public boolean hasSimStateJustChanged() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
index 5e57c83..bc50c25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
@@ -198,7 +198,7 @@
AtomicBoolean isBgThreadFailed = new AtomicBoolean(false);
Thread bgThread = new Thread(() -> {
assertTrue(getLock(unloadLock, 10));
- assertTrue(getLock(loadLock, 3000)); // Wait for the foreground thread
+ assertTrue(getLock(loadLock, 4000)); // Wait for the foreground thread
assertNotNull(mPluginInstance.getPlugin());
// Attempt to delete the plugin, this should block until the load completes
mPluginInstance.unloadPlugin();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 42c7375..0c6f456 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -19,9 +19,12 @@
import static android.app.Notification.VISIBILITY_PRIVATE;
import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE;
+import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
+import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
+import static android.app.Flags.FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS;
import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
import static android.os.UserHandle.USER_ALL;
import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
@@ -111,7 +114,9 @@
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
- return FlagsParameterization.allCombinationsOf(FLAG_ALLOW_PRIVATE_PROFILE);
+ return FlagsParameterization.allCombinationsOf(
+ FLAG_ALLOW_PRIVATE_PROFILE,
+ FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS);
}
public NotificationLockscreenUserManagerTest(FlagsParameterization flags) {
@@ -245,6 +250,19 @@
}
@Test
+ @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
+ public void testInit() {
+ when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
+ mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
+ mLockscreenUserManager.setUpWithPresenter(mPresenter);
+
+ mBackgroundExecutor.runAllReady();
+
+ assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+ }
+
+ @Test
public void testGetCurrentProfiles() {
final SparseArray<UserInfo> expectedCurProfiles = new SparseArray<>();
expectedCurProfiles.put(mCurrentUser.id, mCurrentUser);
@@ -579,6 +597,29 @@
}
@Test
+ @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
+ public void testKeyguardManager_noPrivateNotifications() {
+ Mockito.clearInvocations(mDevicePolicyManager);
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
+ mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
+ new Intent(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED)
+ .putExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, true));
+
+ assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
+ // it's a global field, confirm secondary too
+ assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
+ assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
+ assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
+ mSecondaryUser.id));
+ }
+
+ @Test
public void testDevicePolicy_noPrivateNotifications() {
Mockito.clearInvocations(mDevicePolicyManager);
// User allows notifications
@@ -699,6 +740,29 @@
}
@Test
+ @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
+ public void testShouldShowLockscreenNotifications_keyguardManagerNoPrivateNotifications_show() {
+ // KeyguardManager does not allow notifications
+ when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
+ // User allows notifications
+ mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
+ changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
+ // DevicePolicy allows notifications
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
+ .thenReturn(0);
+ BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
+ 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
+ mLockscreenUserManager.mKeyguardReceiver.setPendingResult(pr);
+ mLockscreenUserManager.mKeyguardReceiver.onReceive(mContext,
+ new Intent(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED)
+ .putExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, false));
+
+ assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications());
+ assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
+ }
+
+ @Test
+ @DisableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
public void testShouldShowLockscreenNotifications_keyguardManagerNoPrivateNotifications() {
// KeyguardManager does not allow notifications
when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
@@ -718,6 +782,7 @@
}
@Test
+ @DisableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
public void testUserAllowsNotificationsInPublic_keyguardManagerNoPrivateNotifications() {
// DevicePolicy allows notifications
when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
index 3a90a95..73ed97f 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureServerSession.java
@@ -35,8 +35,8 @@
import android.util.LocalLog;
import android.util.Slog;
import android.view.contentcapture.ContentCaptureContext;
+import android.view.contentcapture.ContentCaptureSession;
import android.view.contentcapture.ContentCaptureSessionId;
-import android.view.contentcapture.MainContentCaptureSession;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
@@ -123,7 +123,7 @@
public void setContentCaptureEnabledLocked(boolean enabled) {
try {
final Bundle extras = new Bundle();
- extras.putBoolean(MainContentCaptureSession.EXTRA_ENABLED_STATE, true);
+ extras.putBoolean(ContentCaptureSession.EXTRA_ENABLED_STATE, true);
mSessionStateReceiver.send(enabled ? RESULT_CODE_TRUE : RESULT_CODE_FALSE, extras);
} catch (RemoteException e) {
Slog.w(TAG, "Error async reporting result to client: " + e);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 96b1650..02f4485 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2118,11 +2118,6 @@
// anyway, so we just remove the SHORT_SERVICE type.
foregroundServiceType &= ~FOREGROUND_SERVICE_TYPE_SHORT_SERVICE;
}
- if (!shouldAllowBootCompletedStart(r, foregroundServiceType)) {
- throw new ForegroundServiceStartNotAllowedException("FGS type "
- + ServiceInfo.foregroundServiceTypeToLabel(foregroundServiceType)
- + " not allowed to start from BOOT_COMPLETED!");
- }
boolean alreadyStartedOp = false;
boolean stopProcStatsOp = false;
@@ -2137,6 +2132,12 @@
mServiceFGAnrTimer.cancel(r);
}
+ if (!shouldAllowBootCompletedStart(r, foregroundServiceType)) {
+ throw new ForegroundServiceStartNotAllowedException("FGS type "
+ + ServiceInfo.foregroundServiceTypeToLabel(foregroundServiceType)
+ + " not allowed to start from BOOT_COMPLETED!");
+ }
+
final ProcessServiceRecord psr = r.app.mServices;
try {
boolean ignoreForeground = false;
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
index 4df2581..5d609bc 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java
@@ -54,8 +54,8 @@
@NonNull private final Context mContext;
@NonNull private final PackageManager mPackageManager;
- @NonNull private final FaceManager mFaceManager;
- @NonNull private final FingerprintManager mFingerprintManager;
+ @Nullable private final FaceManager mFaceManager;
+ @Nullable private final FingerprintManager mFingerprintManager;
private final boolean mEnabled;
private final float mThreshold;
@@ -197,11 +197,11 @@
}
private boolean hasEnrolledFace(int userId) {
- return mFaceManager.hasEnrolledTemplates(userId);
+ return mFaceManager != null && mFaceManager.hasEnrolledTemplates(userId);
}
private boolean hasEnrolledFingerprint(int userId) {
- return mFingerprintManager.hasEnrolledTemplates(userId);
+ return mFingerprintManager != null && mFingerprintManager.hasEnrolledTemplates(userId);
}
/**
diff --git a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java b/services/core/java/com/android/server/media/AudioManagerRouteController.java
similarity index 98%
rename from services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
rename to services/core/java/com/android/server/media/AudioManagerRouteController.java
index 0eb9166..5c9e61a 100644
--- a/services/core/java/com/android/server/media/AudioPoliciesDeviceRouteController.java
+++ b/services/core/java/com/android/server/media/AudioManagerRouteController.java
@@ -58,11 +58,9 @@
* <p>This implementation obtains and manages all routes via {@link AudioManager}, with the
* exception of {@link AudioManager#handleBluetoothActiveDeviceChanged inactive bluetooth} routes
* which are managed by {@link AudioPoliciesBluetoothRouteController}, which depends on the
- * bluetooth stack (for example {@link BluetoothAdapter}.
+ * bluetooth stack ({@link BluetoothAdapter} and related classes).
*/
-// TODO: b/305199571 - Rename this class to avoid the AudioPolicies prefix, which has been flagged
-// by the audio team as a confusing name.
-/* package */ final class AudioPoliciesDeviceRouteController implements DeviceRouteController {
+/* package */ final class AudioManagerRouteController implements DeviceRouteController {
private static final String TAG = SystemMediaRoute2Provider.TAG;
@NonNull
@@ -103,7 +101,7 @@
Manifest.permission.MODIFY_AUDIO_ROUTING,
Manifest.permission.QUERY_AUDIO_STATE
})
- /* package */ AudioPoliciesDeviceRouteController(
+ /* package */ AudioManagerRouteController(
@NonNull Context context,
@NonNull AudioManager audioManager,
@NonNull Looper looper,
diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java
index 8b62cc9..dff0adf 100644
--- a/services/core/java/com/android/server/media/DeviceRouteController.java
+++ b/services/core/java/com/android/server/media/DeviceRouteController.java
@@ -65,7 +65,7 @@
if (strategyForMedia != null
&& btAdapter != null
&& Flags.enableAudioPoliciesDeviceAndBluetoothController()) {
- return new AudioPoliciesDeviceRouteController(
+ return new AudioManagerRouteController(
context,
audioManager,
looper,
diff --git a/services/core/java/com/android/server/media/TEST_MAPPING b/services/core/java/com/android/server/media/TEST_MAPPING
index 1b49093..b3e5b9e 100644
--- a/services/core/java/com/android/server/media/TEST_MAPPING
+++ b/services/core/java/com/android/server/media/TEST_MAPPING
@@ -3,5 +3,10 @@
{
"name": "CtsMediaBetterTogetherTestCases"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "MediaRouterServiceTests"
+ }
]
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index ff415c1..4d7d8cf 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,7 +16,9 @@
package com.android.server.notification;
+import static android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS;
import static android.Manifest.permission.RECEIVE_SENSITIVE_NOTIFICATIONS;
+import static android.Manifest.permission.STATUS_BAR_SERVICE;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_FOREGROUND_SERVICE;
import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -67,6 +69,8 @@
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON;
import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
import static android.app.Flags.lifetimeExtensionRefactor;
+import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
+import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
import static android.content.Context.BIND_ALLOW_WHITELIST_MANAGEMENT;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_FOREGROUND_SERVICE;
@@ -210,6 +214,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.LauncherApps;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
@@ -3369,9 +3374,7 @@
.setChannelName(r.getChannel().getName().toString())
.setPostedTimeMs(System.currentTimeMillis())
.setTitle(getHistoryTitle(r.getNotification()))
- .setText(getHistoryText(
- r.getSbn().getPackageContext(getContext()),
- r.getNotification()))
+ .setText(getHistoryText(r.getNotification()))
.setIcon(r.getNotification().getSmallIcon())
.build());
}
@@ -3414,12 +3417,11 @@
/**
* Returns the appropriate substring for this notification based on the style of notification.
*/
- private String getHistoryText(Context appContext, Notification n) {
+ private String getHistoryText(Notification n) {
CharSequence text = null;
if (n.extras != null) {
text = n.extras.getCharSequence(EXTRA_TEXT);
-
- Notification.Builder nb = Notification.Builder.recoverBuilder(appContext, n);
+ Notification.Builder nb = Notification.Builder.recoverBuilder(getContext(), n);
if (nb.getStyle() instanceof Notification.BigTextStyle) {
text = ((Notification.BigTextStyle) nb.getStyle()).getBigText();
@@ -5569,7 +5571,7 @@
private void enforceSystemOrSystemUI(String message) {
if (isCallerSystemOrPhone()) return;
- getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+ getContext().enforceCallingPermission(STATUS_BAR_SERVICE,
message);
}
@@ -5578,7 +5580,7 @@
checkCallerIsSystemOrSameApp(pkg);
} catch (SecurityException e) {
getContext().enforceCallingPermission(
- android.Manifest.permission.STATUS_BAR_SERVICE,
+ STATUS_BAR_SERVICE,
message);
}
}
@@ -6183,13 +6185,20 @@
@Override
public void setPrivateNotificationsAllowed(boolean allow) {
if (PackageManager.PERMISSION_GRANTED
- != getContext().checkCallingPermission(
- permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)) {
+ != getContext().checkCallingPermission(CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)) {
throw new SecurityException(
"Requires CONTROL_KEYGUARD_SECURE_NOTIFICATIONS permission");
}
if (allow != mLockScreenAllowSecureNotifications) {
mLockScreenAllowSecureNotifications = allow;
+ if (android.app.Flags.keyguardPrivateNotifications()) {
+ getContext().sendBroadcast(
+ new Intent(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED)
+ .putExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED,
+ mLockScreenAllowSecureNotifications),
+ STATUS_BAR_SERVICE);
+ }
+
handleSavePolicyFile();
}
}
@@ -6197,8 +6206,7 @@
@Override
public boolean getPrivateNotificationsAllowed() {
if (PackageManager.PERMISSION_GRANTED
- != getContext().checkCallingPermission(
- permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)) {
+ != getContext().checkCallingPermission(CONTROL_KEYGUARD_SECURE_NOTIFICATIONS)) {
throw new SecurityException(
"Requires CONTROL_KEYGUARD_SECURE_NOTIFICATIONS permission");
}
@@ -8369,6 +8377,8 @@
boolean posted = false;
try {
posted = postNotification();
+ } catch (Exception e) {
+ Slog.e(TAG, "Error posting", e);
} finally {
if (!posted) {
mTracker.cancel();
@@ -9557,12 +9567,16 @@
}
private void scheduleListenerHintsChanged(int state) {
- mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
+ if (!Flags.notificationReduceMessagequeueUsage()) {
+ mHandler.removeMessages(MESSAGE_LISTENER_HINTS_CHANGED);
+ }
mHandler.obtainMessage(MESSAGE_LISTENER_HINTS_CHANGED, state, 0).sendToTarget();
}
private void scheduleInterruptionFilterChanged(int listenerInterruptionFilter) {
- mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
+ if (!Flags.notificationReduceMessagequeueUsage()) {
+ mHandler.removeMessages(MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED);
+ }
mHandler.obtainMessage(
MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED,
listenerInterruptionFilter,
@@ -9642,15 +9656,24 @@
}
protected void scheduleSendRankingUpdate() {
- if (!hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
+ if (Flags.notificationReduceMessagequeueUsage()) {
Message m = Message.obtain(this, MESSAGE_SEND_RANKING_UPDATE);
sendMessage(m);
+ } else {
+ if (!hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
+ Message m = Message.obtain(this, MESSAGE_SEND_RANKING_UPDATE);
+ sendMessage(m);
+ }
}
}
protected void scheduleCancelNotification(CancelNotificationRunnable cancelRunnable) {
- if (!hasCallbacks(cancelRunnable)) {
+ if (Flags.notificationReduceMessagequeueUsage()) {
sendMessage(Message.obtain(this, cancelRunnable));
+ } else {
+ if (!hasCallbacks(cancelRunnable)) {
+ sendMessage(Message.obtain(this, cancelRunnable));
+ }
}
}
@@ -9684,7 +9707,9 @@
}
public void requestSort() {
- removeMessages(MESSAGE_RANKING_SORT);
+ if (!Flags.notificationReduceMessagequeueUsage()) {
+ removeMessages(MESSAGE_RANKING_SORT);
+ }
Message msg = Message.obtain();
msg.what = MESSAGE_RANKING_SORT;
sendMessage(msg);
@@ -10589,7 +10614,7 @@
if (isCallerSystemOrPhone()) {
return true;
}
- return getContext().checkCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE)
+ return getContext().checkCallingPermission(STATUS_BAR_SERVICE)
== PERMISSION_GRANTED;
}
@@ -10628,7 +10653,7 @@
if (isCallerSystemOrPhone()) {
return;
}
- getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+ getContext().enforceCallingPermission(STATUS_BAR_SERVICE,
message);
}
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 8e79922a..49db7fc 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -50,3 +50,10 @@
# Referenced in WM where WM starts before DeviceConfig
is_fixed_read_only: true
}
+
+flag {
+ name: "notification_reduce_messagequeue_usage"
+ namespace: "systemui"
+ description: "When this flag is on, NMS will no longer call removeMessage() and hasCallbacks() on Handler"
+ bug: "311051285"
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index e546f42..1660c3e 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -21,11 +21,13 @@
import android.Manifest;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.app.role.RoleManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.BugreportManager.BugreportCallback;
import android.os.BugreportParams;
@@ -37,7 +39,6 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
-import android.os.UserManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -95,7 +96,6 @@
private static final long DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS = 30 * 1000;
private final Object mLock = new Object();
- private final Injector mInjector;
private final Context mContext;
private final AppOpsManager mAppOps;
private final TelephonyManager mTelephonyManager;
@@ -346,14 +346,6 @@
AtomicFile getMappingFile() {
return mMappingFile;
}
-
- UserManager getUserManager() {
- return mContext.getSystemService(UserManager.class);
- }
-
- DevicePolicyManager getDevicePolicyManager() {
- return mContext.getSystemService(DevicePolicyManager.class);
- }
}
BugreportManagerServiceImpl(Context context) {
@@ -365,7 +357,6 @@
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
BugreportManagerServiceImpl(Injector injector) {
- mInjector = injector;
mContext = injector.getContext();
mAppOps = mContext.getSystemService(AppOpsManager.class);
mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
@@ -398,7 +389,12 @@
int callingUid = Binder.getCallingUid();
enforcePermission(callingPackage, callingUid, bugreportMode
== BugreportParams.BUGREPORT_MODE_TELEPHONY /* checkCarrierPrivileges */);
- ensureUserCanTakeBugReport(bugreportMode);
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ ensureUserCanTakeBugReport(bugreportMode);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
Slogf.i(TAG, "Starting bugreport for %s / %d", callingPackage, callingUid);
synchronized (mLock) {
@@ -437,6 +433,7 @@
@RequiresPermission(value = Manifest.permission.DUMP, conditional = true)
public void retrieveBugreport(int callingUidUnused, String callingPackage, int userId,
FileDescriptor bugreportFd, String bugreportFile,
+
boolean keepBugreportOnRetrievalUnused, IDumpstateListener listener) {
int callingUid = Binder.getCallingUid();
enforcePermission(callingPackage, callingUid, false);
@@ -568,48 +565,54 @@
}
/**
- * Validates that the calling user is an admin user or, when bugreport is requested remotely
- * that the user is an affiliated user.
+ * Validates that the current user is an admin user or, when bugreport is requested remotely
+ * that the current user is an affiliated user.
*
- * @throws IllegalArgumentException if the calling user is not an admin user
+ * @throws IllegalArgumentException if the current user is not an admin user
*/
private void ensureUserCanTakeBugReport(int bugreportMode) {
- // Get the calling userId before clearing the caller identity.
- int callingUserId = UserHandle.getUserId(Binder.getCallingUid());
- boolean isAdminUser = false;
- final long identity = Binder.clearCallingIdentity();
+ UserInfo currentUser = null;
try {
- isAdminUser = mInjector.getUserManager().isUserAdmin(callingUserId);
- } finally {
- Binder.restoreCallingIdentity(identity);
+ currentUser = ActivityManager.getService().getCurrentUser();
+ } catch (RemoteException e) {
+ // Impossible to get RemoteException for an in-process call.
}
- if (!isAdminUser) {
+
+ if (currentUser == null) {
+ logAndThrow("There is no current user, so no bugreport can be requested.");
+ }
+
+ if (!currentUser.isAdmin()) {
if (bugreportMode == BugreportParams.BUGREPORT_MODE_REMOTE
- && isUserAffiliated(callingUserId)) {
+ && isCurrentUserAffiliated(currentUser.id)) {
return;
}
- logAndThrow(TextUtils.formatSimple("Calling user %s is not an admin user."
- + " Only admin users are allowed to take bugreport.", callingUserId));
+ logAndThrow(TextUtils.formatSimple("Current user %s is not an admin user."
+ + " Only admin users are allowed to take bugreport.", currentUser.id));
}
}
/**
- * Returns {@code true} if the device has device owner and the specified user is affiliated
+ * Returns {@code true} if the device has device owner and the current user is affiliated
* with the device owner.
*/
- private boolean isUserAffiliated(int userId) {
- DevicePolicyManager dpm = mInjector.getDevicePolicyManager();
+ private boolean isCurrentUserAffiliated(int currentUserId) {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
int deviceOwnerUid = dpm.getDeviceOwnerUserId();
if (deviceOwnerUid == UserHandle.USER_NULL) {
return false;
}
- if (DEBUG) {
- Slog.d(TAG, "callingUid: " + userId + " deviceOwnerUid: " + deviceOwnerUid);
- }
+ int callingUserId = UserHandle.getUserId(Binder.getCallingUid());
- if (userId != deviceOwnerUid && !dpm.isAffiliatedUser(userId)) {
- logAndThrow("User " + userId + " is not affiliated to the device owner.");
+ Slog.i(TAG, "callingUid: " + callingUserId + " deviceOwnerUid: " + deviceOwnerUid
+ + " currentUserId: " + currentUserId);
+
+ if (callingUserId != deviceOwnerUid) {
+ logAndThrow("Caller is not device owner on provisioned device.");
+ }
+ if (!dpm.isAffiliatedUser(currentUserId)) {
+ logAndThrow("Current user is not affiliated to the device owner.");
}
return true;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 5b13d3fe..edce3ec 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -473,6 +473,8 @@
private TalkbackShortcutController mTalkbackShortcutController;
+ private WindowWakeUpPolicy mWindowWakeUpPolicy;
+
boolean mSafeMode;
// Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
@@ -640,15 +642,6 @@
// Whether to lock the device after the next dreaming transition has finished.
private boolean mLockAfterDreamingTransitionFinished;
- // Allowed theater mode wake actions
- private boolean mAllowTheaterModeWakeFromKey;
- private boolean mAllowTheaterModeWakeFromPowerKey;
- private boolean mAllowTheaterModeWakeFromMotion;
- private boolean mAllowTheaterModeWakeFromMotionWhenNotDreaming;
- private boolean mAllowTheaterModeWakeFromCameraLens;
- private boolean mAllowTheaterModeWakeFromLidSwitch;
- private boolean mAllowTheaterModeWakeFromWakeGesture;
-
// If true, the power button long press behavior will be invoked even if the default display is
// non-interactive. If false, the power button long press behavior will be skipped if the
// default display is non-interactive.
@@ -930,8 +923,7 @@
if (shouldEnableWakeGestureLp()) {
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
"Wake Up");
- wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture,
- PowerManager.WAKE_REASON_GESTURE, "android.policy:GESTURE");
+ mWindowWakeUpPolicy.wakeUpFromWakeGesture();
}
}
}
@@ -1067,7 +1059,7 @@
|| handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted();
if (!mPowerKeyHandled) {
if (!interactive) {
- wakeUpFromPowerKey(event.getDownTime());
+ wakeUpFromWakeKey(event);
}
} else {
// handled by another power key policy.
@@ -1309,7 +1301,7 @@
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.THEATER_MODE_ON, 0);
if (!interactive) {
- wakeUpFromPowerKey(eventTime);
+ wakeUpFromWakeKey(eventTime, KEYCODE_POWER);
}
} else {
Slog.i(TAG, "Toggling theater mode on.");
@@ -1325,7 +1317,7 @@
case MULTI_PRESS_POWER_BRIGHTNESS_BOOST:
Slog.i(TAG, "Starting brightness boost.");
if (!interactive) {
- wakeUpFromPowerKey(eventTime);
+ wakeUpFromWakeKey(eventTime, KEYCODE_POWER);
}
mPowerManager.boostScreenBrightness(eventTime);
break;
@@ -2312,22 +2304,6 @@
mLidNavigationAccessibility = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lidNavigationAccessibility);
- mAllowTheaterModeWakeFromKey = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allowTheaterModeWakeFromKey);
- mAllowTheaterModeWakeFromPowerKey = mAllowTheaterModeWakeFromKey
- || mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allowTheaterModeWakeFromPowerKey);
- mAllowTheaterModeWakeFromMotion = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allowTheaterModeWakeFromMotion);
- mAllowTheaterModeWakeFromMotionWhenNotDreaming = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allowTheaterModeWakeFromMotionWhenNotDreaming);
- mAllowTheaterModeWakeFromCameraLens = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allowTheaterModeWakeFromCameraLens);
- mAllowTheaterModeWakeFromLidSwitch = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allowTheaterModeWakeFromLidSwitch);
- mAllowTheaterModeWakeFromWakeGesture = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture);
-
mGoToSleepOnButtonPressTheaterMode = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_goToSleepOnButtonPressTheaterMode);
@@ -2457,6 +2433,7 @@
com.android.internal.R.integer.config_keyguardDrawnTimeout);
mKeyguardDelegate = injector.getKeyguardServiceDelegate();
mTalkbackShortcutController = injector.getTalkbackShortcutController();
+ mWindowWakeUpPolicy = new WindowWakeUpPolicy(mContext);
initKeyCombinationRules();
initSingleKeyGestureRules(injector.getLooper());
mButtonOverridePermissionChecker = injector.getButtonOverridePermissionChecker();
@@ -4483,8 +4460,7 @@
updateRotation(true);
if (lidOpen) {
- wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromLidSwitch,
- PowerManager.WAKE_REASON_LID, "android.policy:LID");
+ mWindowWakeUpPolicy.wakeUpFromLid();
} else if (getLidBehavior() != LID_BEHAVIOR_SLEEP) {
mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
}
@@ -4510,8 +4486,7 @@
} else {
intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
}
- wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromCameraLens,
- PowerManager.WAKE_REASON_CAMERA_LAUNCH, "android.policy:CAMERA_COVER");
+ mWindowWakeUpPolicy.wakeUpFromCameraCover(whenNanos / 1000000);
startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
}
mCameraLensCoverState = lensCoverState;
@@ -4589,7 +4564,7 @@
boolean shouldTurnOnTv = false;
if (down && (keyCode == KeyEvent.KEYCODE_POWER
|| keyCode == KeyEvent.KEYCODE_TV_POWER)) {
- wakeUpFromPowerKey(event.getDownTime());
+ wakeUpFromWakeKey(event);
shouldTurnOnTv = true;
} else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP)
&& isWakeKeyWhenScreenOff(keyCode)) {
@@ -5104,9 +5079,7 @@
if (mRequestedOrSleepingDefaultDisplay) {
mCameraGestureTriggeredDuringGoingToSleep = true;
// Wake device up early to prevent display doing redundant turning off/on stuff.
- wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromPowerKey,
- PowerManager.WAKE_REASON_CAMERA_LAUNCH,
- "android.policy:CAMERA_GESTURE_PREVENT_LOCK");
+ mWindowWakeUpPolicy.wakeUpFromPowerKeyCameraGesture();
}
return true;
}
@@ -5204,8 +5177,7 @@
public int interceptMotionBeforeQueueingNonInteractive(int displayId, int source, int action,
long whenNanos, int policyFlags) {
if ((policyFlags & FLAG_WAKE) != 0) {
- if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion,
- PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) {
+ if (mWindowWakeUpPolicy.wakeUpFromMotion(whenNanos / 1000000)) {
// Woke up. Pass motion events to user.
return ACTION_PASS_TO_USER;
}
@@ -5219,8 +5191,7 @@
// there will be no dream to intercept the touch and wake into ambient. The device should
// wake up in this case.
if (isTheaterModeEnabled() && (policyFlags & FLAG_WAKE) != 0) {
- if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotionWhenNotDreaming,
- PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) {
+ if (mWindowWakeUpPolicy.wakeUpFromMotion(whenNanos / 1000000)) {
// Woke up. Pass motion events to user.
return ACTION_PASS_TO_USER;
}
@@ -5554,37 +5525,22 @@
return sleepDurationRealtime > mWakeUpToLastStateTimeout;
}
- private void wakeUpFromPowerKey(long eventTime) {
- if (wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey,
- PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER")) {
- // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
- if (shouldWakeUpWithHomeIntent()) {
- startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ false, /*wakenFromDreams*/ true,
- PowerManager.wakeReasonToString(PowerManager.WAKE_REASON_POWER_BUTTON));
- }
- }
- }
-
private void wakeUpFromWakeKey(KeyEvent event) {
- if (wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey,
- PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY")) {
- // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
- if (shouldWakeUpWithHomeIntent() && event.getKeyCode() == KEYCODE_HOME) {
- startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ true, /*wakenFromDreams*/ true,
- PowerManager.wakeReasonToString(PowerManager.WAKE_REASON_WAKE_KEY));
- }
- }
+ wakeUpFromWakeKey(event.getEventTime(), event.getKeyCode());
}
- private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason,
- String details) {
- final boolean theaterModeEnabled = isTheaterModeEnabled();
- if (!wakeInTheaterMode && theaterModeEnabled) {
- return false;
+ private void wakeUpFromWakeKey(long eventTime, int keyCode) {
+ if (mWindowWakeUpPolicy.wakeUpFromKey(eventTime, keyCode)) {
+ final boolean keyCanLaunchHome = keyCode == KEYCODE_HOME || keyCode == KEYCODE_POWER;
+ // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout
+ if (shouldWakeUpWithHomeIntent() && keyCanLaunchHome) {
+ startDockOrHome(
+ DEFAULT_DISPLAY,
+ /*fromHomeKey*/ keyCode == KEYCODE_HOME,
+ /*wakenFromDreams*/ true,
+ "Wake from " + KeyEvent. keyCodeToString(keyCode));
+ }
}
-
- mPowerManager.wakeUp(wakeTime, reason, details);
- return true;
}
private void finishKeyguardDrawn() {
diff --git a/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
new file mode 100644
index 0000000..392d0d4
--- /dev/null
+++ b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
@@ -0,0 +1,176 @@
+/*
+ * 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 com.android.server.policy;
+
+import static android.os.PowerManager.WAKE_REASON_CAMERA_LAUNCH;
+import static android.os.PowerManager.WAKE_REASON_GESTURE;
+import static android.os.PowerManager.WAKE_REASON_LID;
+import static android.os.PowerManager.WAKE_REASON_POWER_BUTTON;
+import static android.os.PowerManager.WAKE_REASON_WAKE_KEY;
+import static android.os.PowerManager.WAKE_REASON_WAKE_MOTION;
+import static android.view.KeyEvent.KEYCODE_POWER;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeReason;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.util.Slog;
+
+
+/** Policy controlling the decision and execution of window-related wake ups. */
+class WindowWakeUpPolicy {
+ private static final String TAG = "WindowWakeUpPolicy";
+
+ private static final boolean DEBUG = false;
+
+ private final Context mContext;
+ private final PowerManager mPowerManager;
+
+ private final boolean mAllowTheaterModeWakeFromKey;
+ private final boolean mAllowTheaterModeWakeFromPowerKey;
+ private final boolean mAllowTheaterModeWakeFromMotion;
+ private final boolean mAllowTheaterModeWakeFromMotionWhenNotDreaming;
+ private final boolean mAllowTheaterModeWakeFromCameraLens;
+ private final boolean mAllowTheaterModeWakeFromLidSwitch;
+ private final boolean mAllowTheaterModeWakeFromWakeGesture;
+
+ WindowWakeUpPolicy(Context context) {
+ mContext = context;
+ mPowerManager = context.getSystemService(PowerManager.class);
+
+ final Resources res = context.getResources();
+ mAllowTheaterModeWakeFromKey = res.getBoolean(
+ com.android.internal.R.bool.config_allowTheaterModeWakeFromKey);
+ mAllowTheaterModeWakeFromPowerKey = mAllowTheaterModeWakeFromKey
+ || res.getBoolean(
+ com.android.internal.R.bool.config_allowTheaterModeWakeFromPowerKey);
+ mAllowTheaterModeWakeFromMotion = res.getBoolean(
+ com.android.internal.R.bool.config_allowTheaterModeWakeFromMotion);
+ mAllowTheaterModeWakeFromMotionWhenNotDreaming = res.getBoolean(
+ com.android.internal.R.bool.config_allowTheaterModeWakeFromMotionWhenNotDreaming);
+ mAllowTheaterModeWakeFromCameraLens = res.getBoolean(
+ com.android.internal.R.bool.config_allowTheaterModeWakeFromCameraLens);
+ mAllowTheaterModeWakeFromLidSwitch = res.getBoolean(
+ com.android.internal.R.bool.config_allowTheaterModeWakeFromLidSwitch);
+ mAllowTheaterModeWakeFromWakeGesture = res.getBoolean(
+ com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture);
+ }
+
+ /**
+ * Wakes up from a key event.
+ *
+ * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
+ * @param keyCode the {@link android.view.KeyEvent} key code of the key event.
+ * @return {@code true} if the policy allows the requested wake up and the request has been
+ * executed; {@code false} otherwise.
+ */
+ boolean wakeUpFromKey(long eventTime, int keyCode) {
+ final boolean wakeAllowedDuringTheaterMode =
+ keyCode == KEYCODE_POWER
+ ? mAllowTheaterModeWakeFromPowerKey
+ : mAllowTheaterModeWakeFromKey;
+ return wakeUp(
+ eventTime,
+ wakeAllowedDuringTheaterMode,
+ keyCode == KEYCODE_POWER ? WAKE_REASON_POWER_BUTTON : WAKE_REASON_WAKE_KEY,
+ keyCode == KEYCODE_POWER ? "POWER" : "KEY");
+ }
+
+ /**
+ * Wakes up from a motion event.
+ *
+ * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
+ * @return {@code true} if the policy allows the requested wake up and the request has been
+ * executed; {@code false} otherwise.
+ */
+ boolean wakeUpFromMotion(long eventTime) {
+ return wakeUp(
+ eventTime, mAllowTheaterModeWakeFromMotion, WAKE_REASON_WAKE_MOTION, "MOTION");
+ }
+
+ /**
+ * Wakes up due to an opened camera cover.
+ *
+ * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
+ * @return {@code true} if the policy allows the requested wake up and the request has been
+ * executed; {@code false} otherwise.
+ */
+ boolean wakeUpFromCameraCover(long eventTime) {
+ return wakeUp(
+ eventTime,
+ mAllowTheaterModeWakeFromCameraLens,
+ WAKE_REASON_CAMERA_LAUNCH,
+ "CAMERA_COVER");
+ }
+
+ /**
+ * Wakes up due to an opened lid.
+ *
+ * @return {@code true} if the policy allows the requested wake up and the request has been
+ * executed; {@code false} otherwise.
+ */
+ boolean wakeUpFromLid() {
+ return wakeUp(
+ SystemClock.uptimeMillis(),
+ mAllowTheaterModeWakeFromLidSwitch,
+ WAKE_REASON_LID,
+ "LID");
+ }
+
+ /**
+ * Wakes up to prevent sleeping when opening camera through power button.
+ *
+ * @return {@code true} if the policy allows the requested wake up and the request has been
+ * executed; {@code false} otherwise.
+ */
+ boolean wakeUpFromPowerKeyCameraGesture() {
+ return wakeUp(
+ SystemClock.uptimeMillis(),
+ mAllowTheaterModeWakeFromPowerKey,
+ WAKE_REASON_CAMERA_LAUNCH,
+ "CAMERA_GESTURE_PREVENT_LOCK");
+ }
+
+ /**
+ * Wake up from a wake gesture.
+ *
+ * @return {@code true} if the policy allows the requested wake up and the request has been
+ * executed; {@code false} otherwise.
+ */
+ boolean wakeUpFromWakeGesture() {
+ return wakeUp(
+ SystemClock.uptimeMillis(),
+ mAllowTheaterModeWakeFromWakeGesture,
+ WAKE_REASON_GESTURE,
+ "GESTURE");
+ }
+
+ private boolean wakeUp(
+ long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason, String details) {
+ final boolean isTheaterModeEnabled =
+ Settings.Global.getInt(
+ mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0) == 1;
+ if (!wakeInTheaterMode && isTheaterModeEnabled) {
+ if (DEBUG) Slog.d(TAG, "Unable to wake up from " + details);
+ return false;
+ }
+ mPowerManager.wakeUp(wakeTime, reason, "android.policy:" + details);
+ return true;
+ }
+}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ca66a66..6033220 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -152,6 +152,7 @@
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.utils.Slogf;
+import com.android.window.flags.Flags;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -795,6 +796,14 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
+ if (Flags.bundleClientTransactionFlag()) {
+ // mWmService.mResizingWindows is populated in #applySurfaceChangesTransaction()
+ handleResizingWindows();
+
+ // Called after #handleResizingWindows to include WindowStateResizeItem if any.
+ mWmService.mAtmService.getLifecycleManager().dispatchPendingTransactions();
+ }
+
// Send any pending task-info changes that were queued-up during a layout deferment
mWmService.mAtmService.mTaskOrganizerController.dispatchPendingEvents();
mWmService.mAtmService.mTaskFragmentOrganizerController.dispatchPendingEvents();
@@ -838,12 +847,11 @@
}
}
- handleResizingWindows();
+ if (!Flags.bundleClientTransactionFlag()) {
+ handleResizingWindows();
+ }
clearFrameChangingWindows();
- // Called after #handleResizingWindows to include WindowStateResizeItem if any.
- mWmService.mAtmService.getLifecycleManager().dispatchPendingTransactions();
-
if (mWmService.mDisplayFrozen) {
ProtoLog.v(WM_DEBUG_ORIENTATION,
"With display frozen, orientationChangeComplete=%b",
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index dda33f3..502912a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5748,7 +5748,6 @@
case INSETS_CHANGED: {
synchronized (mGlobalLock) {
if (mWindowsInsetsChanged > 0) {
- mWindowsInsetsChanged = 0;
// We need to update resizing windows and dispatch the new insets state
// to them.
mWindowPlacerLocked.performSurfacePlacement();
@@ -6848,6 +6847,7 @@
pw.println(defaultDisplayContent.getLastOrientation());
pw.print(" mWaitingForConfig=");
pw.println(defaultDisplayContent.mWaitingForConfig);
+ pw.print(" mWindowsInsetsChanged="); pw.println(mWindowsInsetsChanged);
mRotationWatcherController.dump(pw);
pw.print(" Animation settings: disabled="); pw.print(mAnimationsDisabled);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 58ade1b..949025c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1443,16 +1443,7 @@
this, mWindowFrames.getInsetsChangedInfo(),
configChanged, didFrameInsetsChange);
- if (insetsChanged) {
- mWindowFrames.setInsetsChanged(false);
- if (mWmService.mWindowsInsetsChanged > 0) {
- mWmService.mWindowsInsetsChanged--;
- }
- if (mWmService.mWindowsInsetsChanged == 0) {
- mWmService.mH.removeMessages(WindowManagerService.H.INSETS_CHANGED);
- }
- }
-
+ consumeInsetsChange();
onResizeHandled();
mWmService.makeWindowFreezingScreenIfNeededLocked(this);
@@ -2349,6 +2340,8 @@
mWmService.mTrustedPresentationListenerController.removeIgnoredWindowTokens(
getWindowToken());
+
+ consumeInsetsChange();
}
@Override
@@ -3722,6 +3715,16 @@
return mClient instanceof IWindow.Stub;
}
+ private void consumeInsetsChange() {
+ if (mWindowFrames.hasInsetsChanged()) {
+ mWindowFrames.setInsetsChanged(false);
+ mWmService.mWindowsInsetsChanged--;
+ if (mWmService.mWindowsInsetsChanged == 0) {
+ mWmService.mH.removeMessages(WindowManagerService.H.INSETS_CHANGED);
+ }
+ }
+ }
+
/**
* Called when the insets state changed.
*/
@@ -3729,10 +3732,10 @@
ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "notifyInsetsChanged for %s ", this);
if (!mWindowFrames.hasInsetsChanged()) {
mWindowFrames.setInsetsChanged(true);
+ mWmService.mWindowsInsetsChanged++;
// If the new InsetsState won't be dispatched before releasing WM lock, the following
// message will be executed.
- mWmService.mWindowsInsetsChanged++;
mWmService.mH.removeMessages(WindowManagerService.H.INSETS_CHANGED);
mWmService.mH.sendEmptyMessage(WindowManagerService.H.INSETS_CHANGED);
}
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index 4a2e1cb..686b2a8 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -65,7 +65,6 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.content.PackageMonitor;
import com.android.server.credentials.metrics.ApiName;
import com.android.server.credentials.metrics.ApiStatus;
import com.android.server.infra.AbstractMasterSystemService;
@@ -1204,6 +1203,9 @@
// If the app being removed matches any of the package names from
// this list then don't add it in the output.
Set<String> providers = new HashSet<>();
+ if (rawProviders == null || packageName == null) {
+ return providers;
+ }
for (String rawComponentName : rawProviders.split(":")) {
if (TextUtils.isEmpty(rawComponentName)
|| rawComponentName.equals("null")) {
diff --git a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
similarity index 98%
rename from services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
rename to services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
index 6f9b6fa..8f5d125 100644
--- a/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioPoliciesDeviceRouteControllerTest.java
+++ b/services/tests/media/mediarouterservicetest/src/com/android/server/media/AudioManagerRouteControllerTest.java
@@ -62,7 +62,7 @@
import java.util.Set;
@RunWith(JUnit4.class)
-public class AudioPoliciesDeviceRouteControllerTest {
+public class AudioManagerRouteControllerTest {
private static final String FAKE_ROUTE_NAME = "fake name";
private static final AudioDeviceInfo FAKE_AUDIO_DEVICE_INFO_BUILTIN_SPEAKER =
@@ -89,7 +89,7 @@
private Set<AudioDeviceInfo> mAvailableAudioDeviceInfos;
@Mock private AudioManager mMockAudioManager;
@Mock private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
- private AudioPoliciesDeviceRouteController mControllerUnderTest;
+ private AudioManagerRouteController mControllerUnderTest;
private AudioDeviceCallback mAudioDeviceCallback;
private AudioProductStrategy mMediaAudioProductStrategy;
@@ -116,7 +116,7 @@
BluetoothAdapter btAdapter =
realContext.getSystemService(BluetoothManager.class).getAdapter();
mControllerUnderTest =
- new AudioPoliciesDeviceRouteController(
+ new AudioManagerRouteController(
mockContext,
mMockAudioManager,
Looper.getMainLooper(),
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
index d2e83e9..9eeb4f3 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java
@@ -271,6 +271,7 @@
.thenReturn(true);
when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false);
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+ when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(null);
mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */);
diff --git a/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java
index fd1abff..d850c73 100644
--- a/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java
@@ -53,6 +53,12 @@
}
@Test
+ public void getStoredProviders_nullValue_success() {
+ Set<String> providers = CredentialManagerService.getStoredProviders(null, null);
+ assertThat(providers.size()).isEqualTo(0);
+ }
+
+ @Test
public void getStoredProviders_success() {
Set<String> providers =
CredentialManagerService.getStoredProviders(
diff --git a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
index 0961b7d..eb78961 100644
--- a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java
@@ -70,7 +70,6 @@
DeviceRouteController.createInstance(
mContext, Looper.getMainLooper(), mOnDeviceRouteChangedListener);
- Truth.assertThat(deviceRouteController)
- .isInstanceOf(AudioPoliciesDeviceRouteController.class);
+ Truth.assertThat(deviceRouteController).isInstanceOf(AudioManagerRouteController.class);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
index 21b8a94..dc1d2c5 100644
--- a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java
@@ -16,26 +16,23 @@
package com.android.server.os;
+import android.app.admin.flags.Flags;
+import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
+
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.when;
-import android.app.admin.DevicePolicyManager;
-import android.app.admin.flags.Flags;
import android.app.role.RoleManager;
import android.content.Context;
import android.os.Binder;
import android.os.BugreportManager.BugreportCallback;
-import android.os.BugreportParams;
import android.os.IBinder;
import android.os.IDumpstateListener;
import android.os.Process;
import android.os.RemoteException;
-import android.os.UserManager;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -51,8 +48,6 @@
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
import java.io.FileDescriptor;
import java.util.concurrent.CompletableFuture;
@@ -71,11 +66,6 @@
private BugreportManagerServiceImpl mService;
private BugreportManagerServiceImpl.BugreportFileManager mBugreportFileManager;
- @Mock
- private UserManager mMockUserManager;
- @Mock
- private DevicePolicyManager mMockDevicePolicyManager;
-
private int mCallingUid = 1234;
private String mCallingPackage = "test.package";
private AtomicFile mMappingFile;
@@ -85,17 +75,14 @@
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
mContext = InstrumentationRegistry.getInstrumentation().getContext();
mMappingFile = new AtomicFile(mContext.getFilesDir(), "bugreport-mapping.xml");
ArraySet<String> mAllowlistedPackages = new ArraySet<>();
mAllowlistedPackages.add(mContext.getPackageName());
mService = new BugreportManagerServiceImpl(
- new TestInjector(mContext, mAllowlistedPackages, mMappingFile,
- mMockUserManager, mMockDevicePolicyManager));
+ new BugreportManagerServiceImpl.Injector(mContext, mAllowlistedPackages,
+ mMappingFile));
mBugreportFileManager = new BugreportManagerServiceImpl.BugreportFileManager(mMappingFile);
- // The calling user is an admin user by default.
- when(mMockUserManager.isUserAdmin(anyInt())).thenReturn(true);
}
@After
@@ -178,36 +165,6 @@
}
@Test
- public void testStartBugreport_throwsForNonAdminUser() throws Exception {
- when(mMockUserManager.isUserAdmin(anyInt())).thenReturn(false);
-
- Exception thrown = assertThrows(Exception.class,
- () -> mService.startBugreport(mCallingUid, mContext.getPackageName(),
- new FileDescriptor(), /* screenshotFd= */ null,
- BugreportParams.BUGREPORT_MODE_FULL,
- /* flags= */ 0, new Listener(new CountDownLatch(1)),
- /* isScreenshotRequested= */ false));
-
- assertThat(thrown.getMessage()).contains("not an admin user");
- }
-
- @Test
- public void testStartBugreport_throwsForNotAffiliatedUser() throws Exception {
- when(mMockUserManager.isUserAdmin(anyInt())).thenReturn(false);
- when(mMockDevicePolicyManager.getDeviceOwnerUserId()).thenReturn(-1);
- when(mMockDevicePolicyManager.isAffiliatedUser(anyInt())).thenReturn(false);
-
- Exception thrown = assertThrows(Exception.class,
- () -> mService.startBugreport(mCallingUid, mContext.getPackageName(),
- new FileDescriptor(), /* screenshotFd= */ null,
- BugreportParams.BUGREPORT_MODE_REMOTE,
- /* flags= */ 0, new Listener(new CountDownLatch(1)),
- /* isScreenshotRequested= */ false));
-
- assertThat(thrown.getMessage()).contains("not affiliated to the device owner");
- }
-
- @Test
public void testRetrieveBugreportWithoutFilesForCaller() throws Exception {
CountDownLatch latch = new CountDownLatch(1);
Listener listener = new Listener(latch);
@@ -250,8 +207,7 @@
private void clearAllowlist() {
mService = new BugreportManagerServiceImpl(
- new TestInjector(mContext, new ArraySet<>(), mMappingFile,
- mMockUserManager, mMockDevicePolicyManager));
+ new BugreportManagerServiceImpl.Injector(mContext, new ArraySet<>(), mMappingFile));
}
private static class Listener implements IDumpstateListener {
@@ -302,27 +258,4 @@
complete(successful);
}
}
-
- private static class TestInjector extends BugreportManagerServiceImpl.Injector {
-
- private final UserManager mUserManager;
- private final DevicePolicyManager mDevicePolicyManager;
-
- TestInjector(Context context, ArraySet<String> allowlistedPackages, AtomicFile mappingFile,
- UserManager um, DevicePolicyManager dpm) {
- super(context, allowlistedPackages, mappingFile);
- mUserManager = um;
- mDevicePolicyManager = dpm;
- }
-
- @Override
- public UserManager getUserManager() {
- return mUserManager;
- }
-
- @Override
- public DevicePolicyManager getDevicePolicyManager() {
- return mDevicePolicyManager;
- }
- }
}
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 3ab7496..0b459f6 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -16,11 +16,14 @@
package com.android.server.notification;
+import static android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS;
+import static android.Manifest.permission.STATUS_BAR_SERVICE;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_FOREGROUND_SERVICE;
import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.SHOW_IMMEDIATELY;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.Flags.FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS;
import static android.app.Notification.EXTRA_ALLOW_DURING_SETUP;
import static android.app.Notification.EXTRA_PICTURE;
import static android.app.Notification.EXTRA_PICTURE_ICON;
@@ -60,6 +63,8 @@
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import static android.app.PendingIntent.FLAG_MUTABLE;
import static android.app.PendingIntent.FLAG_ONE_SHOT;
+import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
+import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.PackageManager.FEATURE_TELECOM;
import static android.content.pm.PackageManager.FEATURE_WATCH;
@@ -547,6 +552,7 @@
mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager);
mContext.addMockSystemService(NotificationManager.class, mMockNm);
+ doNothing().when(mContext).sendBroadcast(any(), anyString());
doNothing().when(mContext).sendBroadcastAsUser(any(), any());
doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
@@ -909,7 +915,9 @@
}
private ApplicationInfo getApplicationInfo(String pkg, int uid) {
final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = pkg;
applicationInfo.uid = uid;
+ applicationInfo.sourceDir = mContext.getApplicationInfo().sourceDir;
switch (pkg) {
case PKG_N_MR1:
applicationInfo.targetSdkVersion = Build.VERSION_CODES.N_MR1;
@@ -5535,15 +5543,6 @@
@Test
public void testBumpFGImportance_channelChangePreOApp() throws Exception {
- String preOPkg = PKG_N_MR1;
- final ApplicationInfo legacy = new ApplicationInfo();
- legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
- when(mPackageManagerClient.getApplicationInfoAsUser(eq(preOPkg), anyInt(), anyInt()))
- .thenReturn(legacy);
- when(mPackageManagerClient.getPackageUidAsUser(eq(preOPkg), anyInt()))
- .thenReturn(Binder.getCallingUid());
- getContext().setMockPackageManager(mPackageManagerClient);
-
Notification.Builder nb = new Notification.Builder(mContext,
NotificationChannel.DEFAULT_CHANNEL_ID)
.setContentTitle("foo")
@@ -5551,7 +5550,7 @@
.setFlag(FLAG_FOREGROUND_SERVICE, true)
.setPriority(Notification.PRIORITY_MIN);
- StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9,
+ StatusBarNotification sbn = new StatusBarNotification(PKG_N_MR1, PKG_N_MR1, 9,
"testBumpFGImportance_channelChangePreOApp",
Binder.getCallingUid(), 0, nb.build(),
UserHandle.getUserHandleForUid(Binder.getCallingUid()), null, 0);
@@ -5571,11 +5570,11 @@
.setFlag(FLAG_FOREGROUND_SERVICE, true)
.setPriority(Notification.PRIORITY_MIN);
- sbn = new StatusBarNotification(preOPkg, preOPkg, 9,
+ sbn = new StatusBarNotification(PKG_N_MR1, PKG_N_MR1, 9,
"testBumpFGImportance_channelChangePreOApp", Binder.getCallingUid(),
0, nb.build(), UserHandle.getUserHandleForUid(Binder.getCallingUid()), null, 0);
- mBinderService.enqueueNotificationWithTag(preOPkg, preOPkg,
+ mBinderService.enqueueNotificationWithTag(PKG_N_MR1, PKG_N_MR1,
"testBumpFGImportance_channelChangePreOApp",
sbn.getId(), sbn.getNotification(), sbn.getUserId());
waitForIdle();
@@ -5583,7 +5582,7 @@
mService.getNotificationRecord(sbn.getKey()).getImportance());
NotificationChannel defaultChannel = mBinderService.getNotificationChannel(
- preOPkg, mContext.getUserId(), preOPkg, NotificationChannel.DEFAULT_CHANNEL_ID);
+ PKG_N_MR1, mContext.getUserId(), PKG_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID);
assertEquals(IMPORTANCE_LOW, defaultChannel.getImportance());
}
@@ -14059,6 +14058,22 @@
any(), any());
}
+ @Test
+ @EnableFlags(FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS)
+ public void testSetPrivateNotificationsAllowed() throws Exception {
+ when(mContext.checkCallingPermission(CONTROL_KEYGUARD_SECURE_NOTIFICATIONS))
+ .thenReturn(PERMISSION_GRANTED);
+ mBinderService.setPrivateNotificationsAllowed(false);
+ Intent expected = new Intent(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED)
+ .putExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, false);
+ ArgumentCaptor<Intent> actual = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).sendBroadcast(actual.capture(), eq(STATUS_BAR_SERVICE));
+
+ assertEquals(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED, actual.getValue().getAction());
+ assertFalse(actual.getValue().getBooleanExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, true));
+ assertFalse(mBinderService.getPrivateNotificationsAllowed());
+ }
+
private NotificationRecord createAndPostNotification(Notification.Builder nb, String testName)
throws RemoteException {
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, testName, mUid, 0,