Merge "Support null service resources in ServiceWatcher" into main
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 2162e3a..68512b8 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -79,6 +79,7 @@
* implementation is described to the system through an AndroidManifest.xml's
* <instrumentation> tag.
*/
+@android.ravenwood.annotation.RavenwoodKeepPartialClass
public class Instrumentation {
/**
@@ -132,6 +133,7 @@
private UiAutomation mUiAutomation;
private final Object mAnimationCompleteLock = new Object();
+ @android.ravenwood.annotation.RavenwoodKeep
public Instrumentation() {
}
@@ -142,6 +144,7 @@
* reflection, but it will serve as noticeable discouragement from
* doing such a thing.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
private void checkInstrumenting(String method) {
// Check if we have an instrumentation context, as init should only get called by
// the system in startup processes that are being instrumented.
@@ -151,6 +154,11 @@
}
}
+ private void checkInstrumenting$ravenwood(String method) {
+ // At the moment, Ravenwood doesn't attach a Context, but we're only ever
+ // running code as part of tests, so we continue quietly
+ }
+
/**
* Returns if it is being called in an instrumentation environment.
*
@@ -2504,6 +2512,7 @@
* Takes control of the execution of messages on the specified looper until
* {@link TestLooperManager#release} is called.
*/
+ @android.ravenwood.annotation.RavenwoodKeep
public TestLooperManager acquireLooperManager(Looper looper) {
checkInstrumenting("acquireLooperManager");
return new TestLooperManager(looper);
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 0261f0a..1ac08ac 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -179,6 +179,14 @@
@Overridable
public static final long BLOCK_MUTABLE_IMPLICIT_PENDING_INTENT = 236704164L;
+ /**
+ * Validate options passed in as bundle.
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ public static final long PENDING_INTENT_OPTIONS_CHECK = 320664730L;
+
/** @hide */
@IntDef(flag = true,
value = {
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index ec181da..1f19f81 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -907,7 +907,10 @@
private InteractionHandler getHandler(InteractionHandler handler) {
return (view, pendingIntent, response) -> {
- AppWidgetManager.getInstance(mContext).noteAppWidgetTapped(mAppWidgetId);
+ AppWidgetManager manager = AppWidgetManager.getInstance(mContext);
+ if (manager != null) {
+ manager.noteAppWidgetTapped(mAppWidgetId);
+ }
if (handler != null) {
return handler.onInteraction(view, pendingIntent, response);
} else {
diff --git a/core/java/android/os/TestLooperManager.java b/core/java/android/os/TestLooperManager.java
index 5e7549f..4b16c1d 100644
--- a/core/java/android/os/TestLooperManager.java
+++ b/core/java/android/os/TestLooperManager.java
@@ -28,6 +28,7 @@
* The test code may use {@link #next()} to acquire messages that have been queued to this
* {@link Looper}/{@link MessageQueue} and then {@link #execute} to run any that desires.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class TestLooperManager {
private static final ArraySet<Looper> sHeldLoopers = new ArraySet<>();
diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
index e7b5dff6..93c2e0e 100644
--- a/core/tests/coretests/src/android/os/BundleTest.java
+++ b/core/tests/coretests/src/android/os/BundleTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
@@ -121,6 +122,14 @@
}
@Test
+ public void testEmpty() throws Exception {
+ assertNotNull(Bundle.EMPTY);
+ assertEquals(0, Bundle.EMPTY.size());
+
+ new Bundle(Bundle.EMPTY);
+ }
+
+ @Test
@IgnoreUnderRavenwood(blockedBy = ParcelFileDescriptor.class)
public void testCreateFromParcel() throws Exception {
boolean withFd;
diff --git a/core/tests/coretests/src/android/os/TestLooperManagerTest.java b/core/tests/coretests/src/android/os/TestLooperManagerTest.java
new file mode 100644
index 0000000..5959444
--- /dev/null
+++ b/core/tests/coretests/src/android/os/TestLooperManagerTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class TestLooperManagerTest {
+ private static final String TAG = "TestLooperManagerTest";
+
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
+ .setProvideMainThread(true)
+ .build();
+
+ @Test
+ public void testMainThread() throws Exception {
+ doTest(Looper.getMainLooper());
+ }
+
+ @Test
+ public void testCustomThread() throws Exception {
+ final HandlerThread thread = new HandlerThread(TAG);
+ thread.start();
+ doTest(thread.getLooper());
+ }
+
+ private void doTest(Looper looper) throws Exception {
+ final TestLooperManager tlm =
+ InstrumentationRegistry.getInstrumentation().acquireLooperManager(looper);
+
+ final Handler handler = new Handler(looper);
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ assertFalse(tlm.hasMessages(handler, null, 42));
+
+ handler.sendEmptyMessage(42);
+ handler.post(() -> {
+ latch.countDown();
+ });
+ assertTrue(tlm.hasMessages(handler, null, 42));
+ assertFalse(latch.await(100, TimeUnit.MILLISECONDS));
+
+ final Message first = tlm.next();
+ assertEquals(42, first.what);
+ assertNull(first.callback);
+ tlm.execute(first);
+ assertFalse(tlm.hasMessages(handler, null, 42));
+ assertFalse(latch.await(100, TimeUnit.MILLISECONDS));
+ tlm.recycle(first);
+
+ final Message second = tlm.next();
+ assertNotNull(second.callback);
+ tlm.execute(second);
+ assertFalse(tlm.hasMessages(handler, null, 42));
+ assertTrue(latch.await(100, TimeUnit.MILLISECONDS));
+ tlm.recycle(second);
+
+ tlm.release();
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 4ba05ce..1f7cc5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -345,7 +345,7 @@
mTaskOperations.injectBackKey();
} else if (id == R.id.caption_handle || id == R.id.open_menu_button) {
if (!decoration.isHandleMenuActive()) {
- moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId));
+ moveTaskToFront(decoration.mTaskInfo);
decoration.createHandleMenu();
} else {
decoration.closeHandleMenu();
@@ -419,10 +419,10 @@
&& id != R.id.maximize_window) {
return false;
}
- moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId));
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
+ moveTaskToFront(decoration.mTaskInfo);
if (!mHasLongClicked && id != R.id.maximize_window) {
- final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
decoration.closeMaximizeMenuIfNeeded(e);
}
@@ -466,7 +466,8 @@
*/
@Override
public boolean handleMotionEvent(@Nullable View v, MotionEvent e) {
- final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
+ final RunningTaskInfo taskInfo = decoration.mTaskInfo;
if (DesktopModeStatus.isEnabled()
&& taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
return false;
@@ -492,8 +493,6 @@
}
case MotionEvent.ACTION_MOVE: {
mShouldClick = false;
- final DesktopModeWindowDecoration decoration =
- mWindowDecorByTaskId.get(mTaskId);
// If a decor's resize drag zone is active, don't also try to reposition it.
if (decoration.isHandlingDragResize()) break;
decoration.closeMaximizeMenu();
@@ -557,9 +556,10 @@
&& action != MotionEvent.ACTION_CANCEL)) {
return false;
}
- final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
- mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo,
- mWindowDecorByTaskId.get(taskInfo.taskId)));
+ mDesktopTasksController.ifPresent(c -> {
+ final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
+ c.toggleDesktopTaskSize(decoration.mTaskInfo, decoration);
+ });
return true;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index b3ffd2f..3f0a281 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -36,7 +36,6 @@
import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.os.Handler;
-import android.util.Log;
import android.view.Choreographer;
import android.view.MotionEvent;
import android.view.SurfaceControl;
@@ -392,23 +391,16 @@
}
private void loadAppInfo() {
- String packageName = mTaskInfo.realActivity.getPackageName();
PackageManager pm = mContext.getApplicationContext().getPackageManager();
- try {
- final IconProvider provider = new IconProvider(mContext);
- mAppIconDrawable = provider.getIcon(pm.getActivityInfo(mTaskInfo.baseActivity,
- PackageManager.ComponentInfoFlags.of(0)));
- final Resources resources = mContext.getResources();
- final BaseIconFactory factory = new BaseIconFactory(mContext,
- resources.getDisplayMetrics().densityDpi,
- resources.getDimensionPixelSize(R.dimen.desktop_mode_caption_icon_radius));
- mAppIconBitmap = factory.createScaledBitmap(mAppIconDrawable, MODE_DEFAULT);
- final ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName,
- PackageManager.ApplicationInfoFlags.of(0));
- mAppName = pm.getApplicationLabel(applicationInfo);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Package not found: " + packageName, e);
- }
+ final IconProvider provider = new IconProvider(mContext);
+ mAppIconDrawable = provider.getIcon(mTaskInfo.topActivityInfo);
+ final Resources resources = mContext.getResources();
+ final BaseIconFactory factory = new BaseIconFactory(mContext,
+ resources.getDisplayMetrics().densityDpi,
+ resources.getDimensionPixelSize(R.dimen.desktop_mode_caption_icon_radius));
+ mAppIconBitmap = factory.createScaledBitmap(mAppIconDrawable, MODE_DEFAULT);
+ final ApplicationInfo applicationInfo = mTaskInfo.topActivityInfo.applicationInfo;
+ mAppName = pm.getApplicationLabel(applicationInfo);
}
private void closeDragResizeListener() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
index 368231e..b0d3b50 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
@@ -125,6 +125,7 @@
relayout(taskBounds, t);
if (fadeIn) {
+ cancelAnimation();
mVeilAnimator = new ValueAnimator();
mVeilAnimator.setFloatValues(0f, 1f);
mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION);
@@ -210,15 +211,16 @@
* Animate veil's alpha to 0, fading it out.
*/
public void hideVeil() {
- final ValueAnimator animator = new ValueAnimator();
- animator.setFloatValues(1, 0);
- animator.setDuration(RESIZE_ALPHA_DURATION);
- animator.addUpdateListener(animation -> {
+ cancelAnimation();
+ mVeilAnimator = new ValueAnimator();
+ mVeilAnimator.setFloatValues(1, 0);
+ mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION);
+ mVeilAnimator.addUpdateListener(animation -> {
SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
- t.setAlpha(mVeilSurface, 1 - animator.getAnimatedFraction());
+ t.setAlpha(mVeilSurface, 1 - mVeilAnimator.getAnimatedFraction());
t.apply();
});
- animator.addListener(new AnimatorListenerAdapter() {
+ mVeilAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
@@ -226,7 +228,7 @@
t.apply();
}
});
- animator.start();
+ mVeilAnimator.start();
}
@ColorRes
@@ -240,10 +242,20 @@
}
}
+ private void cancelAnimation() {
+ if (mVeilAnimator != null) {
+ mVeilAnimator.removeAllUpdateListeners();
+ mVeilAnimator.cancel();
+ }
+ }
+
/**
* Dispose of veil when it is no longer needed, likely on close of its container decor.
*/
void dispose() {
+ cancelAnimation();
+ mVeilAnimator = null;
+
if (mViewHost != null) {
mViewHost.release();
mViewHost = null;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 193f16d..40e61dd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -27,6 +27,8 @@
import android.app.ActivityManager;
import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -202,6 +204,8 @@
.setTaskDescriptionBuilder(taskDescriptionBuilder)
.setVisible(visible)
.build();
+ taskInfo.topActivityInfo = new ActivityInfo();
+ taskInfo.topActivityInfo.applicationInfo = new ApplicationInfo();
taskInfo.realActivity = new ComponentName("com.android.wm.shell.windowdecor",
"DesktopModeWindowDecorationTests");
taskInfo.baseActivity = new ComponentName("com.android.wm.shell.windowdecor",
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
index 7b58531..1404d7c 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl
@@ -63,6 +63,8 @@
void onRequestTvRecordingInfoList(in int type, int seq);
void onRequestSigning(in String id, in String algorithm, in String alias, in byte[] data,
int seq);
+ void onRequestSigning2(in String id, in String algorithm, in String host,
+ int port, in byte[] data, int seq);
void onRequestCertificate(in String host, int port, int seq);
void onAdRequest(in AdRequest request, int Seq);
}
diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
index cb89181..3c91a3e 100644
--- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl
@@ -61,6 +61,7 @@
void onRequestTvRecordingInfo(in String recordingId);
void onRequestTvRecordingInfoList(in int type);
void onRequestSigning(in String id, in String algorithm, in String alias, in byte[] data);
+ void onRequestSigning2(in String id, in String algorithm, in String host, int port, in byte[] data);
void onRequestCertificate(in String host, int port);
void onAdRequest(in AdRequest request);
}
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
index 011744f..498eec6 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java
@@ -657,6 +657,19 @@
}
@Override
+ public void onRequestSigning2(
+ String id, String algorithm, String host, int port, byte[] data, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postRequestSigning(id, algorithm, host, port, data);
+ }
+ }
+
+ @Override
public void onRequestCertificate(String host, int port, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
@@ -2258,6 +2271,17 @@
});
}
+ void postRequestSigning(String id, String algorithm, String host, int port,
+ byte[] data) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onRequestSigning(mSession, id, algorithm, host,
+ port, data);
+ }
+ });
+ }
+
void postRequestCertificate(String host, int port) {
mHandler.post(new Runnable() {
@Override
@@ -2609,6 +2633,25 @@
}
/**
+ * This is called when
+ * {@link TvInteractiveAppService.Session#requestSigning(String, String, String, int, byte[])} is
+ * called.
+ *
+ * @param session A {@link TvInteractiveAppService.Session} associated with this callback.
+ * @param signingId the ID to identify the request.
+ * @param algorithm the standard name of the signature algorithm requested, such as
+ * MD5withRSA, SHA256withDSA, etc.
+ * @param host The host of the SSL CLient Authentication Server
+ * @param port The port of the SSL Client Authentication Server
+ * @param data the original bytes to be signed.
+ * @hide
+ */
+ public void onRequestSigning(
+ Session session, String signingId, String algorithm, String host,
+ int port, byte[] data) {
+ }
+
+ /**
* This is called when the service requests a SSL certificate for client validation.
*
* @param session A {@link TvInteractiveAppService.Session} associated with this callback.
diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
index 054b272..7b6dc38 100755
--- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java
+++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java
@@ -1645,6 +1645,50 @@
}
/**
+ * Requests signing of the given data.
+ *
+ * <p>This is used when the corresponding server of the broadcast-independent interactive
+ * app requires signing during handshaking, and the interactive app service doesn't have
+ * the built-in private key. The private key is provided by the content providers and
+ * pre-built in the related app, such as TV app.
+ *
+ * @param signingId the ID to identify the request. When a result is received, this ID can
+ * be used to correlate the result with the request.
+ * @param algorithm the standard name of the signature algorithm requested, such as
+ * MD5withRSA, SHA256withDSA, etc. The name is from standards like
+ * FIPS PUB 186-4 and PKCS #1.
+ * @param host the host of the SSL client authentication server.
+ * @param port the port of the SSL client authentication server.
+ * @param data the original bytes to be signed.
+ *
+ * @see #onSigningResult(String, byte[])
+ * @see TvInteractiveAppView#createBiInteractiveApp(Uri, Bundle)
+ * @see TvInteractiveAppView#BI_INTERACTIVE_APP_KEY_ALIAS
+ * @hide
+ */
+ @CallSuper
+ public void requestSigning(@NonNull String signingId, @NonNull String algorithm,
+ @NonNull String host, int port, @NonNull byte[] data) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "requestSigning");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onRequestSigning2(signingId, algorithm,
+ host, port, data);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in requestSigning", e);
+ }
+ }
+ });
+ }
+
+ /**
* Requests a SSL certificate for client validation.
*
* @param host the host name of the SSL authentication server.
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index a2530d5..375fe13 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -371,3 +371,10 @@
description: "Enables refactored logic for SysUI+WM unlock/occlusion code paths"
bug: "278086361"
}
+
+flag {
+ name: "enable_keyguard_compose"
+ namespace: "systemui"
+ description: "Enables the compose version of keyguard."
+ bug: "301968149"
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index ade417d..64fcef5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -125,6 +125,7 @@
import com.android.systemui.charging.WirelessChargingAnimation;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -245,6 +246,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Executor;
+import java.util.function.Consumer;
import javax.inject.Inject;
import javax.inject.Named;
@@ -551,6 +553,25 @@
private final WakefulnessLifecycle mWakefulnessLifecycle;
protected final PowerInteractor mPowerInteractor;
+ private final CommunalInteractor mCommunalInteractor;
+
+ /**
+ * True if the device is showing the glanceable hub. See
+ * {@link CommunalInteractor#isIdleOnCommunal()} for more details.
+ */
+ private boolean mIsIdleOnCommunal = false;
+ private final Consumer<Boolean> mIdleOnCommunalConsumer = (Boolean idleOnCommunal) -> {
+ if (idleOnCommunal == mIsIdleOnCommunal) {
+ // Ignore initial value coming through the flow.
+ return;
+ }
+
+ mIsIdleOnCommunal = idleOnCommunal;
+ // Trigger an update for the scrim state when we enter or exit glanceable hub, so that we
+ // can transition to/from ScrimState.GLANCEABLE_HUB if needed.
+ updateScrimController();
+ };
+
private boolean mNoAnimationOnNextBarModeChange;
private final SysuiStatusBarStateController mStatusBarStateController;
@@ -618,6 +639,7 @@
ScreenLifecycle screenLifecycle,
WakefulnessLifecycle wakefulnessLifecycle,
PowerInteractor powerInteractor,
+ CommunalInteractor communalInteractor,
SysuiStatusBarStateController statusBarStateController,
Optional<Bubbles> bubblesOptional,
Lazy<NoteTaskController> noteTaskControllerLazy,
@@ -722,6 +744,7 @@
mScreenLifecycle = screenLifecycle;
mWakefulnessLifecycle = wakefulnessLifecycle;
mPowerInteractor = powerInteractor;
+ mCommunalInteractor = communalInteractor;
mStatusBarStateController = statusBarStateController;
mBubblesOptional = bubblesOptional;
mNoteTaskControllerLazy = noteTaskControllerLazy;
@@ -1051,6 +1074,10 @@
//TODO(b/264502026) move the rest of the listeners here.
mDeviceStateManager.registerCallback(mMainExecutor,
new FoldStateListener(mContext, this::onFoldedStateChanged));
+
+ mJavaAdapter.alwaysCollectFlow(
+ mCommunalInteractor.isIdleOnCommunal(),
+ mIdleOnCommunalConsumer);
}
/**
@@ -2795,6 +2822,8 @@
// This will cancel the keyguardFadingAway animation if it is running. We need to do
// this as otherwise it can remain pending and leave keyguard in a weird state.
mUnlockScrimCallback.onCancelled();
+ } else if (mIsIdleOnCommunal) {
+ mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB);
} else if (mKeyguardStateController.isShowing()
&& !mKeyguardStateController.isOccluded()
&& !unlocking) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 3f20eaf..6f78604 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -17,7 +17,9 @@
package com.android.systemui.statusbar.phone;
import static com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER;
+import static com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB;
import static com.android.systemui.keyguard.shared.model.KeyguardState.GONE;
+import static com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN;
import static com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
@@ -62,6 +64,7 @@
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
+import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.ScrimAlpha;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
@@ -292,6 +295,30 @@
mScrimBehind.setViewAlpha(mBehindAlpha);
};
+ /**
+ * Consumer that fades the behind scrim in and out during the transition between the lock screen
+ * and the glanceable hub.
+ *
+ * While the lock screen is showing, the behind scrim is used to slightly darken the lock screen
+ * wallpaper underneath. Since the glanceable hub is under all of the scrims, we want to fade
+ * out the scrim so that the glanceable hub isn't darkened when it opens.
+ *
+ * {@link #applyState()} handles the scrim alphas once on the glanceable hub, this is only
+ * responsible for setting the behind alpha during the transition.
+ */
+ private final Consumer<TransitionStep> mGlanceableHubConsumer = (TransitionStep step) -> {
+ final float baseAlpha = ScrimState.KEYGUARD.getBehindAlpha();
+ final float transitionProgress = step.getValue();
+ if (step.getTo() == KeyguardState.LOCKSCREEN) {
+ // Transitioning back to lock screen, fade in behind scrim again.
+ mBehindAlpha = baseAlpha * transitionProgress;
+ } else if (step.getTo() == GLANCEABLE_HUB) {
+ // Transitioning to glanceable hub, fade out behind scrim.
+ mBehindAlpha = baseAlpha * (1 - transitionProgress);
+ }
+ mScrimBehind.setViewAlpha(mBehindAlpha);
+ };
+
Consumer<TransitionStep> mBouncerToGoneTransition;
@Inject
@@ -444,6 +471,14 @@
mBouncerToGoneTransition, mMainDispatcher);
collectFlow(behindScrim, mAlternateBouncerToGoneTransitionViewModel.getScrimAlpha(),
mScrimAlphaConsumer, mMainDispatcher);
+
+ // LOCKSCREEN<->GLANCEABLE_HUB
+ collectFlow(behindScrim,
+ mKeyguardTransitionInteractor.transition(LOCKSCREEN, GLANCEABLE_HUB),
+ mGlanceableHubConsumer, mMainDispatcher);
+ collectFlow(behindScrim,
+ mKeyguardTransitionInteractor.transition(GLANCEABLE_HUB, LOCKSCREEN),
+ mGlanceableHubConsumer, mMainDispatcher);
}
// TODO(b/270984686) recompute scrim height accurately, based on shade contents.
@@ -815,9 +850,9 @@
return;
}
mBouncerHiddenFraction = bouncerHiddenAmount;
- if (mState == ScrimState.DREAMING) {
- // Only the dreaming state requires this for the scrim calculation, so we should
- // only trigger an update if dreaming.
+ if (mState == ScrimState.DREAMING || mState == ScrimState.GLANCEABLE_HUB) {
+ // The dreaming and glanceable hub states requires this for the scrim calculation, so we
+ // should only trigger an update in those states.
applyAndDispatchState();
}
}
@@ -939,7 +974,7 @@
} else if (mState == ScrimState.AUTH_SCRIMMED_SHADE) {
mNotificationsAlpha = (float) Math.pow(getInterpolatedFraction(), 0.8f);
} else if (mState == ScrimState.KEYGUARD || mState == ScrimState.SHADE_LOCKED
- || mState == ScrimState.PULSING) {
+ || mState == ScrimState.PULSING || mState == ScrimState.GLANCEABLE_HUB) {
Pair<Integer, Float> result = calculateBackStateForState(mState);
int behindTint = result.first;
float behindAlpha = result.second;
@@ -950,6 +985,11 @@
mTransitionToFullShadeProgress);
behindTint = ColorUtils.blendARGB(behindTint, shadeResult.first,
mTransitionToFullShadeProgress);
+ } else if (mState == ScrimState.GLANCEABLE_HUB && mTransitionToFullShadeProgress == 0.0f
+ && mBouncerHiddenFraction == KeyguardBouncerConstants.EXPANSION_HIDDEN) {
+ // Behind scrim should not be visible when idle on the glanceable hub and neither
+ // bouncer nor shade are showing.
+ behindAlpha = 0f;
}
mInFrontAlpha = mState.getFrontAlpha();
if (mClipsQsScrim) {
@@ -965,6 +1005,13 @@
} else if (mState == ScrimState.SHADE_LOCKED) {
// going from KEYGUARD to SHADE_LOCKED state
mNotificationsAlpha = getInterpolatedFraction();
+ } else if (mState == ScrimState.GLANCEABLE_HUB
+ && mTransitionToFullShadeProgress == 0.0f) {
+ // Notification scrim should not be visible on the glanceable hub unless the
+ // shade is showing or transitioning in. Otherwise the notification scrim will
+ // be visible as the bouncer transitions in or after the notification shade
+ // closes.
+ mNotificationsAlpha = 0;
} else {
mNotificationsAlpha = Math.max(1.0f - getInterpolatedFraction(), mQsExpansion);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 61bd112..f2a649b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -296,6 +296,21 @@
updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor);
}
}
+ },
+
+ /**
+ * Device is locked or on dream and user has swiped from the right edge to enter the glanceable
+ * hub UI. From this state, the user can swipe from the left edge to go back to the lock screen
+ * or dream, as well as swipe down for the notifications and up for the bouncer.
+ */
+ GLANCEABLE_HUB {
+ @Override
+ public void prepare(ScrimState previousState) {
+ // No scrims should be visible by default in this state.
+ mBehindAlpha = 0;
+ mNotifAlpha = 0;
+ mFrontAlpha = 0;
+ }
};
boolean mBlankScreen = false;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 9c4984e..849a13b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -41,6 +41,8 @@
import static java.util.Collections.emptySet;
+import static kotlinx.coroutines.flow.FlowKt.flowOf;
+
import android.app.ActivityManager;
import android.app.IWallpaperManager;
import android.app.WallpaperManager;
@@ -77,7 +79,6 @@
import com.android.internal.logging.testing.FakeMetricsLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.TestScopeProvider;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
@@ -92,6 +93,10 @@
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.communal.data.repository.CommunalRepository;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
+import com.android.systemui.communal.shared.model.CommunalSceneKey;
+import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
@@ -102,6 +107,7 @@
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.notetask.NoteTaskController;
@@ -195,6 +201,8 @@
import javax.inject.Provider;
+import kotlinx.coroutines.test.TestScope;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@@ -203,11 +211,17 @@
private static final int FOLD_STATE_FOLDED = 0;
private static final int FOLD_STATE_UNFOLDED = 1;
+ private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
+
private CentralSurfacesImpl mCentralSurfaces;
private FakeMetricsLogger mMetricsLogger;
private PowerManager mPowerManager;
private VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider;
+
+ private final TestScope mTestScope = mKosmos.getTestScope();
+ private final CommunalInteractor mCommunalInteractor = mKosmos.getCommunalInteractor();
+ private final CommunalRepository mCommunalRepository = mKosmos.getCommunalRepository();
@Mock private NotificationsController mNotificationsController;
@Mock private LightBarController mLightBarController;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -461,7 +475,7 @@
new DisplayMetrics(),
mMetricsLogger,
mShadeLogger,
- new JavaAdapter(TestScopeProvider.getTestScope()),
+ new JavaAdapter(mTestScope),
mUiBgExecutor,
mNotificationPanelViewController,
mNotificationMediaManager,
@@ -473,6 +487,7 @@
mScreenLifecycle,
mWakefulnessLifecycle,
mPowerInteractor,
+ mCommunalInteractor,
mStatusBarStateController,
Optional.of(mBubbles),
() -> mNoteTaskController,
@@ -821,6 +836,25 @@
}
@Test
+ public void testEnteringGlanceableHub_updatesScrim() {
+ // Transition to the glanceable hub.
+ mCommunalRepository.setTransitionState(flowOf(new ObservableCommunalTransitionState.Idle(
+ CommunalSceneKey.Communal.INSTANCE)));
+ mTestScope.getTestScheduler().runCurrent();
+
+ // ScrimState also transitions.
+ verify(mScrimController).transitionTo(ScrimState.GLANCEABLE_HUB);
+
+ // Transition away from the glanceable hub.
+ mCommunalRepository.setTransitionState(flowOf(new ObservableCommunalTransitionState.Idle(
+ CommunalSceneKey.Blank.INSTANCE)));
+ mTestScope.getTestScheduler().runCurrent();
+
+ // ScrimState goes back to UNLOCKED.
+ verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
+ }
+
+ @Test
public void testShowKeyguardImplementation_setsState() {
when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 423cc84..3bde6e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -51,6 +51,7 @@
import android.graphics.Color;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.testing.ViewUtils;
import android.util.MathUtils;
import android.view.View;
@@ -59,13 +60,13 @@
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.TestScopeProvider;
import com.android.systemui.DejankUtils;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.model.KeyguardState;
@@ -73,6 +74,7 @@
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.scrim.ScrimView;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
import com.android.systemui.shade.transition.LinearLargeScreenShadeInterpolator;
@@ -103,7 +105,6 @@
import java.util.HashSet;
import java.util.Map;
-import kotlinx.coroutines.CoroutineDispatcher;
import kotlinx.coroutines.test.TestScope;
@RunWith(AndroidTestingRunner.class)
@@ -112,13 +113,14 @@
public class ScrimControllerTest extends SysuiTestCase {
@Rule public Expect mExpect = Expect.create();
+ private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
private final FakeConfigurationController mConfigurationController =
new FakeConfigurationController();
private final LargeScreenShadeInterpolator
mLinearLargeScreenShadeInterpolator = new LinearLargeScreenShadeInterpolator();
- private final TestScope mTestScope = TestScopeProvider.getTestScope();
+ private final TestScope mTestScope = mKosmos.getTestScope();
private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope());
private ScrimController mScrimController;
@@ -145,10 +147,12 @@
@Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
@Mock private AlternateBouncerToGoneTransitionViewModel
mAlternateBouncerToGoneTransitionViewModel;
- @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
+ private final KeyguardTransitionInteractor mKeyguardTransitionInteractor =
+ mKosmos.getKeyguardTransitionInteractor();
+ private final FakeKeyguardTransitionRepository mKeyguardTransitionRepository =
+ mKosmos.getKeyguardTransitionRepository();
@Mock private KeyguardInteractor mKeyguardInteractor;
private final FakeWallpaperRepository mWallpaperRepository = new FakeWallpaperRepository();
- @Mock private CoroutineDispatcher mMainDispatcher;
@Mock private TypedArray mMockTypedArray;
// TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The
@@ -265,8 +269,6 @@
when(mDelayedWakeLockFactory.create(any(String.class))).thenReturn(mWakeLock);
when(mDockManager.isDocked()).thenReturn(false);
- when(mKeyguardTransitionInteractor.transition(any(), any()))
- .thenReturn(emptyFlow());
when(mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha())
.thenReturn(emptyFlow());
when(mAlternateBouncerToGoneTransitionViewModel.getScrimAlpha())
@@ -292,13 +294,16 @@
mKeyguardTransitionInteractor,
mKeyguardInteractor,
mWallpaperRepository,
- mMainDispatcher,
+ mKosmos.getTestDispatcher(),
mLinearLargeScreenShadeInterpolator);
mScrimController.start();
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener);
+ // Attach behind scrim so flows that are collecting on it start running.
+ ViewUtils.attachView(mScrimBehind);
+
mScrimController.setHasBackdrop(false);
mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false);
@@ -629,6 +634,164 @@
}
@Test
+ public void lockscreenToHubTransition_setsBehindScrimAlpha() {
+ // Start on lockscreen.
+ mScrimController.transitionTo(ScrimState.KEYGUARD);
+ finishAnimationsImmediately();
+
+ // Behind scrim starts at default alpha.
+ final float transitionProgress = 0f;
+ float expectedAlpha = ScrimState.KEYGUARD.getBehindAlpha();
+ mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(),
+ new TransitionStep(
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.GLANCEABLE_HUB,
+ transitionProgress,
+ TransitionState.STARTED
+ ), true);
+ mTestScope.getTestScheduler().runCurrent();
+ assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha);
+
+ // Scrim fades out as transition runs.
+ final float runningProgress = 0.2f;
+ expectedAlpha = (1 - runningProgress) * ScrimState.KEYGUARD.getBehindAlpha();
+ mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(),
+ new TransitionStep(
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.GLANCEABLE_HUB,
+ runningProgress,
+ TransitionState.RUNNING
+ ), true);
+ mTestScope.getTestScheduler().runCurrent();
+ assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha);
+
+ // Scrim invisible at end of transition.
+ final float finishedProgress = 1f;
+ expectedAlpha = 0f;
+ mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(),
+ new TransitionStep(
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.GLANCEABLE_HUB,
+ finishedProgress,
+ TransitionState.FINISHED
+ ), true);
+ mTestScope.getTestScheduler().runCurrent();
+ assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha);
+ }
+
+ @Test
+ public void hubToLockscreenTransition_setsViewAlpha() {
+ // Start on glanceable hub.
+ mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB);
+ finishAnimationsImmediately();
+
+ // Behind scrim starts at 0 alpha.
+ final float transitionProgress = 0f;
+ float expectedAlpha = 0f;
+ mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(),
+ new TransitionStep(
+ KeyguardState.GLANCEABLE_HUB,
+ KeyguardState.LOCKSCREEN,
+ transitionProgress,
+ TransitionState.STARTED
+ ), true);
+ mTestScope.getTestScheduler().runCurrent();
+ assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha);
+
+ // Scrim fades in as transition runs.
+ final float runningProgress = 0.2f;
+ expectedAlpha = runningProgress * ScrimState.KEYGUARD.getBehindAlpha();
+ mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(),
+ new TransitionStep(
+ KeyguardState.GLANCEABLE_HUB,
+ KeyguardState.LOCKSCREEN,
+ runningProgress,
+ TransitionState.RUNNING
+ ), true);
+ mTestScope.getTestScheduler().runCurrent();
+ assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha);
+
+ // Scrim at default visibility at end of transition.
+ final float finishedProgress = 1f;
+ expectedAlpha = finishedProgress * ScrimState.KEYGUARD.getBehindAlpha();
+ mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(),
+ new TransitionStep(
+ KeyguardState.GLANCEABLE_HUB,
+ KeyguardState.LOCKSCREEN,
+ finishedProgress,
+ TransitionState.FINISHED
+ ), true);
+ mTestScope.getTestScheduler().runCurrent();
+ assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha);
+ }
+
+ @Test
+ public void transitionToHub() {
+ mScrimController.setRawPanelExpansionFraction(0f);
+ mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_HIDDEN);
+ mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB);
+ finishAnimationsImmediately();
+
+ // All scrims transparent on the hub.
+ assertScrimAlpha(Map.of(
+ mScrimInFront, TRANSPARENT,
+ mNotificationsScrim, TRANSPARENT,
+ mScrimBehind, TRANSPARENT));
+ }
+
+ @Test
+ public void openBouncerOnHub() {
+ mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB);
+
+ // Open the bouncer.
+ mScrimController.setRawPanelExpansionFraction(0f);
+ mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_VISIBLE);
+ finishAnimationsImmediately();
+
+ // Only behind widget is visible.
+ assertScrimAlpha(Map.of(
+ mScrimInFront, TRANSPARENT,
+ mNotificationsScrim, TRANSPARENT,
+ mScrimBehind, OPAQUE));
+
+ // Bouncer is closed.
+ mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_HIDDEN);
+ mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB);
+ finishAnimationsImmediately();
+
+ // All scrims are transparent.
+ assertScrimAlpha(Map.of(
+ mScrimInFront, TRANSPARENT,
+ mNotificationsScrim, TRANSPARENT,
+ mScrimBehind, TRANSPARENT));
+ }
+
+ @Test
+ public void openShadeOnHub() {
+ mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB);
+
+ // Open the shade.
+ mScrimController.transitionTo(SHADE_LOCKED);
+ mScrimController.setQsPosition(1f, 0);
+ finishAnimationsImmediately();
+
+ // Shade scrims are visible.
+ assertScrimAlpha(Map.of(
+ mNotificationsScrim, OPAQUE,
+ mScrimInFront, TRANSPARENT,
+ mScrimBehind, OPAQUE));
+
+ mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB);
+ finishAnimationsImmediately();
+
+ // All scrims are transparent.
+ assertScrimAlpha(Map.of(
+ mScrimInFront, TRANSPARENT,
+ mNotificationsScrim, TRANSPARENT,
+ mScrimBehind, TRANSPARENT));
+ }
+
+ @Test
public void onThemeChange_bouncerBehindTint_isUpdatedToSurfaceColor() {
assertEquals(BOUNCER.getBehindTint(), 0x112233);
mSurfaceColor = 0x223344;
@@ -1001,7 +1164,7 @@
mKeyguardTransitionInteractor,
mKeyguardInteractor,
mWallpaperRepository,
- mMainDispatcher,
+ mKosmos.getTestDispatcher(),
mLinearLargeScreenShadeInterpolator);
mScrimController.start();
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
@@ -1267,7 +1430,7 @@
ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, BOUNCER,
ScrimState.DREAMING, ScrimState.BOUNCER_SCRIMMED, ScrimState.BRIGHTNESS_MIRROR,
ScrimState.UNLOCKED, SHADE_LOCKED, ScrimState.AUTH_SCRIMMED,
- ScrimState.AUTH_SCRIMMED_SHADE));
+ ScrimState.AUTH_SCRIMMED_SHADE, ScrimState.GLANCEABLE_HUB));
for (ScrimState state : ScrimState.values()) {
if (!lowPowerModeStates.contains(state) && !regularStates.contains(state)) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
index 0c1dbfe..e20a0ab 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -28,9 +28,12 @@
import java.util.UUID
import javax.inject.Inject
import junit.framework.Assert.fail
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -150,6 +153,15 @@
_transitions.emit(step)
}
+ /** Version of [sendTransitionStep] that's usable from Java tests. */
+ fun sendTransitionStepJava(
+ coroutineScope: CoroutineScope,
+ step: TransitionStep,
+ validateStep: Boolean = true
+ ): Job {
+ return coroutineScope.launch { sendTransitionStep(step, validateStep) }
+ }
+
suspend fun sendTransitionSteps(
steps: List<TransitionStep>,
testScope: TestScope,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 11f2938..083de10 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -32,6 +32,8 @@
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.model.sceneContainerPlugin
import com.android.systemui.plugins.statusbar.statusBarStateController
import com.android.systemui.power.data.repository.fakePowerRepository
@@ -61,6 +63,8 @@
val bouncerRepository by lazy { kosmos.bouncerRepository }
val communalRepository by lazy { kosmos.fakeCommunalRepository }
val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
+ val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
+ val keyguardTransitionInteractor by lazy { kosmos.keyguardTransitionInteractor }
val powerRepository by lazy { kosmos.fakePowerRepository }
val clock by lazy { kosmos.systemClock }
val mobileConnectionsRepository by lazy { kosmos.fakeMobileConnectionsRepository }
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index e013a3e..1ac69f6 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -30,6 +30,9 @@
"junit-src/**/*.java",
"junit-impl-src/**/*.java",
],
+ static_libs: [
+ "androidx.test.monitor-for-device",
+ ],
libs: [
"framework-minus-apex.ravenwood",
"junit",
@@ -61,3 +64,17 @@
"core-xml-for-host",
],
}
+
+java_host_for_device {
+ name: "androidx.test.monitor-for-device",
+ libs: [
+ "androidx.test.monitor-for-host",
+ ],
+}
+
+java_device_for_host {
+ name: "androidx.test.monitor-for-host",
+ libs: [
+ "androidx.test.monitor",
+ ],
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index b3dbcde..a797b1d 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -16,12 +16,35 @@
package android.platform.test.ravenwood;
+import android.app.Instrumentation;
+import android.os.Bundle;
import android.os.HandlerThread;
import android.os.Looper;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import java.io.PrintStream;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
public class RavenwoodRuleImpl {
private static final String MAIN_THREAD_NAME = "RavenwoodMain";
+ /**
+ * When enabled, attempt to dump all thread stacks just before we hit the
+ * overall Tradefed timeout, to aid in debugging deadlocks.
+ */
+ private static final boolean ENABLE_TIMEOUT_STACKS = false;
+ private static final int TIMEOUT_MILLIS = 9_000;
+
+ private static final ScheduledExecutorService sTimeoutExecutor =
+ Executors.newScheduledThreadPool(1);
+
+ private static ScheduledFuture<?> sPendingTimeout;
+
public static boolean isOnRavenwood() {
return true;
}
@@ -41,9 +64,22 @@
main.start();
Looper.setMainLooperForTest(main.getLooper());
}
+
+ InstrumentationRegistry.registerInstance(new Instrumentation(), Bundle.EMPTY);
+
+ if (ENABLE_TIMEOUT_STACKS) {
+ sPendingTimeout = sTimeoutExecutor.schedule(RavenwoodRuleImpl::dumpStacks,
+ TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ }
}
public static void reset(RavenwoodRule rule) {
+ if (ENABLE_TIMEOUT_STACKS) {
+ sPendingTimeout.cancel(false);
+ }
+
+ InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
+
if (rule.mProvideMainThread) {
Looper.getMainLooper().quit();
Looper.clearMainLooperForTest();
@@ -55,4 +91,19 @@
android.os.Binder.reset$ravenwood();
android.os.Process.reset$ravenwood();
}
+
+ private static void dumpStacks() {
+ final PrintStream out = System.err;
+ out.println("-----BEGIN ALL THREAD STACKS-----");
+ final Map<Thread, StackTraceElement[]> stacks = Thread.getAllStackTraces();
+ for (Map.Entry<Thread, StackTraceElement[]> stack : stacks.entrySet()) {
+ out.println();
+ Thread t = stack.getKey();
+ out.println(t.toString() + " ID=" + t.getId());
+ for (StackTraceElement e : stack.getValue()) {
+ out.println("\tat " + e);
+ }
+ }
+ out.println("-----END ALL THREAD STACKS-----");
+ }
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java
index 68b163e..8d76970 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java
@@ -19,6 +19,7 @@
import static android.platform.test.ravenwood.RavenwoodRule.ENABLE_PROBE_IGNORED;
import static android.platform.test.ravenwood.RavenwoodRule.IS_ON_RAVENWOOD;
import static android.platform.test.ravenwood.RavenwoodRule.shouldEnableOnRavenwood;
+import static android.platform.test.ravenwood.RavenwoodRule.shouldStillIgnoreInProbeIgnoreMode;
import android.platform.test.annotations.DisabledOnRavenwood;
import android.platform.test.annotations.EnabledOnRavenwood;
@@ -45,6 +46,7 @@
}
if (ENABLE_PROBE_IGNORED) {
+ Assume.assumeFalse(shouldStillIgnoreInProbeIgnoreMode(description));
// Pass through to possible underlying RavenwoodRule for both environment
// configuration and handling method-level annotations
return base;
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index 952ee0e..1e7cbf6 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -27,7 +27,9 @@
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.regex.Pattern;
/**
* {@code @Rule} that configures the Ravenwood test environment. This rule has no effect when
@@ -55,6 +57,43 @@
static final boolean ENABLE_PROBE_IGNORED = "1".equals(
System.getenv("RAVENWOOD_RUN_DISABLED_TESTS"));
+ /**
+ * When using ENABLE_PROBE_IGNORED, you may still want to skip certain tests,
+ * for example because the test would crash the JVM.
+ *
+ * This regex defines the tests that should still be disabled even if ENABLE_PROBE_IGNORED
+ * is set.
+ *
+ * Before running each test class and method, we check if this pattern can be found in
+ * the full test name (either [class full name], or [class full name] + "#" + [method name]),
+ * and if so, we skip it.
+ *
+ * For example, if you want to skip an entire test class, use:
+ * RAVENWOOD_REALLY_DISABLE='\.CustomTileDefaultsRepositoryTest$'
+ *
+ * For example, if you want to skip an entire test class, use:
+ * RAVENWOOD_REALLY_DISABLE='\.CustomTileDefaultsRepositoryTest#testSimple$'
+ *
+ * To ignore multiple classes, use (...|...), for example:
+ * RAVENWOOD_REALLY_DISABLE='\.(ClassA|ClassB)$'
+ *
+ * Because we use a regex-find, setting "." would disable all tests.
+ */
+ private static final Pattern REALLY_DISABLE_PATTERN = Pattern.compile(
+ Objects.requireNonNullElse(System.getenv("RAVENWOOD_REALLY_DISABLE"), ""));
+
+ private static final boolean ENABLE_REALLY_DISABLE_PATTERN =
+ !REALLY_DISABLE_PATTERN.pattern().isEmpty();
+
+ static {
+ if (ENABLE_PROBE_IGNORED) {
+ System.out.println("$RAVENWOOD_RUN_DISABLED_TESTS enabled: force running all tests");
+ if (ENABLE_REALLY_DISABLE_PATTERN) {
+ System.out.println("$RAVENWOOD_REALLY_DISABLE=" + REALLY_DISABLE_PATTERN.pattern());
+ }
+ }
+ }
+
private static final int SYSTEM_UID = 1000;
private static final int NOBODY_UID = 9999;
private static final int FIRST_APPLICATION_UID = 10000;
@@ -203,6 +242,21 @@
return true;
}
+ static boolean shouldStillIgnoreInProbeIgnoreMode(Description description) {
+ if (!ENABLE_REALLY_DISABLE_PATTERN) {
+ return false;
+ }
+
+ final var fullname = description.getTestClass().getName()
+ + (description.isTest() ? "#" + description.getMethodName() : "");
+
+ if (REALLY_DISABLE_PATTERN.matcher(fullname).find()) {
+ System.out.println("Still ignoring " + fullname);
+ return true;
+ }
+ return false;
+ }
+
@Override
public Statement apply(Statement base, Description description) {
// No special treatment when running outside Ravenwood; run tests as-is
@@ -245,6 +299,7 @@
return new Statement() {
@Override
public void evaluate() throws Throwable {
+ Assume.assumeFalse(shouldStillIgnoreInProbeIgnoreMode(description));
RavenwoodRuleImpl.init(RavenwoodRule.this);
try {
base.evaluate();
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index eaf01a3..b775f9a 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -72,6 +72,7 @@
android.os.ServiceSpecificException
android.os.SystemClock
android.os.SystemProperties
+android.os.TestLooperManager
android.os.ThreadLocalWorkSource
android.os.TimestampedValue
android.os.Trace
@@ -141,6 +142,8 @@
android.content.ContentProvider
+android.app.Instrumentation
+
android.metrics.LogMaker
android.view.Display$HdrCapabilities
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index b87184a..416b36f 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -288,22 +288,23 @@
CachedDeviceState.Readonly.class);
mBinderCallsStats.setDeviceState(deviceState);
- BatteryStatsInternal batteryStatsInternal = getLocalService(
- BatteryStatsInternal.class);
- mBinderCallsStats.setCallStatsObserver(new BinderInternal.CallStatsObserver() {
- @Override
- public void noteCallStats(int workSourceUid, long incrementalCallCount,
- Collection<BinderCallsStats.CallStat> callStats) {
- batteryStatsInternal.noteBinderCallStats(workSourceUid,
- incrementalCallCount, callStats);
- }
+ if (!com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) {
+ BatteryStatsInternal batteryStatsInternal = getLocalService(
+ BatteryStatsInternal.class);
+ mBinderCallsStats.setCallStatsObserver(new BinderInternal.CallStatsObserver() {
+ @Override
+ public void noteCallStats(int workSourceUid, long incrementalCallCount,
+ Collection<BinderCallsStats.CallStat> callStats) {
+ batteryStatsInternal.noteBinderCallStats(workSourceUid,
+ incrementalCallCount, callStats);
+ }
- @Override
- public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) {
- batteryStatsInternal.noteBinderThreadNativeIds(binderThreadNativeTids);
- }
- });
-
+ @Override
+ public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) {
+ batteryStatsInternal.noteBinderThreadNativeIds(binderThreadNativeTids);
+ }
+ });
+ }
// It needs to be called before mService.systemReady to make sure the observer is
// initialized before installing it.
mWorkSourceProvider.systemReady(getContext());
diff --git a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
index c391642..f619ca3 100644
--- a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
+++ b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java
@@ -27,6 +27,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.Trace;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
@@ -49,12 +50,12 @@
private static final String TAG = "SensitiveContentProtect";
private static final boolean DEBUG = false;
- @VisibleForTesting
- NotificationListener mNotificationListener;
+ @VisibleForTesting NotificationListener mNotificationListener;
private @Nullable MediaProjectionManager mProjectionManager;
private @Nullable WindowManagerInternal mWindowManager;
final Object mSensitiveContentProtectionLock = new Object();
+
@GuardedBy("mSensitiveContentProtectionLock")
private boolean mProjectionActive = false;
@@ -63,13 +64,24 @@
@Override
public void onStart(MediaProjectionInfo info) {
if (DEBUG) Log.d(TAG, "onStart projection: " + info);
- onProjectionStart();
+ Trace.beginSection(
+ "SensitiveContentProtectionManagerService.onProjectionStart");
+ try {
+ onProjectionStart();
+ } finally {
+ Trace.endSection();
+ }
}
@Override
public void onStop(MediaProjectionInfo info) {
if (DEBUG) Log.d(TAG, "onStop projection: " + info);
- onProjectionEnd();
+ Trace.beginSection("SensitiveContentProtectionManagerService.onProjectionStop");
+ try {
+ onProjectionEnd();
+ } finally {
+ Trace.endSection();
+ }
}
};
@@ -94,8 +106,7 @@
}
@VisibleForTesting
- void init(MediaProjectionManager projectionManager,
- WindowManagerInternal windowManager) {
+ void init(MediaProjectionManager projectionManager, WindowManagerInternal windowManager) {
if (DEBUG) Log.d(TAG, "init");
checkNotNull(projectionManager, "Failed to get valid MediaProjectionManager");
@@ -109,7 +120,8 @@
mProjectionManager.addCallback(mProjectionCallback, new Handler(Looper.getMainLooper()));
try {
- mNotificationListener.registerAsSystemService(getContext(),
+ mNotificationListener.registerAsSystemService(
+ getContext(),
new ComponentName(getContext(), NotificationListener.class),
UserHandle.USER_ALL);
} catch (RemoteException e) {
@@ -174,8 +186,8 @@
}
// notify windowmanager of any currently posted sensitive content notifications
- ArraySet<PackageInfo> packageInfos = getSensitivePackagesFromNotifications(
- notifications, rankingMap);
+ ArraySet<PackageInfo> packageInfos =
+ getSensitivePackagesFromNotifications(notifications, rankingMap);
mWindowManager.addBlockScreenCaptureForApps(packageInfos);
}
@@ -197,8 +209,8 @@
return sensitivePackages;
}
- private PackageInfo getSensitivePackageFromNotification(StatusBarNotification sbn,
- RankingMap rankingMap) {
+ private PackageInfo getSensitivePackageFromNotification(
+ StatusBarNotification sbn, RankingMap rankingMap) {
if (sbn == null) {
Log.w(TAG, "Unable to protect null notification");
return null;
@@ -220,38 +232,55 @@
@Override
public void onListenerConnected() {
super.onListenerConnected();
- // Projection started before notification listener was connected
- synchronized (mSensitiveContentProtectionLock) {
- if (mProjectionActive) {
- updateAppsThatShouldBlockScreenCapture();
+ Trace.beginSection("SensitiveContentProtectionManagerService.onListenerConnected");
+ try {
+ // Projection started before notification listener was connected
+ synchronized (mSensitiveContentProtectionLock) {
+ if (mProjectionActive) {
+ updateAppsThatShouldBlockScreenCapture();
+ }
}
+ } finally {
+ Trace.endSection();
}
}
@Override
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
super.onNotificationPosted(sbn, rankingMap);
- synchronized (mSensitiveContentProtectionLock) {
- if (!mProjectionActive) {
- return;
- }
+ Trace.beginSection("SensitiveContentProtectionManagerService.onNotificationPosted");
+ try {
+ synchronized (mSensitiveContentProtectionLock) {
+ if (!mProjectionActive) {
+ return;
+ }
- // notify windowmanager of any currently posted sensitive content notifications
- PackageInfo packageInfo = getSensitivePackageFromNotification(sbn, rankingMap);
+ // notify windowmanager of any currently posted sensitive content notifications
+ PackageInfo packageInfo = getSensitivePackageFromNotification(sbn, rankingMap);
- if (packageInfo != null) {
- mWindowManager.addBlockScreenCaptureForApps(new ArraySet(Set.of(packageInfo)));
+ if (packageInfo != null) {
+ mWindowManager.addBlockScreenCaptureForApps(
+ new ArraySet(Set.of(packageInfo)));
+ }
}
+ } finally {
+ Trace.endSection();
}
}
@Override
public void onNotificationRankingUpdate(RankingMap rankingMap) {
super.onNotificationRankingUpdate(rankingMap);
- synchronized (mSensitiveContentProtectionLock) {
- if (mProjectionActive) {
- updateAppsThatShouldBlockScreenCapture(rankingMap);
+ Trace.beginSection(
+ "SensitiveContentProtectionManagerService.onNotificationRankingUpdate");
+ try {
+ synchronized (mSensitiveContentProtectionLock) {
+ if (mProjectionActive) {
+ updateAppsThatShouldBlockScreenCapture(rankingMap);
+ }
}
+ } finally {
+ Trace.endSection();
}
}
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 3487ae3..4f46ecd 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -422,7 +422,9 @@
mStats.setExternalStatsSyncLocked(mWorker);
mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
- mStats.startTrackingSystemServerCpuTime();
+ if (!Flags.disableSystemServicePowerAttr()) {
+ mStats.startTrackingSystemServerCpuTime();
+ }
mAggregatedPowerStatsConfig = createAggregatedPowerStatsConfig();
mPowerStatsStore = new PowerStatsStore(systemDir, mHandler, mAggregatedPowerStatsConfig);
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index a20623c..5df9107 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -30,6 +30,7 @@
import android.app.AppGlobals;
import android.app.PendingIntent;
import android.app.PendingIntentStats;
+import android.app.compat.CompatChanges;
import android.content.IIntentSender;
import android.content.Intent;
import android.os.Binder;
@@ -136,6 +137,11 @@
+ "intent creator ("
+ packageName
+ ") because this option is meant for the pending intent sender");
+ if (CompatChanges.isChangeEnabled(PendingIntent.PENDING_INTENT_OPTIONS_CHECK,
+ callingUid)) {
+ throw new IllegalArgumentException("pendingIntentBackgroundActivityStartMode "
+ + "must not be set when creating a PendingIntent");
+ }
opts.setPendingIntentBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED);
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 10d5fd3..95e130e 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -406,6 +406,9 @@
String resolvedType, IBinder allowlistToken, IIntentReceiver finishedReceiver,
String requiredPermission, IBinder resultTo, String resultWho, int requestCode,
int flagsMask, int flagsValues, Bundle options) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+
if (intent != null) intent.setDefusable(true);
if (options != null) options.setDefusable(true);
@@ -458,6 +461,12 @@
+ key.packageName
+ ") because this option is meant for the pending intent "
+ "creator");
+ if (CompatChanges.isChangeEnabled(PendingIntent.PENDING_INTENT_OPTIONS_CHECK,
+ callingUid)) {
+ throw new IllegalArgumentException(
+ "pendingIntentCreatorBackgroundActivityStartMode "
+ + "must not be set when sending a PendingIntent");
+ }
opts.setPendingIntentCreatorBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED);
}
@@ -494,9 +503,6 @@
}
// We don't hold the controller lock beyond this point as we will be calling into AM and WM.
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
-
// Only system senders can declare a broadcast to be alarm-originated. We check
// this here rather than in the general case handling below to fail before the other
// invocation side effects such as allowlisting.
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index df179a9..5b23364c 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -138,6 +138,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
@@ -3870,8 +3871,12 @@
final SyncStorageEngine.EndPoint info = syncOperation.target;
if (activeSyncContext.mIsLinkedToDeath) {
- activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0);
- activeSyncContext.mIsLinkedToDeath = false;
+ try {
+ activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0);
+ activeSyncContext.mIsLinkedToDeath = false;
+ } catch (NoSuchElementException e) {
+ Slog.wtf(TAG, "Failed to unlink active sync adapter to death", e);
+ }
}
final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime;
String historyMessage;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 425659e..4da2cc9 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -10869,6 +10869,7 @@
ArrayList<Notification.Action> smartActions = record.getSystemGeneratedSmartActions();
ArrayList<CharSequence> smartReplies = record.getSmartReplies();
if (redactSensitiveNotificationsFromUntrustedListeners()
+ && info != null
&& !mListeners.isUidTrusted(info.uid)
&& mListeners.hasSensitiveContent(record)) {
smartActions = null;
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 01b464f..25e749f 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -138,6 +138,7 @@
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.net.module.util.NetworkCapabilitiesUtils;
+import com.android.server.power.optimization.Flags;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import libcore.util.EmptyArray;
@@ -185,7 +186,8 @@
// TODO: remove "tcp" from network methods, since we measure total stats.
// Current on-disk Parcel version. Must be updated when the format of the parcelable changes
- public static final int VERSION = 214;
+ public static final int VERSION =
+ !Flags.disableSystemServicePowerAttr() ? 214 : 215;
// The maximum number of names wakelocks we will keep track of
// per uid; once the limit is reached, we batch the remaining wakelocks
@@ -1753,7 +1755,9 @@
mCpuUidActiveTimeReader = new KernelCpuUidActiveTimeReader(true, mClock);
mCpuUidClusterTimeReader = new KernelCpuUidClusterTimeReader(true, mClock);
mKernelWakelockReader = new KernelWakelockReader();
- mSystemServerCpuThreadReader = SystemServerCpuThreadReader.create();
+ if (!Flags.disableSystemServicePowerAttr()) {
+ mSystemServerCpuThreadReader = SystemServerCpuThreadReader.create();
+ }
mKernelMemoryBandwidthStats = new KernelMemoryBandwidthStats();
mTmpRailStats = new RailStats();
}
@@ -11702,7 +11706,9 @@
EnergyConsumerStats.resetIfNotNull(mGlobalEnergyConsumerStats);
- resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs);
+ if (!Flags.disableSystemServicePowerAttr()) {
+ resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs);
+ }
mNumAllUidCpuTimeReads = 0;
mNumUidsRemoved = 0;
@@ -13676,7 +13682,9 @@
mKernelCpuSpeedReaders[i].readDelta();
}
}
- mSystemServerCpuThreadReader.readDelta();
+ if (!Flags.disableSystemServicePowerAttr()) {
+ mSystemServerCpuThreadReader.readDelta();
+ }
return;
}
@@ -15696,23 +15704,25 @@
}
}
- updateSystemServiceCallStats();
- if (mBinderThreadCpuTimesUs != null) {
- pw.println("Per UID System server binder time in ms:");
- long[] systemServiceTimeAtCpuSpeeds = getSystemServiceTimeAtCpuSpeeds();
- for (int i = 0; i < size; i++) {
- int u = mUidStats.keyAt(i);
- Uid uid = mUidStats.get(u);
- double proportionalSystemServiceUsage = uid.getProportionalSystemServiceUsage();
- long timeUs = 0;
- for (int j = systemServiceTimeAtCpuSpeeds.length - 1; j >= 0; j--) {
- timeUs += systemServiceTimeAtCpuSpeeds[j] * proportionalSystemServiceUsage;
- }
+ if (!Flags.disableSystemServicePowerAttr()) {
+ updateSystemServiceCallStats();
+ if (mBinderThreadCpuTimesUs != null) {
+ pw.println("Per UID System server binder time in ms:");
+ long[] systemServiceTimeAtCpuSpeeds = getSystemServiceTimeAtCpuSpeeds();
+ for (int i = 0; i < size; i++) {
+ int u = mUidStats.keyAt(i);
+ Uid uid = mUidStats.get(u);
+ double proportionalSystemServiceUsage = uid.getProportionalSystemServiceUsage();
+ long timeUs = 0;
+ for (int j = systemServiceTimeAtCpuSpeeds.length - 1; j >= 0; j--) {
+ timeUs += systemServiceTimeAtCpuSpeeds[j] * proportionalSystemServiceUsage;
+ }
- pw.print(" ");
- pw.print(u);
- pw.print(": ");
- pw.println(timeUs / 1000);
+ pw.print(" ");
+ pw.print(u);
+ pw.print(": ");
+ pw.println(timeUs / 1000);
+ }
}
}
}
@@ -16428,8 +16438,10 @@
}
}
- mBinderThreadCpuTimesUs =
- LongSamplingCounterArray.readSummaryFromParcelLocked(in, mOnBatteryTimeBase);
+ if (!Flags.disableSystemServicePowerAttr()) {
+ mBinderThreadCpuTimesUs =
+ LongSamplingCounterArray.readSummaryFromParcelLocked(in, mOnBatteryTimeBase);
+ }
}
/**
@@ -16973,7 +16985,9 @@
}
}
- LongSamplingCounterArray.writeSummaryToParcelLocked(out, mBinderThreadCpuTimesUs);
+ if (!Flags.disableSystemServicePowerAttr()) {
+ LongSamplingCounterArray.writeSummaryToParcelLocked(out, mBinderThreadCpuTimesUs);
+ }
}
@GuardedBy("this")
@@ -16985,7 +16999,9 @@
// if we had originally pulled a time before the RTC was set.
getStartClockTime();
- updateSystemServiceCallStats();
+ if (!Flags.disableSystemServicePowerAttr()) {
+ updateSystemServiceCallStats();
+ }
}
@GuardedBy("this")
diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
index c3221e4..30b80ae 100644
--- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
+++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java
@@ -96,11 +96,13 @@
mPowerCalculators.add(new CustomEnergyConsumerPowerCalculator(mPowerProfile));
mPowerCalculators.add(new UserPowerCalculator());
- // It is important that SystemServicePowerCalculator be applied last,
- // because it re-attributes some of the power estimated by the other
- // calculators.
- mPowerCalculators.add(
- new SystemServicePowerCalculator(mCpuScalingPolicies, mPowerProfile));
+ if (!com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) {
+ // It is important that SystemServicePowerCalculator be applied last,
+ // because it re-attributes some of the power estimated by the other
+ // calculators.
+ mPowerCalculators.add(
+ new SystemServicePowerCalculator(mCpuScalingPolicies, mPowerProfile));
+ }
}
}
return mPowerCalculators;
diff --git a/services/core/java/com/android/server/power/stats/flags.aconfig b/services/core/java/com/android/server/power/stats/flags.aconfig
index 0f13571..6546646 100644
--- a/services/core/java/com/android/server/power/stats/flags.aconfig
+++ b/services/core/java/com/android/server/power/stats/flags.aconfig
@@ -13,3 +13,11 @@
description: "Feature flag for streamlined battery stats"
bug: "285646152"
}
+
+flag {
+ name: "disable_system_service_power_attr"
+ namespace: "backstage_power"
+ description: "Deprecation of system service power re-attribution"
+ bug: "311793616"
+ is_fixed_read_only: true
+}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 285bcc3..0ffd002 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -2058,7 +2058,8 @@
}
private void registerCpuCyclesPerThreadGroupCluster() {
- if (KernelCpuBpfTracking.isSupported()) {
+ if (KernelCpuBpfTracking.isSupported()
+ && !com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) {
int tagId = FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER;
PullAtomMetadata metadata = new PullAtomMetadata.Builder()
.setAdditiveFields(new int[]{3, 4})
@@ -2073,6 +2074,10 @@
}
int pullCpuCyclesPerThreadGroupCluster(int atomTag, List<StatsEvent> pulledData) {
+ if (com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) {
+ return StatsManager.PULL_SKIP;
+ }
+
SystemServiceCpuThreadTimes times = LocalServices.getService(BatteryStatsInternal.class)
.getSystemServiceCpuThreadTimes();
if (times == null) {
diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
index b6d0ca1..eacd3f8 100644
--- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java
@@ -4152,6 +4152,25 @@
}
@Override
+ public void onRequestSigning2(String id, String algorithm, String host,
+ int port, byte[] data) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onRequestSigning");
+ }
+ if (mSessionState.mSession == null || mSessionState.mClient == null) {
+ return;
+ }
+ try {
+ mSessionState.mClient.onRequestSigning2(
+ id, algorithm, host, port, data, mSessionState.mSeq);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in onRequestSigning", e);
+ }
+ }
+ }
+
+ @Override
public void onRequestCertificate(String host, int port) {
synchronized (mLock) {
if (DEBUG) {
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 7b08413..4403bce 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -571,8 +571,8 @@
}
static jboolean com_android_server_am_CachedAppOptimizer_isFreezerProfileValid(JNIEnv* env) {
- int uid = getuid();
- int pid = getpid();
+ uid_t uid = getuid();
+ pid_t pid = getpid();
return isProfileValidForProcess("Frozen", uid, pid) &&
isProfileValidForProcess("Unfrozen", uid, pid);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
index 4dae2d5..8e53d52 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java
@@ -28,6 +28,9 @@
import android.os.BatteryConsumer;
import android.os.Binder;
import android.os.Process;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -38,6 +41,7 @@
import com.android.internal.os.KernelSingleUidTimeReader;
import com.android.internal.os.PowerProfile;
import com.android.internal.power.EnergyConsumerStats;
+import com.android.server.power.optimization.Flags;
import org.junit.Before;
import org.junit.Rule;
@@ -54,6 +58,8 @@
@RunWith(AndroidJUnit4.class)
@SuppressWarnings("GuardedBy")
public class SystemServicePowerCalculatorTest {
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private static final double PRECISION = 0.000001;
private static final int APP_UID1 = 100;
@@ -108,6 +114,7 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_DISABLE_SYSTEM_SERVICE_POWER_ATTR)
public void testPowerProfileBasedModel() {
prepareBatteryStats(null);
@@ -135,6 +142,7 @@
}
@Test
+ @RequiresFlagsDisabled(Flags.FLAG_DISABLE_SYSTEM_SERVICE_POWER_ATTR)
public void testMeasuredEnergyBasedModel() {
final boolean[] supportedPowerBuckets =
new boolean[EnergyConsumerStats.NUMBER_STANDARD_POWER_BUCKETS];
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java
index 2e47d48..65da4a1 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java
@@ -28,6 +28,7 @@
private final Object mPoller = new Object();
private volatile boolean mPolling;
+ private volatile boolean mPendingWake;
private void validate() {
if (mDeleted) {
@@ -62,7 +63,9 @@
synchronized (q.mPoller) {
q.mPolling = true;
try {
- if (timeoutMillis == 0) {
+ if (q.mPendingWake) {
+ // Calling with pending wake returns immediately
+ } else if (timeoutMillis == 0) {
// Calling epoll_wait() with 0 returns immediately
} else if (timeoutMillis == -1) {
q.mPoller.wait();
@@ -72,6 +75,8 @@
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
+ // Any reason for returning counts as a "wake", so clear pending
+ q.mPendingWake = false;
q.mPolling = false;
}
}
@@ -79,6 +84,7 @@
public static void nativeWake(long ptr) {
var q = getInstance(ptr);
synchronized (q.mPoller) {
+ q.mPendingWake = true;
q.mPoller.notifyAll();
}
}