diff options
130 files changed, 1799 insertions, 2141 deletions
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index 70c3d7ae3f82..f33d2991329a 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -1346,8 +1346,26 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim } // Set the child animators to the right end: if (mShouldResetValuesAtStart) { - initChildren(); - skipToEndValue(!mReversing); + if (isInitialized()) { + skipToEndValue(!mReversing); + } else if (mReversing) { + // Reversing but haven't initialized all the children yet. + initChildren(); + skipToEndValue(!mReversing); + } else { + // If not all children are initialized and play direction is forward + for (int i = mEvents.size() - 1; i >= 0; i--) { + if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { + Animator anim = mEvents.get(i).mNode.mAnimation; + // Only reset the animations that have been initialized to start value, + // so that if they are defined without a start value, they will get the + // values set at the right time (i.e. the next animation run) + if (anim.isInitialized()) { + anim.skipToEndValue(true); + } + } + } + } } if (mReversing || mStartDelay == 0 || mSeekState.isActive()) { diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl index 3d6ab6fd9478..9a818e49ae4a 100644 --- a/core/java/android/app/IGameManagerService.aidl +++ b/core/java/android/app/IGameManagerService.aidl @@ -20,6 +20,7 @@ import android.app.GameModeConfiguration; import android.app.GameModeInfo; import android.app.GameState; import android.app.IGameModeListener; +import android.app.IGameStateListener; /** * @hide @@ -49,4 +50,6 @@ interface IGameManagerService { void addGameModeListener(IGameModeListener gameModeListener); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") void removeGameModeListener(IGameModeListener gameModeListener); + void addGameStateListener(IGameStateListener gameStateListener); + void removeGameStateListener(IGameStateListener gameStateListener); } diff --git a/core/java/android/app/IGameStateListener.aidl b/core/java/android/app/IGameStateListener.aidl new file mode 100644 index 000000000000..34cff489d077 --- /dev/null +++ b/core/java/android/app/IGameStateListener.aidl @@ -0,0 +1,27 @@ +/* + * 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; + +import android.app.GameState; + +/** @hide */ +interface IGameStateListener { + /** + * Called when the state of the game has changed. + */ + oneway void onGameStateChanged(String packageName, in GameState state, int userId); +} diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index a9c4818393a8..e472a40617ee 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -103,11 +103,10 @@ import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.Xml; import android.util.proto.ProtoOutputStream; -import android.view.BatchedInputEventReceiver.SimpleBatchedInputEventReceiver; -import android.view.Choreographer; import android.view.Gravity; import android.view.InputChannel; import android.view.InputDevice; +import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -1052,17 +1051,22 @@ public class InputMethodService extends AbstractInputMethodService { stylusEvents.forEach(InputMethodService.this::onStylusHandwritingMotionEvent); // create receiver for channel - mHandwritingEventReceiver = new SimpleBatchedInputEventReceiver( - channel, - Looper.getMainLooper(), Choreographer.getInstance(), - event -> { + mHandwritingEventReceiver = new InputEventReceiver(channel, Looper.getMainLooper()) { + @Override + public void onInputEvent(InputEvent event) { + boolean handled = false; + try { if (!(event instanceof MotionEvent)) { - return false; + return; } onStylusHandwritingMotionEvent((MotionEvent) event); scheduleHandwritingSessionTimeout(); - return true; - }); + handled = true; + } finally { + finishInputEvent(event, handled); + } + } + }; scheduleHandwritingSessionTimeout(); } diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 7664bada2c28..e6bdfe1b95c4 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -112,17 +112,9 @@ public class GraphicsEnvironment { private static final int ANGLE_GL_DRIVER_ALL_ANGLE_OFF = 0; // Values for ANGLE_GL_DRIVER_SELECTION_VALUES - private enum AngleDriverChoice { - DEFAULT("default"), - ANGLE("angle"), - NATIVE("native"); - - public final String choice; - - AngleDriverChoice(String choice) { - this.choice = choice; - } - } + private static final String ANGLE_GL_DRIVER_CHOICE_DEFAULT = "default"; + private static final String ANGLE_GL_DRIVER_CHOICE_ANGLE = "angle"; + private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native"; private static final String PROPERTY_RO_ANGLE_SUPPORTED = "ro.gfx.angle.supported"; @@ -203,16 +195,15 @@ public class GraphicsEnvironment { } /** - * Query to determine the ANGLE driver choice. + * Query to determine if ANGLE should be used */ - private AngleDriverChoice queryAngleChoice(Context context, Bundle coreSettings, - String packageName) { + private boolean shouldUseAngle(Context context, Bundle coreSettings, String packageName) { if (TextUtils.isEmpty(packageName)) { Log.v(TAG, "No package name specified; use the system driver"); - return AngleDriverChoice.DEFAULT; + return false; } - return queryAngleChoiceInternal(context, coreSettings, packageName); + return shouldUseAngleInternal(context, coreSettings, packageName); } private int getVulkanVersion(PackageManager pm) { @@ -433,11 +424,10 @@ public class GraphicsEnvironment { * forces a choice; * 3) Use ANGLE if isAngleEnabledByGameMode() returns true; */ - private AngleDriverChoice queryAngleChoiceInternal(Context context, Bundle bundle, - String packageName) { + private boolean shouldUseAngleInternal(Context context, Bundle bundle, String packageName) { // Make sure we have a good package name if (TextUtils.isEmpty(packageName)) { - return AngleDriverChoice.DEFAULT; + return false; } // Check the semi-global switch (i.e. once system has booted enough) for whether ANGLE @@ -452,7 +442,7 @@ public class GraphicsEnvironment { } if (allUseAngle == ANGLE_GL_DRIVER_ALL_ANGLE_ON) { Log.v(TAG, "Turn on ANGLE for all applications."); - return AngleDriverChoice.ANGLE; + return true; } // Get the per-application settings lists @@ -475,7 +465,7 @@ public class GraphicsEnvironment { + optInPackages.size() + ", " + "number of values: " + optInValues.size()); - return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT; + return mEnabledByGameMode; } // See if this application is listed in the per-application settings list @@ -483,7 +473,7 @@ public class GraphicsEnvironment { if (pkgIndex < 0) { Log.v(TAG, packageName + " is not listed in per-application setting"); - return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT; + return mEnabledByGameMode; } mAngleOptInIndex = pkgIndex; @@ -493,14 +483,14 @@ public class GraphicsEnvironment { Log.v(TAG, "ANGLE Developer option for '" + packageName + "' " + "set to: '" + optInValue + "'"); - if (optInValue.equals(AngleDriverChoice.ANGLE.choice)) { - return AngleDriverChoice.ANGLE; - } else if (optInValue.equals(AngleDriverChoice.NATIVE.choice)) { - return AngleDriverChoice.NATIVE; + if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE)) { + return true; + } else if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) { + return false; } else { // The user either chose default or an invalid value; go with the default driver or what // the game mode indicates - return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT; + return mEnabledByGameMode; } } @@ -568,13 +558,7 @@ public class GraphicsEnvironment { private boolean setupAngle(Context context, Bundle bundle, PackageManager packageManager, String packageName) { - final AngleDriverChoice angleDriverChoice = queryAngleChoice(context, bundle, packageName); - if (angleDriverChoice == AngleDriverChoice.DEFAULT) { - return false; - } - - if (queryAngleChoice(context, bundle, packageName) == AngleDriverChoice.NATIVE) { - nativeSetAngleInfo("", true, packageName, null); + if (!shouldUseAngle(context, bundle, packageName)) { return false; } @@ -643,10 +627,10 @@ public class GraphicsEnvironment { Log.d(TAG, "ANGLE package libs: " + paths); } - // If we make it to here, ANGLE apk will be used. Call nativeSetAngleInfo() with the - // application package name and ANGLE features to use. + // If we make it to here, ANGLE will be used. Call setAngleInfo() with the package name, + // and features to use. final String[] features = getAngleEglFeatures(context, bundle); - nativeSetAngleInfo(paths, false, packageName, features); + setAngleInfo(paths, false, packageName, features); return true; } @@ -668,10 +652,10 @@ public class GraphicsEnvironment { return false; } - // If we make it to here, system ANGLE will be used. Call nativeSetAngleInfo() with - // the application package name and ANGLE features to use. + // If we make it to here, ANGLE will be used. Call setAngleInfo() with the package name, + // and features to use. final String[] features = getAngleEglFeatures(context, bundle); - nativeSetAngleInfo("system", false, packageName, features); + setAngleInfo("", true, packageName, features); return true; } @@ -952,8 +936,8 @@ public class GraphicsEnvironment { private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries); private static native void setGpuStats(String driverPackageName, String driverVersionName, long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion); - private static native void nativeSetAngleInfo(String path, boolean useNativeDriver, - String packageName, String[] features); + private static native void setAngleInfo(String path, boolean useSystemAngle, String packageName, + String[] features); private static native boolean setInjectLayersPrSetDumpable(); private static native void nativeToggleAngleAsSystemDriver(boolean enabled); diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 0071a0d00105..827600c83fae 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -149,6 +149,13 @@ public class FeatureFlagUtils { */ public static final String SETTINGS_BIOMETRICS2_ENROLLMENT = "settings_biometrics2_enrollment"; + /** + * Flag to enable/disable FingerprintSettings v2 + * @hide + */ + public static final String SETTINGS_BIOMETRICS2_FINGERPRINT_SETTINGS = + "settings_biometrics2_fingerprint"; + /** Flag to enable/disable entire page in Accessibility -> Hearing aids * @hide */ @@ -239,6 +246,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, "true"); DEFAULT_FLAGS.put(SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API, "true"); DEFAULT_FLAGS.put(SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, "true"); + DEFAULT_FLAGS.put(SETTINGS_BIOMETRICS2_FINGERPRINT_SETTINGS, "false"); } private static final Set<String> PERSISTENT_FLAGS; diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 3f308e6fccee..b3233141f126 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -50,6 +50,7 @@ import android.annotation.TestApi; import android.annotation.UiThread; import android.annotation.UserIdInt; import android.app.ActivityThread; +import android.app.PropertyInvalidatedCache; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; @@ -536,6 +537,13 @@ public final class InputMethodManager { @UnsupportedAppUsage Rect mCursorRect = new Rect(); + /** Cached value for {@link #isStylusHandwritingAvailable} for userId. */ + @GuardedBy("mH") + private PropertyInvalidatedCache<Integer, Boolean> mStylusHandwritingAvailableCache; + + private static final String CACHE_KEY_STYLUS_HANDWRITING_PROPERTY = + "cache_key.system_server.stylus_handwriting"; + @GuardedBy("mH") private int mCursorSelStart; @GuardedBy("mH") @@ -662,6 +670,15 @@ public final class InputMethodManager { private static final int MSG_UPDATE_VIRTUAL_DISPLAY_TO_SCREEN_MATRIX = 30; private static final int MSG_ON_SHOW_REQUESTED = 31; + /** + * Calling this will invalidate Local stylus handwriting availability Cache which + * forces the next query in any process to recompute the cache. + * @hide + */ + public static void invalidateLocalStylusHandwritingAvailabilityCaches() { + PropertyInvalidatedCache.invalidateCache(CACHE_KEY_STYLUS_HANDWRITING_PROPERTY); + } + private static boolean isAutofillUIShowing(View servedView) { AutofillManager afm = servedView.getContext().getSystemService(AutofillManager.class); return afm != null && afm.isAutofillUiShowing(); @@ -1577,8 +1594,21 @@ public final class InputMethodManager { if (fallbackContext == null) { return false; } - - return IInputMethodManagerGlobalInvoker.isStylusHandwritingAvailableAsUser(userId); + boolean isAvailable; + synchronized (mH) { + if (mStylusHandwritingAvailableCache == null) { + mStylusHandwritingAvailableCache = new PropertyInvalidatedCache<>( + 4 /* maxEntries */, CACHE_KEY_STYLUS_HANDWRITING_PROPERTY) { + @Override + public Boolean recompute(Integer userId) { + return IInputMethodManagerGlobalInvoker.isStylusHandwritingAvailableAsUser( + userId); + } + }; + } + isAvailable = mStylusHandwritingAvailableCache.query(userId); + } + return isAvailable; } /** diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index b65c1a17e26b..cb5dbe6c5618 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -1550,6 +1550,7 @@ public class ScrollView extends FrameLayout { float deltaDistance = -unconsumed * FLING_DESTRETCH_FACTOR / size; int consumed = Math.round(-size / FLING_DESTRETCH_FACTOR * mEdgeGlowTop.onPullDistance(deltaDistance, 0.5f)); + mEdgeGlowTop.onRelease(); if (consumed != unconsumed) { mEdgeGlowTop.finish(); } @@ -1560,6 +1561,7 @@ public class ScrollView extends FrameLayout { float deltaDistance = unconsumed * FLING_DESTRETCH_FACTOR / size; int consumed = Math.round(size / FLING_DESTRETCH_FACTOR * mEdgeGlowBottom.onPullDistance(deltaDistance, 0.5f)); + mEdgeGlowBottom.onRelease(); if (consumed != unconsumed) { mEdgeGlowBottom.finish(); } diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java index 413f0ccbc275..e153bb70a7ca 100644 --- a/core/java/android/window/TaskFragmentOperation.java +++ b/core/java/android/window/TaskFragmentOperation.java @@ -73,6 +73,13 @@ public final class TaskFragmentOperation implements Parcelable { /** Sets the relative bounds with {@link WindowContainerTransaction#setRelativeBounds}. */ public static final int OP_TYPE_SET_RELATIVE_BOUNDS = 9; + /** + * Reorders the TaskFragment to be the front-most TaskFragment in the Task. + * Note that there could still have other WindowContainer on top of the front-most + * TaskFragment, such as a non-embedded Activity. + */ + public static final int OP_TYPE_REORDER_TO_FRONT = 10; + @IntDef(prefix = { "OP_TYPE_" }, value = { OP_TYPE_UNKNOWN, OP_TYPE_CREATE_TASK_FRAGMENT, @@ -84,7 +91,8 @@ public final class TaskFragmentOperation implements Parcelable { OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT, OP_TYPE_SET_COMPANION_TASK_FRAGMENT, OP_TYPE_SET_ANIMATION_PARAMS, - OP_TYPE_SET_RELATIVE_BOUNDS + OP_TYPE_SET_RELATIVE_BOUNDS, + OP_TYPE_REORDER_TO_FRONT }) @Retention(RetentionPolicy.SOURCE) public @interface OperationType {} diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp index 8fc30d1c248d..afc3cbd15f88 100644 --- a/core/jni/android_os_GraphicsEnvironment.cpp +++ b/core/jni/android_os_GraphicsEnvironment.cpp @@ -50,7 +50,7 @@ void setGpuStats_native(JNIEnv* env, jobject clazz, jstring driverPackageName, appPackageNameChars.c_str(), vulkanVersion); } -void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useNativeDriver, +void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useSystemAngle, jstring packageName, jobjectArray featuresObj) { ScopedUtfChars pathChars(env, path); ScopedUtfChars packageNameChars(env, packageName); @@ -73,7 +73,7 @@ void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useN } } - android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), useNativeDriver, + android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), useSystemAngle, packageNameChars.c_str(), features); } @@ -118,7 +118,7 @@ const JNINativeMethod g_methods[] = { reinterpret_cast<void*>(setGpuStats_native)}, {"setInjectLayersPrSetDumpable", "()Z", reinterpret_cast<void*>(setInjectLayersPrSetDumpable_native)}, - {"nativeSetAngleInfo", "(Ljava/lang/String;ZLjava/lang/String;[Ljava/lang/String;)V", + {"setAngleInfo", "(Ljava/lang/String;ZLjava/lang/String;[Ljava/lang/String;)V", reinterpret_cast<void*>(setAngleInfo_native)}, {"setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native)}, diff --git a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java index 0525443ecf82..0c0747060f48 100644 --- a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java +++ b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue; import android.util.Property; import android.view.View; +import androidx.annotation.NonNull; import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import androidx.test.rule.ActivityTestRule; @@ -36,6 +37,8 @@ import org.junit.Rule; import org.junit.Test; import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; @SmallTest public class AnimatorSetActivityTest { @@ -613,6 +616,68 @@ public class AnimatorSetActivityTest { }); } + @Test + public void initAfterStartNotification() throws Throwable { + Property<int[], Integer> property = new Property<>(Integer.class, "firstValue") { + @Override + public Integer get(int[] target) { + throw new IllegalStateException("Shouldn't be called"); + } + + @Override + public void set(int[] target, Integer value) { + target[0] = value; + } + }; + int[] target = new int[1]; + ObjectAnimator animator1 = ObjectAnimator.ofInt(target, property, 0, 100); + ObjectAnimator animator2 = ObjectAnimator.ofInt(target, property, 0, 100); + ObjectAnimator animator3 = ObjectAnimator.ofInt(target, property, 0, 100); + animator1.setDuration(10); + animator2.setDuration(10); + animator3.setDuration(10); + AnimatorSet set = new AnimatorSet(); + set.playSequentially(animator1, animator2, animator3); + final int[] values = new int[4]; + animator2.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(@NonNull Animator animation, boolean isReverse) { + values[0] = target[0]; + animator2.setIntValues(target[0], target[0] + 100); + } + + @Override + public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { + values[1] = target[0]; + } + }); + animator3.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(@NonNull Animator animation, boolean isReverse) { + values[2] = target[0]; + animator3.setIntValues(target[0], target[0] + 100); + } + + @Override + public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { + values[3] = target[0]; + } + }); + final CountDownLatch latch = new CountDownLatch(1); + set.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { + latch.countDown(); + } + }); + mActivityRule.runOnUiThread(() -> set.start()); + assertTrue(latch.await(1, TimeUnit.SECONDS)); + assertEquals(100, values[0]); + assertEquals(200, values[1]); + assertEquals(200, values[2]); + assertEquals(300, values[3]); + } + /** * Check that the animator list contains exactly the given animators and nothing else. */ diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index b73a87c8f0d9..48577416b3d0 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -884,12 +884,13 @@ public class ActivityThreadTest { mConfig.setTo(config); ++mNumOfConfigChanges; - if (mConfigLatch != null) { + final CountDownLatch configLatch = mConfigLatch; + if (configLatch != null) { if (mTestLatch != null) { mTestLatch.countDown(); } try { - mConfigLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); + configLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); } catch (InterruptedException e) { throw new IllegalStateException(e); } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java index 76e0e1eb7a95..55eabb039c01 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java @@ -48,7 +48,7 @@ public class WindowExtensionsImpl implements WindowExtensions { // TODO(b/241126279) Introduce constants to better version functionality @Override public int getVendorApiLevel() { - return 3; + return 4; } @NonNull diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java index 18497ad249ee..381e9d472f0f 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java @@ -32,7 +32,7 @@ import androidx.window.extensions.core.util.function.Function; */ class SplitContainer { @NonNull - private final TaskFragmentContainer mPrimaryContainer; + private TaskFragmentContainer mPrimaryContainer; @NonNull private final TaskFragmentContainer mSecondaryContainer; @NonNull @@ -46,17 +46,35 @@ class SplitContainer { @NonNull private final IBinder mToken; + /** + * Whether the selection of which container is primary can be changed at runtime. Runtime + * updates is currently possible only for {@link SplitPinContainer} + * + * @see SplitPinContainer + */ + private final boolean mIsPrimaryContainerMutable; + SplitContainer(@NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule, @NonNull SplitAttributes splitAttributes) { + this(primaryContainer, primaryActivity, secondaryContainer, splitRule, splitAttributes, + false /* isPrimaryContainerMutable */); + } + + SplitContainer(@NonNull TaskFragmentContainer primaryContainer, + @NonNull Activity primaryActivity, + @NonNull TaskFragmentContainer secondaryContainer, + @NonNull SplitRule splitRule, + @NonNull SplitAttributes splitAttributes, boolean isPrimaryContainerMutable) { mPrimaryContainer = primaryContainer; mSecondaryContainer = secondaryContainer; mSplitRule = splitRule; mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes(); mCurrentSplitAttributes = splitAttributes; mToken = new Binder("SplitContainer"); + mIsPrimaryContainerMutable = isPrimaryContainerMutable; if (shouldFinishPrimaryWithSecondary(splitRule)) { if (mPrimaryContainer.getRunningActivityCount() == 1 @@ -74,6 +92,13 @@ class SplitContainer { } } + void setPrimaryContainer(@NonNull TaskFragmentContainer primaryContainer) { + if (!mIsPrimaryContainerMutable) { + throw new IllegalStateException("Cannot update primary TaskFragmentContainer"); + } + mPrimaryContainer = primaryContainer; + } + @NonNull TaskFragmentContainer getPrimaryContainer() { return mPrimaryContainer; 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 4cedd41e2d9a..a2f75e099465 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -213,6 +213,56 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } @Override + public boolean pinTopActivityStack(int taskId, @NonNull SplitPinRule splitPinRule) { + synchronized (mLock) { + final TaskContainer task = getTaskContainer(taskId); + if (task == null) { + Log.e(TAG, "Cannot find the task for id: " + taskId); + return false; + } + + final TaskFragmentContainer topContainer = + task.getTopNonFinishingTaskFragmentContainer(); + // Cannot pin the TaskFragment if no other TaskFragment behind it. + if (topContainer == null || task.indexOf(topContainer) <= 0) { + Log.w(TAG, "Cannot find an ActivityStack to pin or split"); + return false; + } + // Abort if the top container is already pinned. + if (task.getSplitPinContainer() != null) { + Log.w(TAG, "There is already a pinned ActivityStack."); + return false; + } + + // Find a valid adjacent TaskFragmentContainer + final TaskFragmentContainer primaryContainer = + task.getNonFinishingTaskFragmentContainerBelow(topContainer); + if (primaryContainer == null) { + Log.w(TAG, "Cannot find another ActivityStack to split"); + return false; + } + + // Registers a Split + final SplitPinContainer splitPinContainer = new SplitPinContainer(primaryContainer, + topContainer, splitPinRule, splitPinRule.getDefaultSplitAttributes()); + task.addSplitContainer(splitPinContainer); + + // Updates the Split + final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction(); + final WindowContainerTransaction wct = transactionRecord.getTransaction(); + mPresenter.updateSplitContainer(splitPinContainer, wct); + transactionRecord.apply(false /* shouldApplyIndependently */); + updateCallbackIfNecessary(); + return true; + } + } + + @Override + public void unpinTopActivityStack(int taskId){ + // TODO + } + + @Override public void setSplitAttributesCalculator( @NonNull Function<SplitAttributesCalculatorParams, SplitAttributes> calculator) { synchronized (mLock) { @@ -672,7 +722,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (targetContainer == null) { // When there is no embedding rule matched, try to place it in the top container // like a normal launch. - targetContainer = taskContainer.getTopTaskFragmentContainer(); + targetContainer = taskContainer.getTopNonFinishingTaskFragmentContainer(); } if (targetContainer == null) { return; @@ -791,7 +841,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen final TaskFragmentContainer container = getContainerWithActivity(activity); if (!isOnReparent && container != null - && container.getTaskContainer().getTopTaskFragmentContainer() != container) { + && container.getTaskContainer().getTopNonFinishingTaskFragmentContainer() + != container) { // Do not resolve if the launched activity is not the top-most container in the Task. return true; } @@ -888,7 +939,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (taskContainer == null) { return; } - final TaskFragmentContainer targetContainer = taskContainer.getTopTaskFragmentContainer(); + final TaskFragmentContainer targetContainer = + taskContainer.getTopNonFinishingTaskFragmentContainer(); if (targetContainer == null) { return; } @@ -1213,11 +1265,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // 3. Whether the top activity (if any) should be split with the new activity intent. final TaskContainer taskContainer = getTaskContainer(taskId); - if (taskContainer == null || taskContainer.getTopTaskFragmentContainer() == null) { + if (taskContainer == null + || taskContainer.getTopNonFinishingTaskFragmentContainer() == null) { // There is no other activity in the Task to check split with. return null; } - final TaskFragmentContainer topContainer = taskContainer.getTopTaskFragmentContainer(); + final TaskFragmentContainer topContainer = + taskContainer.getTopNonFinishingTaskFragmentContainer(); final Activity topActivity = topContainer.getTopNonFinishingActivity(); if (topActivity != null && topActivity != launchingActivity) { final TaskFragmentContainer container = getSecondaryContainerForSplitIfAny(wct, @@ -1567,6 +1621,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // background. return; } + final SplitContainer splitContainer = getActiveSplitForContainer(container); + if (splitContainer instanceof SplitPinContainer + && updateSplitContainerIfNeeded(splitContainer, wct, null /* splitAttributes */)) { + // A SplitPinContainer exists and is updated. + return; + } if (launchPlaceholderIfNecessary(wct, container)) { // Placeholder was launched, the positions will be updated when the activity is added // to the secondary container. @@ -1579,7 +1639,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // If the info is not available yet the task fragment will be expanded when it's ready return; } - SplitContainer splitContainer = getActiveSplitForContainer(container); if (splitContainer == null) { return; } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPinContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPinContainer.java new file mode 100644 index 000000000000..03c77a089012 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPinContainer.java @@ -0,0 +1,47 @@ +/* + * 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 androidx.window.extensions.embedding; + +import androidx.annotation.NonNull; + +/** + * Client-side descriptor of a split that holds two containers while the secondary + * container is pinned on top of the Task and the primary container is the container that is + * currently below the secondary container. The primary container could be updated to + * another container whenever the existing primary container is removed or no longer + * be the container that's right behind the secondary container. + */ +class SplitPinContainer extends SplitContainer { + + SplitPinContainer(@NonNull TaskFragmentContainer primaryContainer, + @NonNull TaskFragmentContainer secondaryContainer, + @NonNull SplitPinRule splitPinRule, + @NonNull SplitAttributes splitAttributes) { + super(primaryContainer, primaryContainer.getTopNonFinishingActivity(), secondaryContainer, + splitPinRule, splitAttributes, true /* isPrimaryContainerMutable */); + } + + @Override + public String toString() { + return "SplitPinContainer{" + + " primaryContainer=" + getPrimaryContainer() + + " secondaryContainer=" + getSecondaryContainer() + + " splitPinRule=" + getSplitRule() + + " splitAttributes" + getCurrentSplitAttributes() + + "}"; + } +} 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 53d39d9fa28e..4dafbd17f379 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -17,6 +17,7 @@ package androidx.window.extensions.embedding; import static android.content.pm.PackageManager.MATCH_ALL; +import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT; import android.app.Activity; import android.app.ActivityThread; @@ -39,6 +40,7 @@ import android.view.WindowInsets; import android.view.WindowMetrics; import android.window.TaskFragmentAnimationParams; import android.window.TaskFragmentCreationParams; +import android.window.TaskFragmentOperation; import android.window.WindowContainerTransaction; import androidx.annotation.IntDef; @@ -336,10 +338,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { // value. final SplitRule rule = splitContainer.getSplitRule(); final TaskFragmentContainer primaryContainer = splitContainer.getPrimaryContainer(); - final Activity activity = primaryContainer.getTopNonFinishingActivity(); - if (activity == null) { - return; - } final TaskContainer taskContainer = splitContainer.getTaskContainer(); final TaskProperties taskProperties = taskContainer.getTaskProperties(); final SplitAttributes splitAttributes = splitContainer.getCurrentSplitAttributes(); @@ -424,6 +422,16 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { container.setLastRequestedBounds(fragmentOptions.getInitialRelativeBounds()); container.setLastRequestedWindowingMode(fragmentOptions.getWindowingMode()); super.createTaskFragment(wct, fragmentOptions); + + // Reorders the pinned TaskFragment to front to ensure it is the front-most TaskFragment. + final SplitPinContainer pinnedContainer = + container.getTaskContainer().getSplitPinContainer(); + if (pinnedContainer != null) { + final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( + OP_TYPE_REORDER_TO_FRONT).build(); + wct.addTaskFragmentOperation( + pinnedContainer.getSecondaryContainer().getTaskFragmentToken(), operation); + } } @Override 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 4580c9836168..969e3ed5b9b6 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -57,6 +57,10 @@ class TaskContainer { @NonNull private final List<SplitContainer> mSplitContainers = new ArrayList<>(); + /** Active pin split pair in this Task. */ + @Nullable + private SplitPinContainer mSplitPinContainer; + @NonNull private final Configuration mConfiguration; @@ -174,11 +178,28 @@ class TaskContainer { } @Nullable - TaskFragmentContainer getTopTaskFragmentContainer() { - if (mContainers.isEmpty()) { - return null; + TaskFragmentContainer getTopNonFinishingTaskFragmentContainer() { + for (int i = mContainers.size() - 1; i >= 0; i--) { + final TaskFragmentContainer container = mContainers.get(i); + if (!container.isFinished()) { + return container; + } + } + return null; + } + + /** Gets a non-finishing container below the given one. */ + @Nullable + TaskFragmentContainer getNonFinishingTaskFragmentContainerBelow( + @NonNull TaskFragmentContainer current) { + final int index = mContainers.indexOf(current); + for (int i = index - 1; i >= 0; i--) { + final TaskFragmentContainer container = mContainers.get(i); + if (!container.isFinished()) { + return container; + } } - return mContainers.get(mContainers.size() - 1); + return null; } @Nullable @@ -217,31 +238,57 @@ class TaskContainer { } void addSplitContainer(@NonNull SplitContainer splitContainer) { + if (splitContainer instanceof SplitPinContainer) { + mSplitPinContainer = (SplitPinContainer) splitContainer; + mSplitContainers.add(splitContainer); + return; + } + + // Keeps the SplitPinContainer on the top of the list. + mSplitContainers.remove(mSplitPinContainer); mSplitContainers.add(splitContainer); + if (mSplitPinContainer != null) { + mSplitContainers.add(mSplitPinContainer); + } } void removeSplitContainers(@NonNull List<SplitContainer> containers) { mSplitContainers.removeAll(containers); } + void removeSplitPinContainer() { + mSplitContainers.remove(mSplitPinContainer); + mSplitPinContainer = null; + } + + @Nullable + SplitPinContainer getSplitPinContainer() { + return mSplitPinContainer; + } + void addTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) { mContainers.add(taskFragmentContainer); + onTaskFragmentContainerUpdated(); } void addTaskFragmentContainer(int index, @NonNull TaskFragmentContainer taskFragmentContainer) { mContainers.add(index, taskFragmentContainer); + onTaskFragmentContainerUpdated(); } void removeTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) { mContainers.remove(taskFragmentContainer); + onTaskFragmentContainerUpdated(); } void removeTaskFragmentContainers(@NonNull List<TaskFragmentContainer> taskFragmentContainer) { mContainers.removeAll(taskFragmentContainer); + onTaskFragmentContainerUpdated(); } void clearTaskFragmentContainer() { mContainers.clear(); + onTaskFragmentContainerUpdated(); } /** @@ -254,6 +301,34 @@ class TaskContainer { return mContainers; } + private void onTaskFragmentContainerUpdated() { + if (mSplitPinContainer == null) { + return; + } + + final TaskFragmentContainer pinnedContainer = mSplitPinContainer.getSecondaryContainer(); + final int pinnedContainerIndex = mContainers.indexOf(pinnedContainer); + if (pinnedContainerIndex <= 0) { + removeSplitPinContainer(); + return; + } + + // Ensure the pinned container is top-most. + if (pinnedContainerIndex != mContainers.size() - 1) { + mContainers.remove(pinnedContainer); + mContainers.add(pinnedContainer); + } + + // Update the primary container adjacent to the pinned container if needed. + final TaskFragmentContainer adjacentContainer = + getNonFinishingTaskFragmentContainerBelow(pinnedContainer); + if (adjacentContainer == null) { + removeSplitPinContainer(); + } else if (mSplitPinContainer.getPrimaryContainer() != adjacentContainer) { + mSplitPinContainer.setPrimaryContainer(adjacentContainer); + } + } + /** Adds the descriptors of split states in this Task to {@code outSplitStates}. */ void getSplitStates(@NonNull List<SplitInfo> outSplitStates) { for (SplitContainer container : mSplitContainers) { 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 9e264726a65a..9af1fe916279 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 @@ -1462,6 +1462,51 @@ public class SplitControllerTest { verify(testRecord).apply(eq(false)); } + @Test + public void testPinTopActivityStack() { + // Create two activities. + final Activity primaryActivity = createMockActivity(); + final Activity secondaryActivity = createMockActivity(); + + // Unable to pin if not being embedded. + SplitPinRule splitPinRule = new SplitPinRule.Builder(new SplitAttributes.Builder().build(), + parentWindowMetrics -> true /* parentWindowMetricsPredicate */).build(); + assertFalse(mSplitController.pinTopActivityStack(TASK_ID, splitPinRule)); + + // Split the two activities. + addSplitTaskFragments(primaryActivity, secondaryActivity); + final TaskFragmentContainer primaryContainer = + mSplitController.getContainerWithActivity(primaryActivity); + spyOn(primaryContainer); + + // Unable to pin if no valid TaskFragment. + doReturn(true).when(primaryContainer).isFinished(); + assertFalse(mSplitController.pinTopActivityStack(TASK_ID, splitPinRule)); + + // Otherwise, should pin successfully. + doReturn(false).when(primaryContainer).isFinished(); + assertTrue(mSplitController.pinTopActivityStack(TASK_ID, splitPinRule)); + + // Unable to pin if there is already a pinned TaskFragment + assertFalse(mSplitController.pinTopActivityStack(TASK_ID, splitPinRule)); + + // Unable to pin on an unknown Task. + assertFalse(mSplitController.pinTopActivityStack(TASK_ID + 1, splitPinRule)); + + // Gets the current size of all the SplitContainers. + final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID); + final int splitContainerCount = taskContainer.getSplitContainers().size(); + + // Create another activity and split with primary activity. + final Activity thirdActivity = createMockActivity(); + addSplitTaskFragments(primaryActivity, thirdActivity); + + // Ensure another SplitContainer is added and the pinned TaskFragment still on top + assertTrue(taskContainer.getSplitContainers().size() == splitContainerCount + +1); + assertTrue(mSplitController.getTopActiveContainer(TASK_ID).getTopNonFinishingActivity() + == secondaryActivity); + } + /** Creates a mock activity in the organizer process. */ private Activity createMockActivity() { return createMockActivity(TASK_ID); 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 11af1d1f20e1..000c65a75c81 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 @@ -135,15 +135,15 @@ public class TaskContainerTest { @Test public void testGetTopTaskFragmentContainer() { final TaskContainer taskContainer = createTestTaskContainer(); - assertNull(taskContainer.getTopTaskFragmentContainer()); + assertNull(taskContainer.getTopNonFinishingTaskFragmentContainer()); final TaskFragmentContainer tf0 = new TaskFragmentContainer(null /* activity */, new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */); - assertEquals(tf0, taskContainer.getTopTaskFragmentContainer()); + assertEquals(tf0, taskContainer.getTopNonFinishingTaskFragmentContainer()); final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */, new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */); - assertEquals(tf1, taskContainer.getTopTaskFragmentContainer()); + assertEquals(tf1, taskContainer.getTopNonFinishingTaskFragmentContainer()); } @Test diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 6385fd3fd811..8def8ff1ab01 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1075,9 +1075,9 @@ public class BubbleController implements ConfigurationChangeListener, * <p>This is used by external callers (launcher). */ @VisibleForTesting - public void expandStackAndSelectBubbleFromLauncher(String key, int bubbleBarXCoordinate, - int bubbleBarYCoordinate) { - mBubblePositioner.setBubbleBarPosition(bubbleBarXCoordinate, bubbleBarYCoordinate); + public void expandStackAndSelectBubbleFromLauncher(String key, int bubbleBarOffsetX, + int bubbleBarOffsetY) { + mBubblePositioner.setBubbleBarPosition(bubbleBarOffsetX, bubbleBarOffsetY); if (BubbleOverflow.KEY.equals(key)) { mBubbleData.setSelectedBubbleFromLauncher(mBubbleData.getOverflow()); @@ -2088,10 +2088,10 @@ public class BubbleController implements ConfigurationChangeListener, } @Override - public void showBubble(String key, int bubbleBarXCoordinate, int bubbleBarYCoordinate) { + public void showBubble(String key, int bubbleBarOffsetX, int bubbleBarOffsetY) { mMainExecutor.execute( () -> mController.expandStackAndSelectBubbleFromLauncher( - key, bubbleBarXCoordinate, bubbleBarYCoordinate)); + key, bubbleBarOffsetX, bubbleBarOffsetY)); } @Override 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 6a5e3104fed5..ee6996d3d23d 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 @@ -718,9 +718,16 @@ public class BubblePositioner { mShowingInBubbleBar = showingInBubbleBar; } - /** Sets the position of the bubble bar in screen coordinates. */ - public void setBubbleBarPosition(int x, int y) { - mBubbleBarPosition.set(x, y); + /** + * Sets the position of the bubble bar in screen coordinates. + * + * @param offsetX the offset of the bubble bar from the edge of the screen on the X axis + * @param offsetY the offset of the bubble bar from the edge of the screen on the Y axis + */ + public void setBubbleBarPosition(int offsetX, int offsetY) { + mBubbleBarPosition.set( + getAvailableRect().width() - offsetX, + getAvailableRect().height() + mInsets.top + mInsets.bottom - offsetY); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl index 59332f4be627..351319f5fb5e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl @@ -29,8 +29,7 @@ interface IBubbles { oneway void unregisterBubbleListener(in IBubblesListener listener) = 2; - oneway void showBubble(in String key, in int bubbleBarXCoordinate, - in int bubbleBarYCoordinate) = 3; + oneway void showBubble(in String key, in int bubbleBarOffsetX, in int bubbleBarOffsetY) = 3; oneway void removeBubble(in String key, in int reason) = 4; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index b14c3c10846b..08da4857a0b0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -1927,6 +1927,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, pw.println(innerPrefix + "mLeash=" + mLeash); pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState()); pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams); + mPipTransitionController.dump(pw, innerPrefix); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 73eb62ae47e9..e3d53fc415db 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -72,6 +72,7 @@ import com.android.wm.shell.transition.CounterRotatorHelper; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.util.TransitionUtil; +import java.io.PrintWriter; import java.util.Optional; /** @@ -451,6 +452,9 @@ public class PipTransition extends PipTransitionController { @Override public void forceFinishTransition() { + // mFinishCallback might be null with an outdated mCurrentPipTaskToken + // for example, when app crashes while in PiP and exit transition has not started + mCurrentPipTaskToken = null; if (mFinishCallback == null) return; mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */); mFinishCallback = null; @@ -1137,4 +1141,12 @@ public class PipTransition extends PipTransitionController { PipMenuController.ALPHA_NO_CHANGE); mPipMenuController.updateMenuBounds(destinationBounds); } + + @Override + public void dump(PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + pw.println(prefix + TAG); + pw.println(innerPrefix + "mCurrentPipTaskToken=" + mCurrentPipTaskToken); + pw.println(innerPrefix + "mFinishCallback=" + mFinishCallback); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index e1bcd70c256b..63627938ec87 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -42,6 +42,7 @@ import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -283,4 +284,9 @@ public abstract class PipTransitionController implements Transitions.TransitionH */ void onPipTransitionCanceled(int direction); } + + /** + * Dumps internal states. + */ + public void dump(PrintWriter pw, String prefix) {} } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java index 5f54f58557d1..56c0d0e67cab 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java @@ -38,8 +38,6 @@ public class ShellSharedConstants { public static final String KEY_EXTRA_SHELL_RECENT_TASKS = "extra_shell_recent_tasks"; // See IBackAnimation.aidl public static final String KEY_EXTRA_SHELL_BACK_ANIMATION = "extra_shell_back_animation"; - // See IFloatingTasks.aidl - public static final String KEY_EXTRA_SHELL_FLOATING_TASKS = "extra_shell_floating_tasks"; // See IDesktopMode.aidl public static final String KEY_EXTRA_SHELL_DESKTOP_MODE = "extra_shell_desktop_mode"; // See IDragAndDrop.aidl diff --git a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.kt b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.kt index 96bfb78eff0d..d1dd8df81a3e 100644 --- a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.kt +++ b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.kt @@ -23,6 +23,7 @@ import android.content.ComponentName import android.util.Log import com.android.dream.lowlight.dagger.LowLightDreamModule import com.android.dream.lowlight.dagger.qualifiers.Application +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.TimeoutCancellationException @@ -103,6 +104,11 @@ class LowLightDreamManager @Inject constructor( ) } catch (ex: TimeoutCancellationException) { Log.e(TAG, "timed out while waiting for low light animation", ex) + } catch (ex: CancellationException) { + Log.w(TAG, "low light transition animation cancelled") + // Catch the cancellation so that we still set the system dream component if the + // animation is cancelled, such as by a user tapping to wake as the transition to + // low light happens. } dreamManager.setSystemDreamComponent( if (shouldEnterLowLight) lowLightDreamComponent else null diff --git a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.kt b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.kt index 473603002b21..de1aee598667 100644 --- a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.kt +++ b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.kt @@ -110,15 +110,5 @@ class LowLightTransitionCoordinator @Inject constructor() { } } animator.addListener(listener) - continuation.invokeOnCancellation { - try { - animator.removeListener(listener) - animator.cancel() - } catch (exception: IndexOutOfBoundsException) { - // TODO(b/285666217): remove this try/catch once a proper fix is implemented. - // Cancelling the animator can cause an exception since we may be removing a - // listener during the cancellation. See b/285666217 for more details. - } - } } } diff --git a/libs/dream/lowlight/src/com/android/dream/lowlight/util/TruncatedInterpolator.kt b/libs/dream/lowlight/src/com/android/dream/lowlight/util/TruncatedInterpolator.kt new file mode 100644 index 000000000000..f69c84dafbb2 --- /dev/null +++ b/libs/dream/lowlight/src/com/android/dream/lowlight/util/TruncatedInterpolator.kt @@ -0,0 +1,54 @@ +/* + * 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.dream.lowlight.util + +import android.view.animation.Interpolator + +/** + * Interpolator wrapper that shortens another interpolator from its original duration to a portion + * of that duration. + * + * For example, an `originalDuration` of 1000 and a `newDuration` of 200 results in an animation + * that when played for 200ms is the exact same as the first 200ms of a 1000ms animation if using + * the original interpolator. + * + * This is useful for the transition between the user dream and the low light clock as some + * animations are defined in the spec to be longer than the total duration of the animation. For + * example, the low light clock exit translation animation is defined to last >1s while the actual + * fade out of the low light clock is only 250ms, meaning the clock isn't visible anymore after + * 250ms. + * + * Since the dream framework currently only allows one dream to be visible and running, we use this + * interpolator to play just the first 250ms of the translation animation. Simply reducing the + * duration of the animation would result in the text exiting much faster than intended, so a custom + * interpolator is needed. + */ +class TruncatedInterpolator( + private val baseInterpolator: Interpolator, + originalDuration: Float, + newDuration: Float +) : Interpolator { + private val scaleFactor: Float + + init { + scaleFactor = newDuration / originalDuration + } + + override fun getInterpolation(input: Float): Float { + return baseInterpolator.getInterpolation(input * scaleFactor) + } +} diff --git a/libs/dream/lowlight/tests/Android.bp b/libs/dream/lowlight/tests/Android.bp index 2d79090cd7d4..64b53cbb5c5a 100644 --- a/libs/dream/lowlight/tests/Android.bp +++ b/libs/dream/lowlight/tests/Android.bp @@ -27,6 +27,7 @@ android_test { "androidx.test.runner", "androidx.test.rules", "androidx.test.ext.junit", + "animationlib", "frameworks-base-testutils", "junit", "kotlinx_coroutines_test", diff --git a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.kt b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.kt index 2a886bc31788..de84adb2e5c2 100644 --- a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.kt +++ b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.kt @@ -152,6 +152,21 @@ class LowLightDreamManagerTest { verify(mDreamManager).setSystemDreamComponent(DREAM_COMPONENT) } + @Test + fun setAmbientLightMode_animationCancelled_SetsSystemDream() = testScope.runTest { + mLowLightDreamManager.setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT) + runCurrent() + cancelEnterAnimations() + runCurrent() + // Animation never finishes, but we should still set the system dream + verify(mDreamManager).setSystemDreamComponent(DREAM_COMPONENT) + } + + private fun cancelEnterAnimations() { + val listener = withArgCaptor { verify(mEnterAnimator).addListener(capture()) } + listener.onAnimationCancel(mEnterAnimator) + } + private fun completeEnterAnimations() { val listener = withArgCaptor { verify(mEnterAnimator).addListener(capture()) } listener.onAnimationEnd(mEnterAnimator) diff --git a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.kt b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.kt index 4c526a6ac69d..9ae304f9763a 100644 --- a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.kt +++ b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.kt @@ -158,26 +158,6 @@ class LowLightTransitionCoordinatorTest { assertThat(job.isCancelled).isTrue() } - @Test - fun shouldCancelAnimatorWhenJobCancelled() = testScope.runTest { - whenever(mEnterListener.onBeforeEnterLowLight()).thenReturn(mAnimator) - val coordinator = LowLightTransitionCoordinator() - coordinator.setLowLightEnterListener(mEnterListener) - val job = launch { - coordinator.waitForLowLightTransitionAnimation(timeout = TIMEOUT, entering = true) - } - runCurrent() - // Animator listener is added and the runnable is not run yet. - verify(mAnimator).addListener(mAnimatorListenerCaptor.capture()) - verify(mAnimator, never()).cancel() - assertThat(job.isCompleted).isFalse() - - job.cancel() - // We should have removed the listener and cancelled the animator - verify(mAnimator).removeListener(mAnimatorListenerCaptor.value) - verify(mAnimator).cancel() - } - companion object { private val TIMEOUT = 1.toDuration(DurationUnit.SECONDS) } diff --git a/libs/dream/lowlight/tests/src/com/android/dream/lowlight/util/TruncatedInterpolatorTest.kt b/libs/dream/lowlight/tests/src/com/android/dream/lowlight/util/TruncatedInterpolatorTest.kt new file mode 100644 index 000000000000..190f02e97136 --- /dev/null +++ b/libs/dream/lowlight/tests/src/com/android/dream/lowlight/util/TruncatedInterpolatorTest.kt @@ -0,0 +1,53 @@ +/* + * 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.dream.lowlight.util + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.app.animation.Interpolators +import com.google.common.truth.Truth +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class TruncatedInterpolatorTest { + @Test + fun truncatedInterpolator_matchesRegularInterpolator() { + val originalInterpolator = Interpolators.EMPHASIZED + val truncatedInterpolator = + TruncatedInterpolator(originalInterpolator, ORIGINAL_DURATION_MS, NEW_DURATION_MS) + + // Both interpolators should start at the same value. + var animationPercent = 0f + Truth.assertThat(truncatedInterpolator.getInterpolation(animationPercent)) + .isEqualTo(originalInterpolator.getInterpolation(animationPercent)) + + animationPercent = 1f + Truth.assertThat(truncatedInterpolator.getInterpolation(animationPercent)) + .isEqualTo(originalInterpolator.getInterpolation(animationPercent * DURATION_RATIO)) + + animationPercent = 0.25f + Truth.assertThat(truncatedInterpolator.getInterpolation(animationPercent)) + .isEqualTo(originalInterpolator.getInterpolation(animationPercent * DURATION_RATIO)) + } + + companion object { + private const val ORIGINAL_DURATION_MS: Float = 1000f + private const val NEW_DURATION_MS: Float = 200f + private const val DURATION_RATIO: Float = NEW_DURATION_MS / ORIGINAL_DURATION_MS + } +} diff --git a/packages/SystemUI/res/layout/status_bar_wifi_group.xml b/packages/SystemUI/res/layout/status_bar_wifi_group.xml deleted file mode 100644 index 6cb6993bb762..000000000000 --- a/packages/SystemUI/res/layout/status_bar_wifi_group.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 2018, 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. -*/ ---> -<com.android.systemui.statusbar.StatusBarWifiView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/wifi_combo" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:gravity="center_vertical" > - - <include layout="@layout/status_bar_wifi_group_inner" /> - -</com.android.systemui.statusbar.StatusBarWifiView>
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt index 0fbeb1a054a7..f3296f0632bb 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt @@ -32,7 +32,7 @@ open class RegionSampler @JvmOverloads constructor( val sampledView: View, - mainExecutor: Executor?, + val mainExecutor: Executor?, val bgExecutor: Executor?, val regionSamplingEnabled: Boolean, val isLockscreen: Boolean = false, @@ -166,7 +166,7 @@ constructor( if (isLockscreen) WallpaperManager.FLAG_LOCK else WallpaperManager.FLAG_SYSTEM ) - onColorsChanged(sampledRegionWithOffset, initialSampling) + mainExecutor?.execute { onColorsChanged(sampledRegionWithOffset, initialSampling) } } ) } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index ca064efd4f76..4b14d3cff718 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -42,8 +42,6 @@ public class QuickStepContract { "com.google.android.apps.nexuslauncher.NexusLauncherActivity"; public static final String KEY_EXTRA_SYSUI_PROXY = "extra_sysui_proxy"; - public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius"; - public static final String KEY_EXTRA_SUPPORTS_WINDOW_CORNERS = "extra_supports_window_corners"; public static final String KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER = "extra_unfold_animation"; // See ISysuiUnlockAnimationController.aidl public static final String KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER = "unlock_animation"; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java deleted file mode 100644 index 74c325dea15c..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +++ /dev/null @@ -1,541 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.shared.system; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; -import static android.view.WindowManager.TRANSIT_CHANGE; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED; -import static android.view.WindowManager.TRANSIT_SLEEP; - -import android.annotation.SuppressLint; -import android.app.ActivityManager; -import android.app.ActivityTaskManager; -import android.app.IApplicationThread; -import android.graphics.Rect; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.ArrayMap; -import android.util.Log; -import android.view.IRecentsAnimationController; -import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl; -import android.window.IRemoteTransition; -import android.window.IRemoteTransitionFinishedCallback; -import android.window.PictureInPictureSurfaceTransaction; -import android.window.RemoteTransition; -import android.window.TaskSnapshot; -import android.window.TransitionInfo; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.wm.shell.util.TransitionUtil; - -import java.util.ArrayList; -import java.util.HashMap; - -/** - * Helper class to build {@link RemoteTransition} objects - */ -public class RemoteTransitionCompat { - private static final String TAG = "RemoteTransitionCompat"; - - /** Constructor specifically for recents animation */ - public static RemoteTransition newRemoteTransition(RecentsAnimationListener recents, - IApplicationThread appThread) { - IRemoteTransition remote = new IRemoteTransition.Stub() { - final RecentsControllerWrap mRecentsSession = new RecentsControllerWrap(); - IBinder mToken = null; - - @Override - public void startAnimation(IBinder transition, TransitionInfo info, - SurfaceControl.Transaction t, - IRemoteTransitionFinishedCallback finishedCallback) { - // TODO(b/177438007): Move this set-up logic into launcher's animation impl. - mToken = transition; - mRecentsSession.start(recents, mToken, info, t, finishedCallback); - } - - @Override - public void mergeAnimation(IBinder transition, TransitionInfo info, - SurfaceControl.Transaction t, IBinder mergeTarget, - IRemoteTransitionFinishedCallback finishedCallback) { - if (mergeTarget.equals(mToken) && mRecentsSession.merge(info, t)) { - try { - finishedCallback.onTransitionFinished(null /* wct */, null /* sct */); - } catch (RemoteException e) { - Log.e(TAG, "Error merging transition.", e); - } - // commit taskAppeared after merge transition finished. - mRecentsSession.commitTasksAppearedIfNeeded(); - } else { - t.close(); - info.releaseAllSurfaces(); - } - } - }; - return new RemoteTransition(remote, appThread, "Recents"); - } - - /** - * Wrapper to hook up parts of recents animation to shell transition. - * TODO(b/177438007): Remove this once Launcher handles shell transitions directly. - */ - @VisibleForTesting - static class RecentsControllerWrap extends IRecentsAnimationController.Default { - private RecentsAnimationListener mListener = null; - private IRemoteTransitionFinishedCallback mFinishCB = null; - - /** - * List of tasks that we are switching away from via this transition. Upon finish, these - * pausing tasks will become invisible. - * These need to be ordered since the order must be restored if there is no task-switch. - */ - private ArrayList<TaskState> mPausingTasks = null; - - /** - * List of tasks that we are switching to. Upon finish, these will remain visible and - * on top. - */ - private ArrayList<TaskState> mOpeningTasks = null; - - private WindowContainerToken mPipTask = null; - private WindowContainerToken mRecentsTask = null; - private int mRecentsTaskId = 0; - private TransitionInfo mInfo = null; - private boolean mOpeningSeparateHome = false; - private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null; - private PictureInPictureSurfaceTransaction mPipTransaction = null; - private IBinder mTransition = null; - private boolean mKeyguardLocked = false; - private RemoteAnimationTarget[] mAppearedTargets; - private boolean mWillFinishToHome = false; - - /** The animation is idle, waiting for the user to choose a task to switch to. */ - private static final int STATE_NORMAL = 0; - - /** The user chose a new task to switch to and the animation is animating to it. */ - private static final int STATE_NEW_TASK = 1; - - /** The latest state that the recents animation is operating in. */ - private int mState = STATE_NORMAL; - - void start(RecentsAnimationListener listener, - IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, - IRemoteTransitionFinishedCallback finishedCallback) { - if (mInfo != null) { - throw new IllegalStateException("Trying to run a new recents animation while" - + " recents is already active."); - } - mListener = listener; - mInfo = info; - mFinishCB = finishedCallback; - mPausingTasks = new ArrayList<>(); - mOpeningTasks = new ArrayList<>(); - mPipTask = null; - mRecentsTask = null; - mRecentsTaskId = -1; - mLeashMap = new ArrayMap<>(); - mTransition = transition; - mKeyguardLocked = (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0; - mState = STATE_NORMAL; - - final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>(); - final ArrayList<RemoteAnimationTarget> wallpapers = new ArrayList<>(); - TransitionUtil.LeafTaskFilter leafTaskFilter = new TransitionUtil.LeafTaskFilter(); - // About layering: we divide up the "layer space" into 3 regions (each the size of - // the change count). This lets us categorize things into above/below/between - // while maintaining their relative ordering. - for (int i = 0; i < info.getChanges().size(); ++i) { - final TransitionInfo.Change change = info.getChanges().get(i); - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (TransitionUtil.isWallpaper(change)) { - final RemoteAnimationTarget target = TransitionUtil.newTarget(change, - // wallpapers go into the "below" layer space - info.getChanges().size() - i, info, t, mLeashMap); - wallpapers.add(target); - // Make all the wallpapers opaque since we want them visible from the start - t.setAlpha(target.leash, 1); - } else if (leafTaskFilter.test(change)) { - // start by putting everything into the "below" layer space. - final RemoteAnimationTarget target = TransitionUtil.newTarget(change, - info.getChanges().size() - i, info, t, mLeashMap); - apps.add(target); - if (TransitionUtil.isClosingType(change.getMode())) { - // raise closing (pausing) task to "above" layer so it isn't covered - t.setLayer(target.leash, info.getChanges().size() * 3 - i); - mPausingTasks.add(new TaskState(change, target.leash)); - if (taskInfo.pictureInPictureParams != null - && taskInfo.pictureInPictureParams.isAutoEnterEnabled()) { - mPipTask = taskInfo.token; - } - } else if (taskInfo != null - && taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) { - // There's a 3p launcher, so make sure recents goes above that. - t.setLayer(target.leash, info.getChanges().size() * 3 - i); - mRecentsTask = taskInfo.token; - mRecentsTaskId = taskInfo.taskId; - } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { - mRecentsTask = taskInfo.token; - mRecentsTaskId = taskInfo.taskId; - } else if (TransitionUtil.isOpeningType(change.getMode())) { - mOpeningTasks.add(new TaskState(change, target.leash)); - } - } - } - t.apply(); - mListener.onAnimationStart(new RecentsAnimationControllerCompat(this), - apps.toArray(new RemoteAnimationTarget[apps.size()]), - wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]), - new Rect(0, 0, 0, 0), new Rect()); - } - - @SuppressLint("NewApi") - boolean merge(TransitionInfo info, SurfaceControl.Transaction t) { - if (info.getType() == TRANSIT_SLEEP) { - // A sleep event means we need to stop animations immediately, so cancel here. - mListener.onAnimationCanceled(new HashMap<>()); - finish(mWillFinishToHome, false /* userLeaveHint */); - return false; - } - ArrayList<TransitionInfo.Change> openingTasks = null; - ArrayList<TransitionInfo.Change> closingTasks = null; - mAppearedTargets = null; - mOpeningSeparateHome = false; - TransitionInfo.Change recentsOpening = null; - boolean foundRecentsClosing = false; - boolean hasChangingApp = false; - final TransitionUtil.LeafTaskFilter leafTaskFilter = - new TransitionUtil.LeafTaskFilter(); - for (int i = 0; i < info.getChanges().size(); ++i) { - final TransitionInfo.Change change = info.getChanges().get(i); - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - final boolean isLeafTask = leafTaskFilter.test(change); - if (TransitionUtil.isOpeningType(change.getMode())) { - if (mRecentsTask.equals(change.getContainer())) { - recentsOpening = change; - } else if (isLeafTask) { - if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { - // This is usually a 3p launcher - mOpeningSeparateHome = true; - } - if (openingTasks == null) { - openingTasks = new ArrayList<>(); - } - openingTasks.add(change); - } - } else if (TransitionUtil.isClosingType(change.getMode())) { - if (mRecentsTask.equals(change.getContainer())) { - foundRecentsClosing = true; - } else if (isLeafTask) { - if (closingTasks == null) { - closingTasks = new ArrayList<>(); - } - closingTasks.add(change); - } - } else if (change.getMode() == TRANSIT_CHANGE) { - // Finish recents animation if the display is changed, so the default - // transition handler can play the animation such as rotation effect. - if (change.hasFlags(TransitionInfo.FLAG_IS_DISPLAY)) { - mListener.onSwitchToScreenshot(() -> finish(false /* toHome */, - false /* userLeaveHint */)); - return false; - } - hasChangingApp = true; - } - } - if (hasChangingApp && foundRecentsClosing) { - // This happens when a visible app is expanding (usually PiP). In this case, - // that transition probably has a special-purpose animation, so finish recents - // now and let it do its animation (since recents is going to be occluded). - if (!mListener.onSwitchToScreenshot( - () -> finish(true /* toHome */, false /* userLeaveHint */))) { - Log.w(TAG, "Recents callback doesn't support support switching to screenshot" - + ", there might be a flicker."); - finish(true /* toHome */, false /* userLeaveHint */); - } - return false; - } - if (recentsOpening != null) { - // the recents task re-appeared. This happens if the user gestures before the - // task-switch (NEW_TASK) animation finishes. - if (mState == STATE_NORMAL) { - Log.e(TAG, "Returning to recents while recents is already idle."); - } - if (closingTasks == null || closingTasks.size() == 0) { - Log.e(TAG, "Returning to recents without closing any opening tasks."); - } - // Setup may hide it initially since it doesn't know that overview was still active. - t.show(recentsOpening.getLeash()); - t.setAlpha(recentsOpening.getLeash(), 1.f); - mState = STATE_NORMAL; - } - boolean didMergeThings = false; - if (closingTasks != null) { - // Cancelling a task-switch. Move the tasks back to mPausing from mOpening - for (int i = 0; i < closingTasks.size(); ++i) { - final TransitionInfo.Change change = closingTasks.get(i); - int openingIdx = TaskState.indexOf(mOpeningTasks, change); - if (openingIdx < 0) { - Log.e(TAG, "Back to existing recents animation from an unrecognized " - + "task: " + change.getTaskInfo().taskId); - continue; - } - mPausingTasks.add(mOpeningTasks.remove(openingIdx)); - didMergeThings = true; - } - } - if (openingTasks != null && openingTasks.size() > 0) { - // Switching to some new tasks, add to mOpening and remove from mPausing. Also, - // enter NEW_TASK state since this will start the switch-to animation. - final int layer = mInfo.getChanges().size() * 3; - final RemoteAnimationTarget[] targets = - new RemoteAnimationTarget[openingTasks.size()]; - for (int i = 0; i < openingTasks.size(); ++i) { - final TransitionInfo.Change change = openingTasks.get(i); - int pausingIdx = TaskState.indexOf(mPausingTasks, change); - if (pausingIdx >= 0) { - // Something is showing/opening a previously-pausing app. - targets[i] = TransitionUtil.newTarget(change, layer, - mPausingTasks.get(pausingIdx).mLeash); - mOpeningTasks.add(mPausingTasks.remove(pausingIdx)); - // Setup hides opening tasks initially, so make it visible again (since we - // are already showing it). - t.show(change.getLeash()); - t.setAlpha(change.getLeash(), 1.f); - } else { - // We are receiving new opening tasks, so convert to onTasksAppeared. - targets[i] = TransitionUtil.newTarget(change, layer, info, t, mLeashMap); - // reparent into the original `mInfo` since that's where we are animating. - final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo); - t.reparent(targets[i].leash, mInfo.getRoot(rootIdx).getLeash()); - t.setLayer(targets[i].leash, layer); - mOpeningTasks.add(new TaskState(change, targets[i].leash)); - } - } - didMergeThings = true; - mState = STATE_NEW_TASK; - mAppearedTargets = targets; - } - if (!didMergeThings) { - // Didn't recognize anything in incoming transition so don't merge it. - Log.w(TAG, "Don't know how to merge this transition."); - return false; - } - t.apply(); - // not using the incoming anim-only surfaces - info.releaseAnimSurfaces(); - return true; - } - - private void commitTasksAppearedIfNeeded() { - if (mAppearedTargets != null) { - mListener.onTasksAppeared(mAppearedTargets); - mAppearedTargets = null; - } - } - - @Override public TaskSnapshot screenshotTask(int taskId) { - try { - return ActivityTaskManager.getService().takeTaskSnapshot(taskId, - true /* updateCache */); - } catch (RemoteException e) { - Log.e(TAG, "Failed to screenshot task", e); - } - return null; - } - - @Override public void setInputConsumerEnabled(boolean enabled) { - if (!enabled) return; - // transient launches don't receive focus automatically. Since we are taking over - // the gesture now, take focus explicitly. - // This also moves recents back to top if the user gestured before a switch - // animation finished. - try { - ActivityTaskManager.getService().setFocusedTask(mRecentsTaskId); - } catch (RemoteException e) { - Log.e(TAG, "Failed to set focused task", e); - } - } - - @Override public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) { - } - - @Override public void setFinishTaskTransaction(int taskId, - PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) { - mPipTransaction = finishTransaction; - } - - @Override - @SuppressLint("NewApi") - public void finish(boolean toHome, boolean sendUserLeaveHint) { - if (mFinishCB == null) { - Log.e(TAG, "Duplicate call to finish", new RuntimeException()); - return; - } - final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - final WindowContainerTransaction wct = new WindowContainerTransaction(); - - if (mKeyguardLocked && mRecentsTask != null) { - if (toHome) wct.reorder(mRecentsTask, true /* toTop */); - else wct.restoreTransientOrder(mRecentsTask); - } - if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) { - // The gesture is returning to the pausing-task(s) rather than continuing with - // recents, so end the transition by moving the app back to the top (and also - // re-showing it's task). - for (int i = mPausingTasks.size() - 1; i >= 0; --i) { - // reverse order so that index 0 ends up on top - wct.reorder(mPausingTasks.get(i).mToken, true /* onTop */); - t.show(mPausingTasks.get(i).mTaskSurface); - } - if (!mKeyguardLocked && mRecentsTask != null) { - wct.restoreTransientOrder(mRecentsTask); - } - } else if (toHome && mOpeningSeparateHome && mPausingTasks != null) { - // Special situation where 3p launcher was changed during recents (this happens - // during tapltests...). Here we get both "return to home" AND "home opening". - // This is basically going home, but we have to restore the recents and home order. - for (int i = 0; i < mOpeningTasks.size(); ++i) { - final TaskState state = mOpeningTasks.get(i); - if (state.mTaskInfo.topActivityType == ACTIVITY_TYPE_HOME) { - // Make sure it is on top. - wct.reorder(state.mToken, true /* onTop */); - } - t.show(state.mTaskSurface); - } - for (int i = mPausingTasks.size() - 1; i >= 0; --i) { - t.hide(mPausingTasks.get(i).mTaskSurface); - } - if (!mKeyguardLocked && mRecentsTask != null) { - wct.restoreTransientOrder(mRecentsTask); - } - } else { - // The general case: committing to recents, going home, or switching tasks. - for (int i = 0; i < mOpeningTasks.size(); ++i) { - t.show(mOpeningTasks.get(i).mTaskSurface); - } - for (int i = 0; i < mPausingTasks.size(); ++i) { - if (!sendUserLeaveHint) { - // This means recents is not *actually* finishing, so of course we gotta - // do special stuff in WMCore to accommodate. - wct.setDoNotPip(mPausingTasks.get(i).mToken); - } - // Since we will reparent out of the leashes, pre-emptively hide the child - // surface to match the leash. Otherwise, there will be a flicker before the - // visibility gets committed in Core when using split-screen (in splitscreen, - // the leaf-tasks are not "independent" so aren't hidden by normal setup). - t.hide(mPausingTasks.get(i).mTaskSurface); - } - if (mPipTask != null && mPipTransaction != null && sendUserLeaveHint) { - t.show(mInfo.getChange(mPipTask).getLeash()); - PictureInPictureSurfaceTransaction.apply(mPipTransaction, - mInfo.getChange(mPipTask).getLeash(), t); - mPipTask = null; - mPipTransaction = null; - } - } - try { - mFinishCB.onTransitionFinished(wct.isEmpty() ? null : wct, t); - } catch (RemoteException e) { - Log.e(TAG, "Failed to call animation finish callback", e); - t.apply(); - } - // Only release the non-local created surface references. The animator is responsible - // for releasing the leashes created by local. - mInfo.releaseAllSurfaces(); - // Reset all members. - mListener = null; - mFinishCB = null; - mPausingTasks = null; - mOpeningTasks = null; - mAppearedTargets = null; - mInfo = null; - mOpeningSeparateHome = false; - mLeashMap = null; - mTransition = null; - mState = STATE_NORMAL; - } - - @Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { - } - - @Override public void cleanupScreenshot() { - } - - @Override public void setWillFinishToHome(boolean willFinishToHome) { - mWillFinishToHome = willFinishToHome; - } - - /** - * @see IRecentsAnimationController#removeTask - */ - @Override public boolean removeTask(int taskId) { - return false; - } - - /** - * @see IRecentsAnimationController#detachNavigationBarFromApp - */ - @Override public void detachNavigationBarFromApp(boolean moveHomeToTop) { - try { - ActivityTaskManager.getService().detachNavigationBarFromApp(mTransition); - } catch (RemoteException e) { - Log.e(TAG, "Failed to detach the navigation bar from app", e); - } - } - - /** - * @see IRecentsAnimationController#animateNavigationBarToApp(long) - */ - @Override public void animateNavigationBarToApp(long duration) { - } - } - - /** Utility class to track the state of a task as-seen by recents. */ - private static class TaskState { - WindowContainerToken mToken; - ActivityManager.RunningTaskInfo mTaskInfo; - - /** The surface/leash of the task provided by Core. */ - SurfaceControl mTaskSurface; - - /** The (local) animation-leash created for this task. */ - SurfaceControl mLeash; - - TaskState(TransitionInfo.Change change, SurfaceControl leash) { - mToken = change.getContainer(); - mTaskInfo = change.getTaskInfo(); - mTaskSurface = change.getLeash(); - mLeash = leash; - } - - static int indexOf(ArrayList<TaskState> list, TransitionInfo.Change change) { - for (int i = list.size() - 1; i >= 0; --i) { - if (list.get(i).mToken.equals(change.getContainer())) { - return i; - } - } - return -1; - } - - public String toString() { - return "" + mToken + " : " + mLeash; - } - } -} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java deleted file mode 100644 index 98212e1d91b6..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.shared.tracing; - -import android.os.Trace; -import android.util.Log; -import android.view.Choreographer; - -import com.android.internal.util.TraceBuffer; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Queue; -import java.util.function.Consumer; - -/** - * A proto tracer implementation that can be updated directly (upon state change), or on the next - * scheduled frame. - * - * @param <P> The class type of the proto provider - * @param <S> The proto class type of the encapsulating proto - * @param <T> The proto class type of the individual proto entries in the buffer - * @param <R> The proto class type of the entry root proto in the buffer - */ -public class FrameProtoTracer<P, S extends P, T extends P, R> - implements Choreographer.FrameCallback { - - private static final String TAG = "FrameProtoTracer"; - private static final int BUFFER_CAPACITY = 1024 * 1024; - - private final Object mLock = new Object(); - private final TraceBuffer<P, S, T> mBuffer; - private final File mTraceFile; - private final ProtoTraceParams<P, S, T, R> mParams; - private Choreographer mChoreographer; - private final Queue<T> mPool = new ArrayDeque<>(); - private final ArrayList<ProtoTraceable<R>> mTraceables = new ArrayList<>(); - private final ArrayList<ProtoTraceable<R>> mTmpTraceables = new ArrayList<>(); - - private volatile boolean mEnabled; - private boolean mFrameScheduled; - - private final TraceBuffer.ProtoProvider<P, S, T> mProvider = - new TraceBuffer.ProtoProvider<P, S, T>() { - @Override - public int getItemSize(P proto) { - return mParams.getProtoSize(proto); - } - - @Override - public byte[] getBytes(P proto) { - return mParams.getProtoBytes(proto); - } - - @Override - public void write(S encapsulatingProto, Queue<T> buffer, OutputStream os) - throws IOException { - os.write(mParams.serializeEncapsulatingProto(encapsulatingProto, buffer)); - } - }; - - public interface ProtoTraceParams<P, S, T, R> { - File getTraceFile(); - S getEncapsulatingTraceProto(); - T updateBufferProto(T reuseObj, ArrayList<ProtoTraceable<R>> traceables); - byte[] serializeEncapsulatingProto(S encapsulatingProto, Queue<T> buffer); - byte[] getProtoBytes(P proto); - int getProtoSize(P proto); - } - - public FrameProtoTracer(ProtoTraceParams<P, S, T, R> params) { - mParams = params; - mBuffer = new TraceBuffer<>(BUFFER_CAPACITY, mProvider, new Consumer<T>() { - @Override - public void accept(T t) { - onProtoDequeued(t); - } - }); - mTraceFile = params.getTraceFile(); - } - - public void start() { - synchronized (mLock) { - if (mEnabled) { - return; - } - mBuffer.resetBuffer(); - mEnabled = true; - } - logState(); - } - - public void stop() { - synchronized (mLock) { - if (!mEnabled) { - return; - } - mEnabled = false; - } - writeToFile(); - } - - public boolean isEnabled() { - return mEnabled; - } - - public void add(ProtoTraceable<R> traceable) { - synchronized (mLock) { - mTraceables.add(traceable); - } - } - - public void remove(ProtoTraceable<R> traceable) { - synchronized (mLock) { - mTraceables.remove(traceable); - } - } - - public void scheduleFrameUpdate() { - if (!mEnabled || mFrameScheduled) { - return; - } - - // Schedule an update on the next frame - if (mChoreographer == null) { - mChoreographer = Choreographer.getMainThreadInstance(); - } - mChoreographer.postFrameCallback(this); - mFrameScheduled = true; - } - - public void update() { - if (!mEnabled) { - return; - } - - logState(); - } - - public float getBufferUsagePct() { - return (float) mBuffer.getBufferSize() / BUFFER_CAPACITY; - } - - @Override - public void doFrame(long frameTimeNanos) { - logState(); - } - - private void onProtoDequeued(T proto) { - mPool.add(proto); - } - - private void logState() { - synchronized (mLock) { - mTmpTraceables.addAll(mTraceables); - } - - mBuffer.add(mParams.updateBufferProto(mPool.poll(), mTmpTraceables)); - mTmpTraceables.clear(); - mFrameScheduled = false; - } - - private void writeToFile() { - try { - Trace.beginSection("ProtoTracer.writeToFile"); - mBuffer.writeTraceToFile(mTraceFile, mParams.getEncapsulatingTraceProto()); - } catch (IOException e) { - Log.e(TAG, "Unable to write buffer to file", e); - } finally { - Trace.endSection(); - } - } -} - - diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/ProtoTraceable.java b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/ProtoTraceable.java deleted file mode 100644 index e05b0b074449..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/ProtoTraceable.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -package com.android.systemui.shared.tracing; - -/** - * @see FrameProtoTracer - */ -public interface ProtoTraceable<T> { - - /** - * NOTE: Implementations should update all fields in this proto. - */ - void writeToProto(T proto); -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java index 794e6941c2bb..b3e08c0bc69f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java @@ -165,15 +165,18 @@ public class KeyguardPinViewController * Responsible for identifying if PIN hinting is to be enabled or not */ private boolean isPinHinting() { - return mLockPatternUtils.getPinLength(KeyguardUpdateMonitor.getCurrentUser()) - == DEFAULT_PIN_LENGTH; + return mPinLength == DEFAULT_PIN_LENGTH; } /** - * Responsible for identifying if auto confirm is enabled or not in Settings + * Responsible for identifying if auto confirm is enabled or not in Settings and + * a valid PIN_LENGTH is stored on the device (though the latter check is only to make it more + * robust since we only allow enabling PIN confirmation if the user has a valid PIN length + * saved on device) */ private boolean isAutoPinConfirmEnabledInSettings() { //Checks if user has enabled the auto confirm in Settings - return mLockPatternUtils.isAutoPinConfirmEnabled(KeyguardUpdateMonitor.getCurrentUser()); + return mLockPatternUtils.isAutoPinConfirmEnabled(KeyguardUpdateMonitor.getCurrentUser()) + && mPinLength != LockPatternUtils.PIN_LENGTH_UNAVAILABLE; } } diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java index a7d4455b43c2..87627698597f 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java @@ -20,6 +20,7 @@ import com.android.keyguard.CarrierText; import com.android.systemui.R; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.statusbar.phone.KeyguardStatusBarView; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; import dagger.Module; @@ -44,6 +45,13 @@ public abstract class KeyguardStatusBarViewModule { /** */ @Provides @KeyguardStatusBarViewScope + static StatusBarLocation getStatusBarLocation() { + return StatusBarLocation.KEYGUARD; + } + + /** */ + @Provides + @KeyguardStatusBarViewScope static StatusBarUserSwitcherContainer getUserSwitcherContainer(KeyguardStatusBarView view) { return view.findViewById(R.id.user_switcher_container); } diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 1f1b154ef1c8..04acd0b91173 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -123,7 +123,6 @@ import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.systemui.telephony.TelephonyListenerManager; -import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.tuner.TunablePadding.TunablePaddingService; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.DeviceConfigProxy; @@ -335,7 +334,6 @@ public class Dependency { @Inject Lazy<IWallpaperManager> mWallpaperManager; @Inject Lazy<CommandQueue> mCommandQueue; @Inject Lazy<RecordingController> mRecordingController; - @Inject Lazy<ProtoTracer> mProtoTracer; @Inject Lazy<MediaOutputDialogFactory> mMediaOutputDialogFactory; @Inject Lazy<DeviceConfigProxy> mDeviceConfigProxy; @Inject Lazy<TelephonyListenerManager> mTelephonyListenerManager; @@ -528,7 +526,6 @@ public class Dependency { mProviders.put(DozeParameters.class, mDozeParameters::get); mProviders.put(IWallpaperManager.class, mWallpaperManager::get); mProviders.put(CommandQueue.class, mCommandQueue::get); - mProviders.put(ProtoTracer.class, mProtoTracer::get); mProviders.put(DeviceConfigProxy.class, mDeviceConfigProxy::get); mProviders.put(TelephonyListenerManager.class, mTelephonyListenerManager::get); diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt index 43fb3630bd19..444491f53629 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt @@ -144,12 +144,7 @@ constructor( private val lockPatternUtils: LockPatternUtils, ) : AuthenticationRepository { - override val isUnlocked: StateFlow<Boolean> = - keyguardRepository.isKeyguardUnlocked.stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = false, - ) + override val isUnlocked: StateFlow<Boolean> = keyguardRepository.isKeyguardUnlocked override suspend fun isLockscreenEnabled(): Boolean { return withContext(backgroundDispatcher) { diff --git a/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt b/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt index b52ddc1dbc42..b34f1b45d763 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt +++ b/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt @@ -87,6 +87,10 @@ class AccessorizedBatteryDrawable( } var displayShield: Boolean = false + set(value) { + field = value + postInvalidate() + } private fun updateSizes() { val b = bounds @@ -204,4 +208,11 @@ class AccessorizedBatteryDrawable( val shieldPathString = context.resources.getString(R.string.config_batterymeterShieldPath) shieldPath.set(PathParser.createPathFromPathData(shieldPathString)) } + + private val invalidateRunnable: () -> Unit = { invalidateSelf() } + + private fun postInvalidate() { + unscheduleSelf(invalidateRunnable) + scheduleSelf(invalidateRunnable, 0) + } } diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java index c1238d911ecf..4e8383c3e39e 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java @@ -464,9 +464,11 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver { public void dump(PrintWriter pw, String[] args) { String powerSave = mDrawable == null ? null : mDrawable.getPowerSaveEnabled() + ""; + String displayShield = mDrawable == null ? null : mDrawable.getDisplayShield() + ""; CharSequence percent = mBatteryPercentView == null ? null : mBatteryPercentView.getText(); pw.println(" BatteryMeterView:"); pw.println(" mDrawable.getPowerSave: " + powerSave); + pw.println(" mDrawable.getDisplayShield: " + displayShield); pw.println(" mBatteryPercentView.getText(): " + percent); pw.println(" mTextColor: #" + Integer.toHexString(mTextColor)); pw.println(" mBatteryStateUnknown: " + mBatteryStateUnknown); diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java index f6a10bd4af4a..6a5749cc5571 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java @@ -30,16 +30,18 @@ import android.view.View; import androidx.annotation.NonNull; +import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.StatusBarIconController; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.ViewController; +import java.io.PrintWriter; + import javax.inject.Inject; /** Controller for {@link BatteryMeterView}. **/ @@ -53,6 +55,7 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> private final String mSlotBattery; private final SettingObserver mSettingObserver; private final UserTracker mUserTracker; + private final StatusBarLocation mLocation; private final ConfigurationController.ConfigurationListener mConfigurationListener = new ConfigurationController.ConfigurationListener() { @@ -94,6 +97,13 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> public void onIsBatteryDefenderChanged(boolean isBatteryDefender) { mView.onIsBatteryDefenderChanged(isBatteryDefender); } + + @Override + public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { + pw.print(super.toString()); + pw.println(" location=" + mLocation); + mView.dump(pw, args); + } }; private final UserTracker.Callback mUserChangedCallback = @@ -113,14 +123,15 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> @Inject public BatteryMeterViewController( BatteryMeterView view, + StatusBarLocation location, UserTracker userTracker, ConfigurationController configurationController, TunerService tunerService, @Main Handler mainHandler, ContentResolver contentResolver, - FeatureFlags featureFlags, BatteryController batteryController) { super(view); + mLocation = location; mUserTracker = userTracker; mConfigurationController = configurationController; mTunerService = tunerService; @@ -129,7 +140,8 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> mBatteryController = batteryController; mView.setBatteryEstimateFetcher(mBatteryController::getEstimatedTimeRemainingString); - mView.setDisplayShieldEnabled(featureFlags.isEnabled(Flags.BATTERY_SHIELD_ICON)); + mView.setDisplayShieldEnabled( + getContext().getResources().getBoolean(R.bool.flag_battery_shield_icon)); mSlotBattery = getResources().getString(com.android.internal.R.string.status_bar_battery); mSettingObserver = new SettingObserver(mMainHandler); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java index a90980fddfb0..046ccf165d07 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java @@ -18,6 +18,7 @@ package com.android.systemui.dagger; import com.android.systemui.globalactions.ShutdownUiModule; import com.android.systemui.keyguard.CustomizationProvider; +import com.android.systemui.shade.ShadeModule; import com.android.systemui.statusbar.NotificationInsetsModule; import com.android.systemui.statusbar.QsFrameTranslateModule; @@ -32,6 +33,7 @@ import dagger.Subcomponent; DependencyProvider.class, NotificationInsetsModule.class, QsFrameTranslateModule.class, + ShadeModule.class, ShutdownUiModule.class, SystemUIBinder.class, SystemUIModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 3b897394c515..3c42a29451ca 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -82,7 +82,6 @@ import com.android.systemui.screenshot.dagger.ScreenshotModule; import com.android.systemui.security.data.repository.SecurityRepositoryModule; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.shade.ShadeController; -import com.android.systemui.shade.ShadeModule; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.shade.transition.LargeScreenShadeInterpolatorImpl; import com.android.systemui.shared.condition.Monitor; @@ -199,7 +198,6 @@ import javax.inject.Named; SecurityRepositoryModule.class, ScreenRecordModule.class, SettingsUtilModule.class, - ShadeModule.class, SmartRepliesInflationModule.class, SmartspaceModule.class, StatusBarPipelineModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt index ee046c27d72e..484bf3d51f36 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt @@ -26,6 +26,7 @@ import androidx.core.animation.doOnEnd import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators +import com.android.dream.lowlight.util.TruncatedInterpolator import com.android.systemui.R import com.android.systemui.complication.ComplicationHostViewController import com.android.systemui.complication.ComplicationLayoutParams @@ -204,31 +205,28 @@ constructor( translationYAnimator( from = 0f, to = -mDreamInTranslationYDistance.toFloat(), - durationMs = mDreamInTranslationYDurationMs, + durationMs = mDreamInComplicationsAnimDurationMs, delayMs = 0, - interpolator = Interpolators.EMPHASIZED + // Truncate the animation from the full duration to match the alpha + // animation so that the whole animation ends at the same time. + interpolator = + TruncatedInterpolator( + Interpolators.EMPHASIZED, + /*originalDuration=*/ mDreamInTranslationYDurationMs.toFloat(), + /*newDuration=*/ mDreamInComplicationsAnimDurationMs.toFloat() + ) ), alphaAnimator( - from = - mCurrentAlphaAtPosition.getOrDefault( - key = POSITION_BOTTOM, - defaultValue = 1f - ), - to = 0f, - durationMs = mDreamInComplicationsAnimDurationMs, - delayMs = 0, - positions = POSITION_BOTTOM - ) - .apply { - doOnEnd { - // The logical end of the animation is once the alpha and blur - // animations finish, end the animation so that any listeners are - // notified. The Y translation animation is much longer than all of - // the other animations due to how the spec is defined, but is not - // expected to run to completion. - mAnimator?.end() - } - }, + from = + mCurrentAlphaAtPosition.getOrDefault( + key = POSITION_BOTTOM, + defaultValue = 1f + ), + to = 0f, + durationMs = mDreamInComplicationsAnimDurationMs, + delayMs = 0, + positions = POSITION_BOTTOM + ), alphaAnimator( from = mCurrentAlphaAtPosition.getOrDefault( diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index a5a799565eaf..83c1c7100761 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -290,12 +290,17 @@ object Flags { /** Migrate the lock icon view to the new keyguard root view. */ // TODO(b/286552209): Tracking bug. @JvmField - val MIGRATE_LOCK_ICON = unreleasedFlag(240, "migrate_lock_icon") + val MIGRATE_LOCK_ICON = unreleasedFlag(240, "migrate_lock_icon", teamfood = true) // TODO(b/288276738): Tracking bug. @JvmField val WIDGET_ON_KEYGUARD = unreleasedFlag(241, "widget_on_keyguard") + /** Migrate the NSSL to the a sibling to both the panel and keyguard root view. */ + // TODO(b/288074305): Tracking bug. + @JvmField + val MIGRATE_NSSL = unreleasedFlag(242, "migrate_nssl") + // 300 - power menu // TODO(b/254512600): Tracking Bug @JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite") @@ -367,22 +372,10 @@ object Flags { // TODO(b/256614753): Tracking Bug val NEW_STATUS_BAR_MOBILE_ICONS = releasedFlag(606, "new_status_bar_mobile_icons") - // TODO(b/256614210): Tracking Bug - val NEW_STATUS_BAR_WIFI_ICON = releasedFlag(607, "new_status_bar_wifi_icon") - // TODO(b/256614751): Tracking Bug val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND = unreleasedFlag(608, "new_status_bar_mobile_icons_backend", teamfood = true) - // TODO(b/256613548): Tracking Bug - val NEW_STATUS_BAR_WIFI_ICON_BACKEND = - unreleasedFlag(609, "new_status_bar_wifi_icon_backend", teamfood = true) - - // TODO(b/256623670): Tracking Bug - @JvmField - val BATTERY_SHIELD_ICON = - resourceBooleanFlag(610, R.bool.flag_battery_shield_icon, "battery_shield_icon") - // TODO(b/260881289): Tracking Bug val NEW_STATUS_BAR_ICONS_DEBUG_COLORING = unreleasedFlag(611, "new_status_bar_icons_debug_coloring") @@ -799,4 +792,11 @@ object Flags { // TODO(b/285174336): Tracking Bug @JvmField val USE_REPOS_FOR_BOUNCER_SHOWING = unreleasedFlag(2900, "use_repos_for_bouncer_showing") + + // 3100 - Haptic interactions + + // TODO(b/290213663): Tracking Bug + @JvmField + val ONE_WAY_HAPTICS_API_MIGRATION = + unreleasedFlag(3100, "oneway_haptics_api_migration") } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index e7704d625168..d119920e1a42 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -87,7 +87,7 @@ interface KeyguardRepository { val isKeyguardShowing: Flow<Boolean> /** Is the keyguard in a unlocked state? */ - val isKeyguardUnlocked: Flow<Boolean> + val isKeyguardUnlocked: StateFlow<Boolean> /** Is an activity showing over the keyguard? */ val isKeyguardOccluded: Flow<Boolean> @@ -299,7 +299,7 @@ constructor( } .distinctUntilChanged() - override val isKeyguardUnlocked: Flow<Boolean> = + override val isKeyguardUnlocked: StateFlow<Boolean> = conflatedCallbackFlow { val callback = object : KeyguardStateController.Callback { @@ -330,7 +330,11 @@ constructor( awaitClose { keyguardStateController.removeCallback(callback) } } - .distinctUntilChanged() + .stateIn( + scope = scope, + started = SharingStarted.WhileSubscribed(), + initialValue = keyguardStateController.isUnlocked, + ) override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow { val callback = diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index a8af67a9fc97..b21b0017208c 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -88,11 +88,7 @@ import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.TaskStackChangeListeners; -import com.android.systemui.shared.tracing.ProtoTraceable; import com.android.systemui.statusbar.phone.LightBarController; -import com.android.systemui.tracing.ProtoTracer; -import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto; -import com.android.systemui.tracing.nano.SystemUiTraceProto; import com.android.systemui.util.Assert; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.desktopmode.DesktopMode; @@ -115,8 +111,7 @@ import javax.inject.Provider; /** * Utility class to handle edge swipes for back gesture */ -public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBackPlugin>, - ProtoTraceable<SystemUiTraceProto> { +public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBackPlugin> { private static final String TAG = "EdgeBackGestureHandler"; private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt( @@ -192,7 +187,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private Consumer<Boolean> mButtonForcedVisibleCallback; private final PluginManager mPluginManager; - private final ProtoTracer mProtoTracer; private final NavigationModeController mNavigationModeController; private final BackPanelController.Factory mBackPanelControllerFactory; private final ViewConfiguration mViewConfiguration; @@ -402,7 +396,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack @Main Handler handler, @Background Executor backgroundExecutor, UserTracker userTracker, - ProtoTracer protoTracer, NavigationModeController navigationModeController, BackPanelController.Factory backPanelControllerFactory, ViewConfiguration viewConfiguration, @@ -425,7 +418,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mOverviewProxyService = overviewProxyService; mSysUiState = sysUiState; mPluginManager = pluginManager; - mProtoTracer = protoTracer; mNavigationModeController = navigationModeController; mBackPanelControllerFactory = backPanelControllerFactory; mViewConfiguration = viewConfiguration; @@ -557,7 +549,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack */ public void onNavBarAttached() { mIsAttached = true; - mProtoTracer.add(this); mOverviewProxyService.addCallback(mQuickSwitchListener); mSysUiState.addCallback(mSysUiStateCallback); if (mIsTrackpadGestureFeaturesEnabled) { @@ -576,7 +567,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack */ public void onNavBarDetached() { mIsAttached = false; - mProtoTracer.remove(this); mOverviewProxyService.removeCallback(mQuickSwitchListener); mSysUiState.removeCallback(mSysUiStateCallback); mInputManager.unregisterInputDeviceListener(mInputDeviceListener); @@ -1135,8 +1125,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack dispatchToBackAnimation(ev); } } - - mProtoTracer.scheduleFrameUpdate(); } private boolean isButtonPressFromTrackpad(MotionEvent ev) { @@ -1285,14 +1273,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack return topActivity != null && mGestureBlockingActivities.contains(topActivity); } - @Override - public void writeToProto(SystemUiTraceProto proto) { - if (proto.edgeBackGestureHandler == null) { - proto.edgeBackGestureHandler = new EdgeBackGestureHandlerProto(); - } - proto.edgeBackGestureHandler.allowGesture = mAllowGesture; - } - public void setBackAnimation(BackAnimation backAnimation) { mBackAnimation = backAnimation; updateBackAnimationThresholds(); @@ -1319,7 +1299,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private final Handler mHandler; private final Executor mBackgroundExecutor; private final UserTracker mUserTracker; - private final ProtoTracer mProtoTracer; private final NavigationModeController mNavigationModeController; private final BackPanelController.Factory mBackPanelControllerFactory; private final ViewConfiguration mViewConfiguration; @@ -1343,7 +1322,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack @Main Handler handler, @Background Executor backgroundExecutor, UserTracker userTracker, - ProtoTracer protoTracer, NavigationModeController navigationModeController, BackPanelController.Factory backPanelControllerFactory, ViewConfiguration viewConfiguration, @@ -1365,7 +1343,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mHandler = handler; mBackgroundExecutor = backgroundExecutor; mUserTracker = userTracker; - mProtoTracer = protoTracer; mNavigationModeController = navigationModeController; mBackPanelControllerFactory = backPanelControllerFactory; mViewConfiguration = viewConfiguration; @@ -1392,7 +1369,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mHandler, mBackgroundExecutor, mUserTracker, - mProtoTracer, mNavigationModeController, mBackPanelControllerFactory, mViewConfiguration, diff --git a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt index c13476fbbe08..eb1ca66f6ca8 100644 --- a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt @@ -20,6 +20,7 @@ package com.android.systemui.power.domain.interactor import android.os.PowerManager import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.data.repository.PowerRepository import com.android.systemui.statusbar.phone.ScreenOffAnimationController @@ -32,6 +33,7 @@ class PowerInteractor @Inject constructor( private val repository: PowerRepository, + private val keyguardRepository: KeyguardRepository, private val falsingCollector: FalsingCollector, private val screenOffAnimationController: ScreenOffAnimationController, private val statusBarStateController: StatusBarStateController, @@ -54,4 +56,21 @@ constructor( falsingCollector.onScreenOnFromTouch() } } + + /** + * Wakes up the device if the device was dozing or going to sleep in order to display a + * full-screen intent. + */ + fun wakeUpForFullScreenIntent() { + if ( + keyguardRepository.wakefulness.value.isStartingToSleep() || + statusBarStateController.isDozing + ) { + repository.wakeUp(why = FSI_WAKE_WHY, wakeReason = PowerManager.WAKE_REASON_APPLICATION) + } + } + + companion object { + private const val FSI_WAKE_WHY = "full_screen_intent" + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index e7dde6617964..207cc1398279 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -25,11 +25,9 @@ import static android.view.MotionEvent.ACTION_UP; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_AWAKE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING; @@ -173,8 +171,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private boolean mInputFocusTransferStarted; private float mInputFocusTransferStartY; private long mInputFocusTransferStartMillis; - private float mWindowCornerRadius; - private boolean mSupportsRoundedCornersOnWindows; private int mNavBarMode = NAV_BAR_MODE_3BUTTON; @VisibleForTesting @@ -454,8 +450,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis Bundle params = new Bundle(); params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder()); - params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius); - params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows); params.putBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER, mSysuiUnlockAnimationController.asBinder()); mUnfoldTransitionProgressForwarder.ifPresent( @@ -588,9 +582,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis com.android.internal.R.string.config_recentsComponentName)); mQuickStepIntent = new Intent(ACTION_QUICKSTEP) .setPackage(mRecentsComponentName.getPackageName()); - mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext); - mSupportsRoundedCornersOnWindows = ScreenDecorationsUtils - .supportsRoundedCornersOnWindows(mContext.getResources()); mSysUiState = sysUiState; mSysUiState.addCallback(this::notifySystemUiStateFlags); mUiEventLogger = uiEventLogger; @@ -1084,8 +1075,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis pw.print(" mInputFocusTransferStarted="); pw.println(mInputFocusTransferStarted); pw.print(" mInputFocusTransferStartY="); pw.println(mInputFocusTransferStartY); pw.print(" mInputFocusTransferStartMillis="); pw.println(mInputFocusTransferStartMillis); - pw.print(" mWindowCornerRadius="); pw.println(mWindowCornerRadius); - pw.print(" mSupportsRoundedCornersOnWindows="); pw.println(mSupportsRoundedCornersOnWindows); pw.print(" mActiveNavBarRegion="); pw.println(mActiveNavBarRegion); pw.print(" mNavigationBarSurface="); pw.println(mNavigationBarSurface); pw.print(" mNavBarMode="); pw.println(mNavBarMode); diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt index 59f82f034723..285ff74f8b2e 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt @@ -22,6 +22,8 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneContainerNames import com.android.systemui.scene.shared.model.SceneKey @@ -45,6 +47,7 @@ constructor( @Application private val applicationScope: CoroutineScope, private val sceneInteractor: SceneInteractor, private val authenticationInteractor: AuthenticationInteractor, + private val keyguardInteractor: KeyguardInteractor, private val featureFlags: FeatureFlags, ) : CoreStartable { @@ -78,7 +81,7 @@ constructor( when { isUnlocked -> when (currentSceneKey) { - // When the device becomes unlocked in Bouncer, go to the Gone. + // When the device becomes unlocked in Bouncer, go to Gone. is SceneKey.Bouncer -> SceneKey.Gone // When the device becomes unlocked in Lockscreen, go to Gone if // bypass is enabled. @@ -101,15 +104,30 @@ constructor( } } .filterNotNull() - .collect { targetSceneKey -> - sceneInteractor.setCurrentScene( - containerName = CONTAINER_NAME, - scene = SceneModel(targetSceneKey), - ) + .collect { targetSceneKey -> switchToScene(targetSceneKey) } + } + + applicationScope.launch { + keyguardInteractor.wakefulnessModel + .map { it.state == WakefulnessState.ASLEEP } + .distinctUntilChanged() + .collect { isAsleep -> + if (isAsleep) { + // When the device goes to sleep, reset the current scene. + val isUnlocked = authenticationInteractor.isUnlocked.value + switchToScene(if (isUnlocked) SceneKey.Gone else SceneKey.Lockscreen) + } } } } + private fun switchToScene(targetSceneKey: SceneKey) { + sceneInteractor.setCurrentScene( + containerName = CONTAINER_NAME, + scene = SceneModel(targetSceneKey), + ) + } + companion object { private const val CONTAINER_NAME = SceneContainerNames.SYSTEM_UI_DEFAULT } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index d97db3b27c87..ea15035a6c6f 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -1076,6 +1076,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mTapAgainViewController.init(); mShadeHeaderController.init(); + mShadeHeaderController.setShadeCollapseAction( + () -> collapse(/* delayed= */ false , /* speedUpFactor= */ 1.0f)); mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView)); mNotificationPanelUnfoldAnimationController.ifPresent(controller -> controller.setup(mNotificationContainerParent)); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java index ebb98883c679..02f337a8752a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java @@ -18,6 +18,7 @@ package com.android.systemui.shade; import android.view.MotionEvent; +import com.android.systemui.CoreStartable; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.StatusBarState; @@ -31,7 +32,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; * these are coordinated with {@link StatusBarKeyguardViewManager} via * {@link com.android.systemui.keyguard.KeyguardViewMediator} and others. */ -public interface ShadeController { +public interface ShadeController extends CoreStartable { /** Make our window larger and the shade expanded */ void instantExpandShade(); @@ -164,17 +165,14 @@ public interface ShadeController { void onLaunchAnimationEnd(boolean launchIsFullScreen); /** Sets the listener for when the visibility of the shade changes. */ - default void setVisibilityListener(ShadeVisibilityListener listener) {}; + default void setVisibilityListener(ShadeVisibilityListener listener) {} /** */ - default void setNotificationPresenter(NotificationPresenter presenter) {}; + default void setNotificationPresenter(NotificationPresenter presenter) {} /** */ default void setNotificationShadeWindowViewController( - NotificationShadeWindowViewController notificationShadeWindowViewController) {}; - - /** */ - default void setShadeViewController(ShadeViewController shadeViewController) {}; + NotificationShadeWindowViewController notificationShadeWindowViewController) {} /** Listens for shade visibility changes. */ interface ShadeVisibilityListener { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt index 4d0500786ca3..5f95bcae030e 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt @@ -1,3 +1,19 @@ +/* + * 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.systemui.shade import android.view.MotionEvent @@ -7,6 +23,7 @@ import javax.inject.Inject /** Empty implementation of ShadeController for variants of Android without shades. */ @SysUISingleton open class ShadeControllerEmptyImpl @Inject constructor() : ShadeController { + override fun start() {} override fun instantExpandShade() {} override fun instantCollapseShade() {} override fun animateCollapseShade( diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java index af74a8d7dca1..22c638177a48 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java @@ -63,6 +63,7 @@ public final class ShadeControllerImpl implements ShadeController { private final StatusBarWindowController mStatusBarWindowController; private final DeviceProvisionedController mDeviceProvisionedController; + private final Lazy<ShadeViewController> mShadeViewControllerLazy; private final Lazy<AssistManager> mAssistManagerLazy; private final Lazy<NotificationGutsManager> mGutsManager; @@ -70,8 +71,6 @@ public final class ShadeControllerImpl implements ShadeController { private boolean mExpandedVisible; - // TODO(b/237661616): Rename this variable to mShadeViewController. - private ShadeViewController mNotificationPanelViewController; private NotificationPresenter mPresenter; private NotificationShadeWindowViewController mNotificationShadeWindowViewController; private ShadeVisibilityListener mShadeVisibilityListener; @@ -87,11 +86,13 @@ public final class ShadeControllerImpl implements ShadeController { DeviceProvisionedController deviceProvisionedController, NotificationShadeWindowController notificationShadeWindowController, WindowManager windowManager, + Lazy<ShadeViewController> shadeViewControllerLazy, Lazy<AssistManager> assistManagerLazy, Lazy<NotificationGutsManager> gutsManager ) { mCommandQueue = commandQueue; mMainExecutor = mainExecutor; + mShadeViewControllerLazy = shadeViewControllerLazy; mStatusBarStateController = statusBarStateController; mStatusBarWindowController = statusBarWindowController; mDeviceProvisionedController = deviceProvisionedController; @@ -107,7 +108,7 @@ public final class ShadeControllerImpl implements ShadeController { public void instantExpandShade() { // Make our window larger and the panel expanded. makeExpandedVisible(true /* force */); - mNotificationPanelViewController.expand(false /* animate */); + getShadeViewController().expand(false /* animate */); mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */); } @@ -123,13 +124,13 @@ public final class ShadeControllerImpl implements ShadeController { "animateCollapse(): mExpandedVisible=" + mExpandedVisible + "flags=" + flags); } if (getNotificationShadeWindowView() != null - && mNotificationPanelViewController.canBeCollapsed() + && getShadeViewController().canBeCollapsed() && (flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) { // release focus immediately to kick off focus change transition mNotificationShadeWindowController.setNotificationShadeFocusable(false); mNotificationShadeWindowViewController.cancelExpandHelper(); - mNotificationPanelViewController.collapse(true, delayed, speedUpFactor); + getShadeViewController().collapse(true, delayed, speedUpFactor); } } @@ -138,7 +139,7 @@ public final class ShadeControllerImpl implements ShadeController { if (!mCommandQueue.panelsEnabled()) { return; } - mNotificationPanelViewController.expandToNotifications(); + getShadeViewController().expandToNotifications(); } @Override @@ -149,12 +150,12 @@ public final class ShadeControllerImpl implements ShadeController { // Settings are not available in setup if (!mDeviceProvisionedController.isCurrentUserSetup()) return; - mNotificationPanelViewController.expandToQs(); + getShadeViewController().expandToQs(); } @Override public boolean closeShadeIfOpen() { - if (!mNotificationPanelViewController.isFullyCollapsed()) { + if (!getShadeViewController().isFullyCollapsed()) { mCommandQueue.animateCollapsePanels( CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); notifyVisibilityChanged(false); @@ -170,12 +171,12 @@ public final class ShadeControllerImpl implements ShadeController { @Override public boolean isShadeFullyOpen() { - return mNotificationPanelViewController.isShadeFullyExpanded(); + return getShadeViewController().isShadeFullyExpanded(); } @Override public boolean isExpandingOrCollapsing() { - return mNotificationPanelViewController.isExpandingOrCollapsing(); + return getShadeViewController().isExpandingOrCollapsing(); } @Override public void postAnimateCollapseShade() { @@ -194,13 +195,13 @@ public final class ShadeControllerImpl implements ShadeController { @Override public void postOnShadeExpanded(Runnable executable) { - mNotificationPanelViewController.addOnGlobalLayoutListener( + getShadeViewController().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (getNotificationShadeWindowView().isVisibleToUser()) { - mNotificationPanelViewController.removeOnGlobalLayoutListener(this); - mNotificationPanelViewController.postToView(executable); + getShadeViewController().removeOnGlobalLayoutListener(this); + getShadeViewController().postToView(executable); } } }); @@ -224,7 +225,7 @@ public final class ShadeControllerImpl implements ShadeController { @Override public boolean collapseShade() { - if (!mNotificationPanelViewController.isFullyCollapsed()) { + if (!getShadeViewController().isFullyCollapsed()) { // close the shade if it was open animateCollapseShadeForcedDelayed(); notifyVisibilityChanged(false); @@ -252,10 +253,10 @@ public final class ShadeControllerImpl implements ShadeController { @Override public void cancelExpansionAndCollapseShade() { - if (mNotificationPanelViewController.isTracking()) { + if (getShadeViewController().isTracking()) { mNotificationShadeWindowViewController.cancelCurrentTouch(); } - if (mNotificationPanelViewController.isPanelExpanded() + if (getShadeViewController().isPanelExpanded() && mStatusBarStateController.getState() == StatusBarState.SHADE) { animateCollapseShade(); } @@ -311,7 +312,7 @@ public final class ShadeControllerImpl implements ShadeController { @Override public void instantCollapseShade() { - mNotificationPanelViewController.instantCollapse(); + getShadeViewController().instantCollapse(); runPostCollapseRunnables(); } @@ -342,7 +343,7 @@ public final class ShadeControllerImpl implements ShadeController { } // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868) - mNotificationPanelViewController.collapse(false, false, 1.0f); + getShadeViewController().collapse(false, false, 1.0f); mExpandedVisible = false; notifyVisibilityChanged(false); @@ -364,7 +365,7 @@ public final class ShadeControllerImpl implements ShadeController { notifyExpandedVisibleChanged(false); mCommandQueue.recomputeDisableFlags( mDisplayId, - mNotificationPanelViewController.shouldHideStatusBarIconsWhenExpanded()); + getShadeViewController().shouldHideStatusBarIconsWhenExpanded()); // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in // the bouncer appear animation. @@ -406,11 +407,14 @@ public final class ShadeControllerImpl implements ShadeController { return mNotificationShadeWindowViewController.getView(); } + private ShadeViewController getShadeViewController() { + return mShadeViewControllerLazy.get(); + } + @Override - public void setShadeViewController(ShadeViewController shadeViewController) { - mNotificationPanelViewController = shadeViewController; - mNotificationPanelViewController.setTrackingStartedListener(this::runPostCollapseRunnables); - mNotificationPanelViewController.setOpenCloseListener( + public void start() { + getShadeViewController().setTrackingStartedListener(this::runPostCollapseRunnables); + getShadeViewController().setOpenCloseListener( new OpenCloseListener() { @Override public void onClosingFinished() { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt index 411f91f0cb07..8b89ff49f418 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt @@ -122,6 +122,8 @@ constructor( } } + var shadeCollapseAction: Runnable? = null + private lateinit var iconManager: StatusBarIconController.TintedIconManager private lateinit var carrierIconSlots: List<String> private lateinit var mShadeCarrierGroupController: ShadeCarrierGroupController @@ -469,9 +471,11 @@ constructor( if (largeScreenActive) { logInstantEvent("Large screen constraints set") header.setTransition(LARGE_SCREEN_HEADER_TRANSITION_ID) + systemIcons.setOnClickListener { shadeCollapseAction?.run() } } else { logInstantEvent("Small screen constraints set") header.setTransition(HEADER_TRANSITION_ID) + systemIcons.setOnClickListener(null) } header.jumpToState(header.startState) updatePosition() diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt index 8ae9e5e1fb8c..8ec8d115de78 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt @@ -50,6 +50,7 @@ import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfC import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout import com.android.systemui.statusbar.phone.KeyguardBottomAreaView +import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.phone.StatusIconContainer import com.android.systemui.statusbar.phone.TapAgainView import com.android.systemui.statusbar.policy.BatteryController @@ -64,7 +65,7 @@ import javax.inject.Named import javax.inject.Provider /** Module for classes related to the notification shade. */ -@Module +@Module(includes = [StartShadeModule::class]) abstract class ShadeModule { @Binds @@ -281,17 +282,16 @@ abstract class ShadeModule { tunerService: TunerService, @Main mainHandler: Handler, contentResolver: ContentResolver, - featureFlags: FeatureFlags, batteryController: BatteryController, ): BatteryMeterViewController { return BatteryMeterViewController( batteryMeterView, + StatusBarLocation.QS, userTracker, configurationController, tunerService, mainHandler, contentResolver, - featureFlags, batteryController, ) } diff --git a/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt new file mode 100644 index 000000000000..c50693c30533 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt @@ -0,0 +1,31 @@ +/* + * 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.systemui.shade + +import com.android.systemui.CoreStartable +import dagger.Binds +import dagger.Module +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap + +@Module +internal abstract class StartShadeModule { + @Binds + @IntoMap + @ClassKey(ShadeController::class) + abstract fun bind(shadeController: ShadeController): CoreStartable +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt index ce730baeed0d..5d06f8d083d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt @@ -21,8 +21,8 @@ import android.widget.FrameLayout /** * A temporary base class that's shared between our old status bar connectivity view implementations - * ([StatusBarWifiView], [StatusBarMobileView]) and our new status bar implementations ( - * [ModernStatusBarWifiView], [ModernStatusBarMobileView]). + * ([StatusBarMobileView]) and our new status bar implementations ([ModernStatusBarWifiView], + * [ModernStatusBarMobileView]). * * Once our refactor is over, we should be able to delete this go-between class and the old view * class. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index a532195c5b9f..92df78bac17f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -73,7 +73,6 @@ import com.android.systemui.settings.DisplayTracker; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.commandline.CommandRegistry; import com.android.systemui.statusbar.policy.CallbackController; -import com.android.systemui.tracing.ProtoTracer; import java.io.FileDescriptor; import java.io.FileOutputStream; @@ -190,7 +189,6 @@ public class CommandQueue extends IStatusBar.Stub implements */ private int mLastUpdatedImeDisplayId = INVALID_DISPLAY; private final DisplayTracker mDisplayTracker; - private ProtoTracer mProtoTracer; private final @Nullable CommandRegistry mRegistry; private final @Nullable DumpHandler mDumpHandler; @@ -504,18 +502,16 @@ public class CommandQueue extends IStatusBar.Stub implements @VisibleForTesting public CommandQueue(Context context, DisplayTracker displayTracker) { - this(context, displayTracker, null, null, null); + this(context, displayTracker, null, null); } public CommandQueue( Context context, DisplayTracker displayTracker, - ProtoTracer protoTracer, CommandRegistry registry, DumpHandler dumpHandler ) { mDisplayTracker = displayTracker; - mProtoTracer = protoTracer; mRegistry = registry; mDumpHandler = dumpHandler; mDisplayTracker.addDisplayChangeCallback(new DisplayTracker.Callback() { @@ -1160,9 +1156,6 @@ public class CommandQueue extends IStatusBar.Stub implements @Override public void startTracing() { synchronized (mLock) { - if (mProtoTracer != null) { - mProtoTracer.start(); - } mHandler.obtainMessage(MSG_TRACING_STATE_CHANGED, true).sendToTarget(); } } @@ -1170,9 +1163,6 @@ public class CommandQueue extends IStatusBar.Stub implements @Override public void stopTracing() { synchronized (mLock) { - if (mProtoTracer != null) { - mProtoTracer.stop(); - } mHandler.obtainMessage(MSG_TRACING_STATE_CHANGED, false).sendToTarget(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java deleted file mode 100644 index 8d7214d6bd75..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar; - -import static com.android.systemui.plugins.DarkIconDispatcher.getTint; -import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT; -import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN; -import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; - -import com.android.systemui.R; -import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; - -import java.util.ArrayList; - -/** - * Start small: StatusBarWifiView will be able to layout from a WifiIconState - */ -public class StatusBarWifiView extends BaseStatusBarFrameLayout implements DarkReceiver { - private static final String TAG = "StatusBarWifiView"; - - /// Used to show etc dots - private StatusBarIconView mDotView; - /// Contains the main icon layout - private LinearLayout mWifiGroup; - private ImageView mWifiIcon; - private ImageView mIn; - private ImageView mOut; - private View mInoutContainer; - private View mSignalSpacer; - private View mAirplaneSpacer; - private WifiIconState mState; - private String mSlot; - @StatusBarIconView.VisibleState - private int mVisibleState = STATE_HIDDEN; - - public static StatusBarWifiView fromContext(Context context, String slot) { - LayoutInflater inflater = LayoutInflater.from(context); - StatusBarWifiView v = (StatusBarWifiView) inflater.inflate(R.layout.status_bar_wifi_group, null); - v.setSlot(slot); - v.init(); - v.setVisibleState(STATE_ICON); - return v; - } - - public StatusBarWifiView(Context context) { - super(context); - } - - public StatusBarWifiView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public StatusBarWifiView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public void setSlot(String slot) { - mSlot = slot; - } - - @Override - public void setStaticDrawableColor(int color) { - ColorStateList list = ColorStateList.valueOf(color); - mWifiIcon.setImageTintList(list); - mIn.setImageTintList(list); - mOut.setImageTintList(list); - mDotView.setDecorColor(color); - } - - @Override - public void setDecorColor(int color) { - mDotView.setDecorColor(color); - } - - @Override - public String getSlot() { - return mSlot; - } - - @Override - public boolean isIconVisible() { - return mState != null && mState.visible; - } - - @Override - public void setVisibleState(@StatusBarIconView.VisibleState int state, boolean animate) { - if (state == mVisibleState) { - return; - } - mVisibleState = state; - - switch (state) { - case STATE_ICON: - mWifiGroup.setVisibility(View.VISIBLE); - mDotView.setVisibility(View.GONE); - break; - case STATE_DOT: - mWifiGroup.setVisibility(View.GONE); - mDotView.setVisibility(View.VISIBLE); - break; - case STATE_HIDDEN: - default: - mWifiGroup.setVisibility(View.GONE); - mDotView.setVisibility(View.GONE); - break; - } - } - - @Override - @StatusBarIconView.VisibleState - public int getVisibleState() { - return mVisibleState; - } - - @Override - public void getDrawingRect(Rect outRect) { - super.getDrawingRect(outRect); - float translationX = getTranslationX(); - float translationY = getTranslationY(); - outRect.left += translationX; - outRect.right += translationX; - outRect.top += translationY; - outRect.bottom += translationY; - } - - private void init() { - mWifiGroup = findViewById(R.id.wifi_group); - mWifiIcon = findViewById(R.id.wifi_signal); - mIn = findViewById(R.id.wifi_in); - mOut = findViewById(R.id.wifi_out); - mSignalSpacer = findViewById(R.id.wifi_signal_spacer); - mAirplaneSpacer = findViewById(R.id.wifi_airplane_spacer); - mInoutContainer = findViewById(R.id.inout_container); - - initDotView(); - } - - private void initDotView() { - mDotView = new StatusBarIconView(mContext, mSlot, null); - mDotView.setVisibleState(STATE_DOT); - - int width = mContext.getResources().getDimensionPixelSize(R.dimen.status_bar_icon_size_sp); - LayoutParams lp = new LayoutParams(width, width); - lp.gravity = Gravity.CENTER_VERTICAL | Gravity.START; - addView(mDotView, lp); - } - - public void applyWifiState(WifiIconState state) { - boolean requestLayout = false; - - if (state == null) { - requestLayout = getVisibility() != View.GONE; - setVisibility(View.GONE); - mState = null; - } else if (mState == null) { - requestLayout = true; - mState = state.copy(); - initViewState(); - } else if (!mState.equals(state)) { - requestLayout = updateState(state.copy()); - } - - if (requestLayout) { - requestLayout(); - } - } - - private boolean updateState(WifiIconState state) { - setContentDescription(state.contentDescription); - if (mState.resId != state.resId && state.resId >= 0) { - mWifiIcon.setImageDrawable(mContext.getDrawable(state.resId)); - } - - mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE); - mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE); - mInoutContainer.setVisibility( - (state.activityIn || state.activityOut) ? View.VISIBLE : View.GONE); - mAirplaneSpacer.setVisibility(state.airplaneSpacerVisible ? View.VISIBLE : View.GONE); - mSignalSpacer.setVisibility(state.signalSpacerVisible ? View.VISIBLE : View.GONE); - - boolean needsLayout = state.activityIn != mState.activityIn - ||state.activityOut != mState.activityOut; - - if (mState.visible != state.visible) { - needsLayout |= true; - setVisibility(state.visible ? View.VISIBLE : View.GONE); - } - - mState = state; - return needsLayout; - } - - private void initViewState() { - setContentDescription(mState.contentDescription); - if (mState.resId >= 0) { - mWifiIcon.setImageDrawable(mContext.getDrawable(mState.resId)); - } - - mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE); - mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE); - mInoutContainer.setVisibility( - (mState.activityIn || mState.activityOut) ? View.VISIBLE : View.GONE); - mAirplaneSpacer.setVisibility(mState.airplaneSpacerVisible ? View.VISIBLE : View.GONE); - mSignalSpacer.setVisibility(mState.signalSpacerVisible ? View.VISIBLE : View.GONE); - setVisibility(mState.visible ? View.VISIBLE : View.GONE); - } - - @Override - public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) { - int areaTint = getTint(areas, this, tint); - ColorStateList color = ColorStateList.valueOf(areaTint); - mWifiIcon.setImageTintList(color); - mIn.setImageTintList(color); - mOut.setImageTintList(color); - mDotView.setDecorColor(areaTint); - mDotView.setIconColor(areaTint, false); - } - - - @Override - public String toString() { - return "StatusBarWifiView(slot=" + mSlot + " state=" + mState + ")"; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java index 73f181b8c734..9aa28c31cfd8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java @@ -18,10 +18,6 @@ package com.android.systemui.statusbar.connectivity; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN; -import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT; -import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE; -import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import android.annotation.Nullable; @@ -557,10 +553,6 @@ public class NetworkControllerImpl extends BroadcastReceiver mBroadcastDispatcher.unregisterReceiver(this); } - public int getConnectedWifiLevel() { - return mWifiSignalController.getState().level; - } - @Override public AccessPointController getAccessPointController() { return mAccessPoints; @@ -654,14 +646,6 @@ public class NetworkControllerImpl extends BroadcastReceiver return mWifiSignalController.isCarrierMergedWifi(subId); } - boolean hasDefaultNetwork() { - return !mNoDefaultNetwork; - } - - boolean isNonCarrierWifiNetworkAvailable() { - return !mNoNetworksAvailable; - } - boolean isEthernetDefault() { return mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET); } @@ -1242,15 +1226,12 @@ public class NetworkControllerImpl extends BroadcastReceiver } private boolean mDemoInetCondition; - private WifiState mDemoWifiState; @Override public void onDemoModeStarted() { if (DEBUG) Log.d(TAG, "Entering demo mode"); unregisterListeners(); mDemoInetCondition = mInetCondition; - mDemoWifiState = mWifiSignalController.getState(); - mDemoWifiState.ssid = "DemoMode"; } @Override @@ -1300,41 +1281,6 @@ public class NetworkControllerImpl extends BroadcastReceiver controller.updateConnectivity(connected, connected); } } - String wifi = args.getString("wifi"); - if (wifi != null && !mStatusBarPipelineFlags.runNewWifiIconBackend()) { - boolean show = wifi.equals("show"); - String level = args.getString("level"); - if (level != null) { - mDemoWifiState.level = level.equals("null") ? -1 - : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1); - mDemoWifiState.connected = mDemoWifiState.level >= 0; - } - String activity = args.getString("activity"); - if (activity != null) { - switch (activity) { - case "inout": - mWifiSignalController.setActivity(DATA_ACTIVITY_INOUT); - break; - case "in": - mWifiSignalController.setActivity(DATA_ACTIVITY_IN); - break; - case "out": - mWifiSignalController.setActivity(DATA_ACTIVITY_OUT); - break; - default: - mWifiSignalController.setActivity(DATA_ACTIVITY_NONE); - break; - } - } else { - mWifiSignalController.setActivity(DATA_ACTIVITY_NONE); - } - String ssid = args.getString("ssid"); - if (ssid != null) { - mDemoWifiState.ssid = ssid; - } - mDemoWifiState.enabled = show; - mWifiSignalController.notifyListeners(); - } String sims = args.getString("sims"); if (sims != null && !mStatusBarPipelineFlags.useNewMobileIcons()) { int num = MathUtils.constrain(Integer.parseInt(sims), 1, 8); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java index 035fa0454bfc..e5ba3ce1fdae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java @@ -78,7 +78,6 @@ import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.statusbar.window.StatusBarWindowController; -import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.time.SystemClock; @@ -195,11 +194,10 @@ public interface CentralSurfacesDependenciesModule { static CommandQueue provideCommandQueue( Context context, DisplayTracker displayTracker, - ProtoTracer protoTracer, CommandRegistry registry, DumpHandler dumpHandler ) { - return new CommandQueue(context, displayTracker, protoTracer, registry, dumpHandler); + return new CommandQueue(context, displayTracker, registry, dumpHandler); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt index 212f2c215989..1cf9c1e1f299 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt @@ -3,7 +3,10 @@ package com.android.systemui.statusbar.notification import android.util.FloatProperty import android.view.View import androidx.annotation.FloatRange +import com.android.systemui.Dependency import com.android.systemui.R +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.statusbar.notification.stack.AnimationProperties import com.android.systemui.statusbar.notification.stack.StackStateAnimator import kotlin.math.abs @@ -20,6 +23,8 @@ interface Roundable { /** Properties required for a Roundable */ val roundableState: RoundableState + val clipHeight: Int + /** Current top roundness */ @get:FloatRange(from = 0.0, to = 1.0) @JvmDefault @@ -40,12 +45,16 @@ interface Roundable { /** Current top corner in pixel, based on [topRoundness] and [maxRadius] */ @JvmDefault val topCornerRadius: Float - get() = topRoundness * maxRadius + get() = + if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.topCornerRadius + else topRoundness * maxRadius /** Current bottom corner in pixel, based on [bottomRoundness] and [maxRadius] */ @JvmDefault val bottomCornerRadius: Float - get() = bottomRoundness * maxRadius + get() = + if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.bottomCornerRadius + else bottomRoundness * maxRadius /** Get and update the current radii */ @JvmDefault @@ -320,14 +329,20 @@ interface Roundable { * @param roundable Target of the radius animation * @param maxRadius Max corner radius in pixels */ -class RoundableState( +class RoundableState +@JvmOverloads +constructor( internal val targetView: View, private val roundable: Roundable, maxRadius: Float, + private val featureFlags: FeatureFlags = Dependency.get(FeatureFlags::class.java) ) { internal var maxRadius = maxRadius private set + internal val newHeadsUpAnimFlagEnabled + get() = featureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS) + /** Animatable for top roundness */ private val topAnimatable = topAnimatable(roundable) @@ -344,6 +359,41 @@ class RoundableState( internal var bottomRoundness = 0f private set + internal val topCornerRadius: Float + get() { + val height = roundable.clipHeight + val topRadius = topRoundness * maxRadius + val bottomRadius = bottomRoundness * maxRadius + + if (height == 0) { + return 0f + } else if (topRadius + bottomRadius > height) { + // The sum of top and bottom corner radii should be at max the clipped height + val overShoot = topRadius + bottomRadius - height + return topRadius - (overShoot * topRoundness / (topRoundness + bottomRoundness)) + } + + return topRadius + } + + internal val bottomCornerRadius: Float + get() { + val height = roundable.clipHeight + val topRadius = topRoundness * maxRadius + val bottomRadius = bottomRoundness * maxRadius + + if (height == 0) { + return 0f + } else if (topRadius + bottomRadius > height) { + // The sum of top and bottom corner radii should be at max the clipped height + val overShoot = topRadius + bottomRadius - height + return bottomRadius - + (overShoot * bottomRoundness / (topRoundness + bottomRoundness)) + } + + return bottomRadius + } + /** Last requested top roundness associated by [SourceType] */ internal val topRoundnessMap = mutableMapOf<SourceType, Float>() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 27510d47b5ab..908c11a1d076 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -566,12 +566,20 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView @Override public float getTopCornerRadius() { + if (isNewHeadsUpAnimFlagEnabled()) { + return super.getTopCornerRadius(); + } + float fraction = getInterpolatedAppearAnimationFraction(); return MathUtils.lerp(0, super.getTopCornerRadius(), fraction); } @Override public float getBottomCornerRadius() { + if (isNewHeadsUpAnimFlagEnabled()) { + return super.getBottomCornerRadius(); + } + float fraction = getInterpolatedAppearAnimationFraction(); return MathUtils.lerp(0, super.getBottomCornerRadius(), fraction); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java index 9aa50e989ff1..7f23c1b89b51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java @@ -28,7 +28,10 @@ import android.util.IndentingPrintWriter; import android.view.View; import android.view.ViewOutlineProvider; +import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.statusbar.notification.RoundableState; import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer; import com.android.systemui.util.DumpUtilsKt; @@ -47,12 +50,14 @@ public abstract class ExpandableOutlineView extends ExpandableView { private float mOutlineAlpha = -1f; private boolean mAlwaysRoundBothCorners; private Path mTmpPath = new Path(); + private final FeatureFlags mFeatureFlags; /** * {@code false} if the children views of the {@link ExpandableOutlineView} are translated when * it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself. */ protected boolean mDismissUsingRowTranslationX = true; + private float[] mTmpCornerRadii = new float[8]; private final ViewOutlineProvider mProvider = new ViewOutlineProvider() { @@ -81,6 +86,15 @@ public abstract class ExpandableOutlineView extends ExpandableView { return mRoundableState; } + @Override + public int getClipHeight() { + if (mCustomOutline) { + return mOutlineRect.height(); + } + + return super.getClipHeight(); + } + protected Path getClipPath(boolean ignoreTranslation) { int left; int top; @@ -112,7 +126,7 @@ public abstract class ExpandableOutlineView extends ExpandableView { return EMPTY_PATH; } float bottomRadius = mAlwaysRoundBothCorners ? getMaxRadius() : getBottomCornerRadius(); - if (topRadius + bottomRadius > height) { + if (!isNewHeadsUpAnimFlagEnabled() && (topRadius + bottomRadius > height)) { float overShoot = topRadius + bottomRadius - height; float currentTopRoundness = getTopRoundness(); float currentBottomRoundness = getBottomRoundness(); @@ -153,6 +167,7 @@ public abstract class ExpandableOutlineView extends ExpandableView { super(context, attrs); setOutlineProvider(mProvider); initDimens(); + mFeatureFlags = Dependency.get(FeatureFlags.class); } @Override @@ -360,4 +375,9 @@ public abstract class ExpandableOutlineView extends ExpandableView { } }); } + + // TODO(b/290365128) replace with ViewRefactorFlag + protected boolean isNewHeadsUpAnimFlagEnabled() { + return mFeatureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java index f98624409e56..c4c116ba0613 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java @@ -93,6 +93,12 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro return mRoundableState; } + @Override + public int getClipHeight() { + int clipHeight = Math.max(mActualHeight - mClipTopAmount - mClipBottomAmount, 0); + return Math.max(clipHeight, mMinimumHeightForClipping); + } + private void initDimens() { mContentShift = getResources().getDimensionPixelSize( R.dimen.shelf_transform_content_shift); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index ef5e86f06ef0..87205e2df9c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -120,6 +120,11 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple } @Override + public int getClipHeight() { + return mView.getHeight(); + } + + @Override public void applyRoundnessAndInvalidate() { if (mRoundnessChangedListener != null) { // We cannot apply the rounded corner to this View, so our parents (in drawChild()) will diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt index 04308b47abc9..a8d8a8e03ed5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt @@ -30,8 +30,8 @@ import com.android.systemui.statusbar.notification.row.ExpandableView */ class MediaContainerView(context: Context, attrs: AttributeSet?) : ExpandableView(context, attrs) { + override var clipHeight = 0 var cornerRadius = 0f - var clipHeight = 0 var clipRect = RectF() var clipPath = Path() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index dad806418f2d..626f851d9d11 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -192,6 +192,11 @@ public class NotificationChildrenContainer extends ViewGroup } @Override + public int getClipHeight() { + return Math.max(mActualHeight - mClipBottomAmount, 0); + } + + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = Math.min(mAttachedChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt index 730ef57f1972..26b51a95acad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt @@ -37,6 +37,7 @@ import com.android.systemui.animation.DelegateLaunchAnimatorController import com.android.systemui.assist.AssistManager import com.android.systemui.camera.CameraIntents.Companion.isInsecureCameraIntent import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.DisplayId import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle @@ -72,6 +73,7 @@ constructor( private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>, private val activityLaunchAnimator: ActivityLaunchAnimator, private val context: Context, + @DisplayId private val displayId: Int, private val lockScreenUserManager: NotificationLockscreenUserManager, private val statusBarWindowController: StatusBarWindowController, private val wakefulnessLifecycle: WakefulnessLifecycle, @@ -471,9 +473,7 @@ constructor( intent.getPackage() ) { adapter: RemoteAnimationAdapter? -> val options = - ActivityOptions( - CentralSurfaces.getActivityOptions(centralSurfaces!!.displayId, adapter) - ) + ActivityOptions(CentralSurfaces.getActivityOptions(displayId, adapter)) // We know that the intent of the caller is to dismiss the keyguard and // this runnable is called right after the keyguard is solved, so we tell @@ -596,7 +596,7 @@ constructor( val options = ActivityOptions( CentralSurfaces.getActivityOptions( - centralSurfaces!!.displayId, + displayId, animationAdapter ) ) @@ -762,7 +762,7 @@ constructor( TaskStackBuilder.create(context) .addNextIntent(intent) .startActivities( - CentralSurfaces.getActivityOptions(centralSurfaces!!.displayId, adapter), + CentralSurfaces.getActivityOptions(displayId, adapter), userHandle ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index 478baf2f58d6..2b9c3d33e9b8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -355,15 +355,11 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { void updateNotificationPanelTouchState(); - int getDisplayId(); - int getRotation(); @VisibleForTesting void setBarStateForTest(int state); - void wakeUpForFullScreenIntent(); - void showTransientUnchecked(); void clearTransient(); 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 8902a186c43a..62e98f934888 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -1613,7 +1613,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // (Right now, there's a circular dependency.) mNotificationShadeWindowController.setWindowRootView(windowRootView); mNotificationShadeWindowViewController.setupExpandedStatusBar(); - mShadeController.setShadeViewController(mShadeSurface); mShadeController.setNotificationShadeWindowViewController( mNotificationShadeWindowViewController); mBackActionInteractor.setup(mQsController, mShadeSurface); @@ -1773,7 +1772,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { try { EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION, sbn.getKey()); - wakeUpForFullScreenIntent(); + mPowerInteractor.wakeUpForFullScreenIntent(); ActivityOptions opts = ActivityOptions.makeBasic(); opts.setPendingIntentBackgroundActivityStartMode( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); @@ -1786,16 +1785,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mHeadsUpManager.releaseAllImmediately(); } - @Override - public void wakeUpForFullScreenIntent() { - if (isGoingToSleep() || mDozing) { - mPowerManager.wakeUp( - SystemClock.uptimeMillis(), - PowerManager.WAKE_REASON_APPLICATION, - "com.android.systemui:full_screen_intent"); - } - } - /** * Called when another window is about to transfer it's input focus. */ @@ -2108,11 +2097,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } @Override - public int getDisplayId() { - return mDisplayId; - } - - @Override public void readyForKeyguardDone() { mStatusBarKeyguardViewManager.readyForKeyguardDone(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java index 39b5b5a4cef8..8e9f382d8c00 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java @@ -35,11 +35,9 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarMobileView; -import com.android.systemui.statusbar.StatusBarWifiView; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger; import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel; @@ -58,7 +56,6 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da private final ArrayList<ModernStatusBarMobileView> mModernMobileViews = new ArrayList<>(); private final int mIconSize; - private StatusBarWifiView mWifiView; private ModernStatusBarWifiView mModernWifiView; private boolean mDemoMode; private int mColor; @@ -238,36 +235,6 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da addView(v, 0, createLayoutParams()); } - public void addDemoWifiView(WifiIconState state) { - Log.d(TAG, "addDemoWifiView: "); - StatusBarWifiView view = StatusBarWifiView.fromContext(mContext, state.slot); - - int viewIndex = getChildCount(); - // If we have mobile views, put wifi before them - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (child instanceof StatusBarMobileView - || child instanceof ModernStatusBarMobileView) { - viewIndex = i; - break; - } - } - - mWifiView = view; - mWifiView.applyWifiState(state); - mWifiView.setStaticDrawableColor(mColor); - addView(view, viewIndex, createLayoutParams()); - } - - public void updateWifiState(WifiIconState state) { - Log.d(TAG, "updateWifiState: "); - if (mWifiView == null) { - addDemoWifiView(state); - } else { - mWifiView.applyWifiState(state); - } - } - /** * Add a new mobile icon view */ @@ -352,10 +319,7 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da public void onRemoveIcon(StatusIconDisplayable view) { if (view.getSlot().equals("wifi")) { - if (view instanceof StatusBarWifiView) { - removeView(mWifiView); - mWifiView = null; - } else if (view instanceof ModernStatusBarWifiView) { + if (view instanceof ModernStatusBarWifiView) { Log.d(TAG, "onRemoveIcon: removing modern wifi view"); removeView(mModernWifiView); mModernWifiView = null; @@ -409,9 +373,6 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) { setColor(DarkIconDispatcher.getTint(areas, mStatusIcons, tint)); - if (mWifiView != null) { - mWifiView.onDarkChanged(areas, darkIntensity, tint); - } if (mModernWifiView != null) { mModernWifiView.onDarkChanged(areas, darkIntensity, tint); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index 59583ddf3e34..42b0a4f13e0d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE_NEW; -import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI_NEW; import android.annotation.Nullable; @@ -43,12 +42,10 @@ import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.BaseStatusBarFrameLayout; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarMobileView; -import com.android.systemui.statusbar.StatusBarWifiView; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter; import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconsBinder; @@ -97,16 +94,9 @@ public interface StatusBarIconController { */ void setIcon(String slot, int resourceId, CharSequence contentDescription); - /** */ - void setWifiIcon(String slot, WifiIconState state); - /** * Sets up a wifi icon using the new data pipeline. No effect if the wifi icon has already been * set up (inflated and added to the view hierarchy). - * - * This method completely replaces {@link #setWifiIcon} with the information from the new wifi - * data pipeline. Icons will automatically keep their state up to date, so we don't have to - * worry about funneling state objects through anymore. */ void setNewWifiIcon(); @@ -408,13 +398,7 @@ public interface StatusBarIconController { mMobileIconsViewModel = null; } - if (statusBarPipelineFlags.runNewWifiIconBackend()) { - // This starts the flow for the new pipeline, and will notify us of changes if - // {@link StatusBarPipelineFlags#useNewWifiIcon} is also true. - mWifiViewModel = wifiUiAdapter.bindGroup(mGroup, mLocation); - } else { - mWifiViewModel = null; - } + mWifiViewModel = wifiUiAdapter.bindGroup(mGroup, mLocation); } public boolean isDemoable() { @@ -462,9 +446,6 @@ public interface StatusBarIconController { case TYPE_ICON: return addIcon(index, slot, blocked, holder.getIcon()); - case TYPE_WIFI: - return addWifiIcon(index, slot, holder.getWifiState()); - case TYPE_WIFI_NEW: return addNewWifiIcon(index, slot); @@ -487,29 +468,7 @@ public interface StatusBarIconController { return view; } - @VisibleForTesting - protected StatusIconDisplayable addWifiIcon(int index, String slot, WifiIconState state) { - if (mStatusBarPipelineFlags.useNewWifiIcon()) { - throw new IllegalStateException("Attempting to add a wifi icon while the new " - + "icons are enabled is not supported"); - } - - final StatusBarWifiView view = onCreateStatusBarWifiView(slot); - view.applyWifiState(state); - mGroup.addView(view, index, onCreateLayoutParams()); - - if (mIsInDemoMode) { - mDemoStatusIcons.addDemoWifiView(state); - } - return view; - } - protected StatusIconDisplayable addNewWifiIcon(int index, String slot) { - if (!mStatusBarPipelineFlags.useNewWifiIcon()) { - throw new IllegalStateException("Attempting to add a wifi icon using the new" - + "pipeline, but the enabled flag is false."); - } - ModernStatusBarWifiView view = onCreateModernStatusBarWifiView(slot); mGroup.addView(view, index, onCreateLayoutParams()); @@ -573,11 +532,6 @@ public interface StatusBarIconController { return new StatusBarIconView(mContext, slot, null, blocked); } - private StatusBarWifiView onCreateStatusBarWifiView(String slot) { - StatusBarWifiView view = StatusBarWifiView.fromContext(mContext, slot); - return view; - } - private ModernStatusBarWifiView onCreateModernStatusBarWifiView(String slot) { return ModernStatusBarWifiView.constructAndBind(mContext, slot, mWifiViewModel); } @@ -640,9 +594,6 @@ public interface StatusBarIconController { case TYPE_ICON: onSetIcon(viewIndex, holder.getIcon()); return; - case TYPE_WIFI: - onSetWifiIcon(viewIndex, holder.getWifiState()); - return; case TYPE_MOBILE: onSetMobileIcon(viewIndex, holder.getMobileState()); return; @@ -655,23 +606,6 @@ public interface StatusBarIconController { } } - public void onSetWifiIcon(int viewIndex, WifiIconState state) { - View view = mGroup.getChildAt(viewIndex); - if (view instanceof StatusBarWifiView) { - ((StatusBarWifiView) view).applyWifiState(state); - } else if (view instanceof ModernStatusBarWifiView) { - // ModernStatusBarWifiView will automatically apply state based on its callbacks, so - // we don't need to call applyWifiState. - } else { - throw new IllegalStateException("View at " + viewIndex + " must be of type " - + "StatusBarWifiView or ModernStatusBarWifiView"); - } - - if (mIsInDemoMode) { - mDemoStatusIcons.updateWifiState(state); - } - } - public void onSetMobileIcon(int viewIndex, MobileIconState state) { View view = mGroup.getChildAt(viewIndex); if (view instanceof StatusBarMobileView) { @@ -703,9 +637,7 @@ public interface StatusBarIconController { mIsInDemoMode = true; if (mDemoStatusIcons == null) { mDemoStatusIcons = createDemoStatusIcons(); - if (mStatusBarPipelineFlags.useNewWifiIcon()) { - mDemoStatusIcons.addModernWifiView(mWifiViewModel); - } + mDemoStatusIcons.addModernWifiView(mWifiViewModel); } mDemoStatusIcons.onDemoModeStarted(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index 80d5651a65dc..d1a02d6cd611 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -40,7 +40,6 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; @@ -202,35 +201,7 @@ public class StatusBarIconControllerImpl implements Tunable, } @Override - public void setWifiIcon(String slot, WifiIconState state) { - if (mStatusBarPipelineFlags.useNewWifiIcon()) { - Log.d(TAG, "ignoring old pipeline callback because the new wifi icon is enabled"); - return; - } - - if (state == null) { - removeIcon(slot, 0); - return; - } - - StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, 0); - if (holder == null) { - holder = StatusBarIconHolder.fromWifiIconState(state); - setIcon(slot, holder); - } else { - holder.setWifiState(state); - handleSet(slot, holder); - } - } - - - @Override public void setNewWifiIcon() { - if (!mStatusBarPipelineFlags.useNewWifiIcon()) { - Log.d(TAG, "ignoring new pipeline callback because the new wifi icon is disabled"); - return; - } - String slot = mContext.getString(com.android.internal.R.string.status_bar_wifi); StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, /* tag= */ 0); if (holder == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java index 833cb93f62e9..01fd247f54bf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java @@ -25,7 +25,6 @@ import android.os.UserHandle; import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel; import java.lang.annotation.Retention; @@ -36,7 +35,6 @@ import java.lang.annotation.RetentionPolicy; */ public class StatusBarIconHolder { public static final int TYPE_ICON = 0; - public static final int TYPE_WIFI = 1; public static final int TYPE_MOBILE = 2; /** * TODO (b/249790733): address this once the new pipeline is in place @@ -65,7 +63,6 @@ public class StatusBarIconHolder { @IntDef({ TYPE_ICON, - TYPE_WIFI, TYPE_MOBILE, TYPE_MOBILE_NEW, TYPE_WIFI_NEW @@ -74,7 +71,6 @@ public class StatusBarIconHolder { @interface IconType {} private StatusBarIcon mIcon; - private WifiIconState mWifiState; private MobileIconState mMobileState; private @IconType int mType = TYPE_ICON; private int mTag = 0; @@ -83,7 +79,6 @@ public class StatusBarIconHolder { public static String getTypeString(@IconType int type) { switch(type) { case TYPE_ICON: return "ICON"; - case TYPE_WIFI: return "WIFI_OLD"; case TYPE_MOBILE: return "MOBILE_OLD"; case TYPE_MOBILE_NEW: return "MOBILE_NEW"; case TYPE_WIFI_NEW: return "WIFI_NEW"; @@ -101,25 +96,6 @@ public class StatusBarIconHolder { return wrapper; } - /** */ - public static StatusBarIconHolder fromResId( - Context context, - int resId, - CharSequence contentDescription) { - StatusBarIconHolder holder = new StatusBarIconHolder(); - holder.mIcon = new StatusBarIcon(UserHandle.SYSTEM, context.getPackageName(), - Icon.createWithResource( context, resId), 0, 0, contentDescription); - return holder; - } - - /** */ - public static StatusBarIconHolder fromWifiIconState(WifiIconState state) { - StatusBarIconHolder holder = new StatusBarIconHolder(); - holder.mWifiState = state; - holder.mType = TYPE_WIFI; - return holder; - } - /** Creates a new holder with for the new wifi icon. */ public static StatusBarIconHolder forNewWifiIcon() { StatusBarIconHolder holder = new StatusBarIconHolder(); @@ -178,15 +154,6 @@ public class StatusBarIconHolder { } @Nullable - public WifiIconState getWifiState() { - return mWifiState; - } - - public void setWifiState(WifiIconState state) { - mWifiState = state; - } - - @Nullable public MobileIconState getMobileState() { return mMobileState; } @@ -199,8 +166,6 @@ public class StatusBarIconHolder { switch (mType) { case TYPE_ICON: return mIcon.visible; - case TYPE_WIFI: - return mWifiState.visible; case TYPE_MOBILE: return mMobileState.visible; case TYPE_MOBILE_NEW: @@ -223,10 +188,6 @@ public class StatusBarIconHolder { mIcon.visible = visible; break; - case TYPE_WIFI: - mWifiState.visible = visible; - break; - case TYPE_MOBILE: mMobileState.visible = visible; break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index f79a08173bf2..ec0c00e26c2f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -54,8 +54,10 @@ import com.android.systemui.ActivityIntentHelper; import com.android.systemui.EventLogTags; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.assist.AssistManager; +import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.ShadeController; import com.android.systemui.shade.ShadeViewController; @@ -94,6 +96,7 @@ import javax.inject.Inject; class StatusBarNotificationActivityStarter implements NotificationActivityStarter { private final Context mContext; + private final int mDisplayId; private final Handler mMainThreadHandler; private final Executor mUiBgExecutor; @@ -120,12 +123,12 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte private final MetricsLogger mMetricsLogger; private final StatusBarNotificationActivityStarterLogger mLogger; - private final CentralSurfaces mCentralSurfaces; private final NotificationPresenter mPresenter; private final ShadeViewController mShadeViewController; private final NotificationShadeWindowController mNotificationShadeWindowController; private final ActivityLaunchAnimator mActivityLaunchAnimator; private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider; + private final PowerInteractor mPowerInteractor; private final UserTracker mUserTracker; private final OnUserInteractionCallback mOnUserInteractionCallback; @@ -134,6 +137,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte @Inject StatusBarNotificationActivityStarter( Context context, + @DisplayId int displayId, Handler mainThreadHandler, Executor uiBgExecutor, NotificationVisibilityProvider visibilityProvider, @@ -156,16 +160,17 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte MetricsLogger metricsLogger, StatusBarNotificationActivityStarterLogger logger, OnUserInteractionCallback onUserInteractionCallback, - CentralSurfaces centralSurfaces, NotificationPresenter presenter, ShadeViewController shadeViewController, NotificationShadeWindowController notificationShadeWindowController, ActivityLaunchAnimator activityLaunchAnimator, NotificationLaunchAnimatorControllerProvider notificationAnimationProvider, LaunchFullScreenIntentProvider launchFullScreenIntentProvider, + PowerInteractor powerInteractor, FeatureFlags featureFlags, UserTracker userTracker) { mContext = context; + mDisplayId = displayId; mMainThreadHandler = mainThreadHandler; mUiBgExecutor = uiBgExecutor; mVisibilityProvider = visibilityProvider; @@ -190,12 +195,11 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mMetricsLogger = metricsLogger; mLogger = logger; mOnUserInteractionCallback = onUserInteractionCallback; - // TODO: use KeyguardStateController#isOccluded to remove this dependency - mCentralSurfaces = centralSurfaces; mPresenter = presenter; mShadeViewController = shadeViewController; mActivityLaunchAnimator = activityLaunchAnimator; mNotificationAnimationProvider = notificationAnimationProvider; + mPowerInteractor = powerInteractor; mUserTracker = userTracker; launchFullScreenIntentProvider.registerListener(entry -> launchFullScreenIntent(entry)); @@ -280,7 +284,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mShadeController.addPostCollapseAction(runnable); mShadeController.collapseShade(true /* animate */); } else if (mKeyguardStateController.isShowing() - && mCentralSurfaces.isOccluded()) { + && mKeyguardStateController.isOccluded()) { mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable); mShadeController.collapseShade(); } else { @@ -452,11 +456,11 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte long eventTime = row.getAndResetLastActionUpTime(); Bundle options = eventTime > 0 ? getActivityOptions( - mCentralSurfaces.getDisplayId(), + mDisplayId, adapter, mKeyguardStateController.isShowing(), eventTime) - : getActivityOptions(mCentralSurfaces.getDisplayId(), adapter); + : getActivityOptions(mDisplayId, adapter); int result = intent.sendAndReturnResult(mContext, 0, fillInIntent, null, null, null, options); mLogger.logSendPendingIntent(entry, intent, result); @@ -491,7 +495,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte (adapter) -> TaskStackBuilder.create(mContext) .addNextIntentWithParentStack(intent) .startActivities(getActivityOptions( - mCentralSurfaces.getDisplayId(), + mDisplayId, adapter), new UserHandle(UserHandle.getUserId(appUid)))); }); @@ -539,7 +543,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate, intent.getPackage(), (adapter) -> tsb.startActivities( - getActivityOptions(mCentralSurfaces.getDisplayId(), adapter), + getActivityOptions(mDisplayId, adapter), mUserTracker.getUserHandle())); }); return true; @@ -592,7 +596,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte try { EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION, entry.getKey()); - mCentralSurfaces.wakeUpForFullScreenIntent(); + mPowerInteractor.wakeUpForFullScreenIntent(); ActivityOptions options = ActivityOptions.makeBasic(); options.setPendingIntentBackgroundActivityStartMode( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index d731f8886536..69199966dea6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -30,7 +30,6 @@ import com.android.systemui.statusbar.connectivity.IconState; import com.android.systemui.statusbar.connectivity.MobileDataIndicators; import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.connectivity.SignalCallback; -import com.android.systemui.statusbar.connectivity.WifiIndicators; import com.android.systemui.statusbar.policy.SecurityController; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; @@ -51,7 +50,6 @@ public class StatusBarSignalPolicy implements SignalCallback, private final String mSlotAirplane; private final String mSlotMobile; - private final String mSlotWifi; private final String mSlotEthernet; private final String mSlotVpn; private final String mSlotNoCalling; @@ -67,17 +65,14 @@ public class StatusBarSignalPolicy implements SignalCallback, private boolean mHideAirplane; private boolean mHideMobile; - private boolean mHideWifi; private boolean mHideEthernet; private boolean mActivityEnabled; // Track as little state as possible, and only for padding purposes private boolean mIsAirplaneMode = false; - private boolean mIsWifiEnabled = false; private ArrayList<MobileIconState> mMobileStates = new ArrayList<>(); private ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>(); - private WifiIconState mWifiIconState = new WifiIconState(); private boolean mInitialized; @Inject @@ -99,7 +94,6 @@ public class StatusBarSignalPolicy implements SignalCallback, mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane); mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile); - mSlotWifi = mContext.getString(com.android.internal.R.string.status_bar_wifi); mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet); mSlotVpn = mContext.getString(com.android.internal.R.string.status_bar_vpn); mSlotNoCalling = mContext.getString(com.android.internal.R.string.status_bar_no_calling); @@ -154,15 +148,13 @@ public class StatusBarSignalPolicy implements SignalCallback, ArraySet<String> hideList = StatusBarIconController.getIconHideList(mContext, newValue); boolean hideAirplane = hideList.contains(mSlotAirplane); boolean hideMobile = hideList.contains(mSlotMobile); - boolean hideWifi = hideList.contains(mSlotWifi); boolean hideEthernet = hideList.contains(mSlotEthernet); if (hideAirplane != mHideAirplane || hideMobile != mHideMobile - || hideEthernet != mHideEthernet || hideWifi != mHideWifi) { + || hideEthernet != mHideEthernet) { mHideAirplane = hideAirplane; mHideMobile = hideMobile; mHideEthernet = hideEthernet; - mHideWifi = hideWifi; // Re-register to get new callbacks. mNetworkController.removeCallback(this); mNetworkController.addCallback(this); @@ -170,56 +162,6 @@ public class StatusBarSignalPolicy implements SignalCallback, } @Override - public void setWifiIndicators(@NonNull WifiIndicators indicators) { - if (DEBUG) { - Log.d(TAG, "setWifiIndicators: " + indicators); - } - boolean visible = indicators.statusIcon.visible && !mHideWifi; - boolean in = indicators.activityIn && mActivityEnabled && visible; - boolean out = indicators.activityOut && mActivityEnabled && visible; - mIsWifiEnabled = indicators.enabled; - - WifiIconState newState = mWifiIconState.copy(); - - if (mWifiIconState.noDefaultNetwork && mWifiIconState.noNetworksAvailable - && !mIsAirplaneMode) { - newState.visible = true; - newState.resId = R.drawable.ic_qs_no_internet_unavailable; - } else if (mWifiIconState.noDefaultNetwork && !mWifiIconState.noNetworksAvailable - && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) { - newState.visible = true; - newState.resId = R.drawable.ic_qs_no_internet_available; - } else { - newState.visible = visible; - newState.resId = indicators.statusIcon.icon; - newState.activityIn = in; - newState.activityOut = out; - newState.contentDescription = indicators.statusIcon.contentDescription; - MobileIconState first = getFirstMobileState(); - newState.signalSpacerVisible = first != null && first.typeId != 0; - } - newState.slot = mSlotWifi; - newState.airplaneSpacerVisible = mIsAirplaneMode; - updateWifiIconWithState(newState); - mWifiIconState = newState; - } - - private void updateShowWifiSignalSpacer(WifiIconState state) { - MobileIconState first = getFirstMobileState(); - state.signalSpacerVisible = first != null && first.typeId != 0; - } - - private void updateWifiIconWithState(WifiIconState state) { - if (DEBUG) Log.d(TAG, "WifiIconState: " + state == null ? "" : state.toString()); - if (state.visible && state.resId > 0) { - mIconController.setWifiIcon(mSlotWifi, state); - mIconController.setIconVisibility(mSlotWifi, true); - } else { - mIconController.setIconVisibility(mSlotWifi, false); - } - } - - @Override public void setCallIndicator(@NonNull IconState statusIcon, int subId) { if (DEBUG) { Log.d(TAG, "setCallIndicator: " @@ -257,10 +199,6 @@ public class StatusBarSignalPolicy implements SignalCallback, return; } - // Visibility of the data type indicator changed - boolean typeChanged = indicators.statusType != state.typeId - && (indicators.statusType == 0 || state.typeId == 0); - state.visible = indicators.statusIcon.visible && !mHideMobile; state.strengthId = indicators.statusIcon.icon; state.typeId = indicators.statusType; @@ -277,15 +215,6 @@ public class StatusBarSignalPolicy implements SignalCallback, } // Always send a copy to maintain value type semantics mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates)); - - if (typeChanged) { - WifiIconState wifiCopy = mWifiIconState.copy(); - updateShowWifiSignalSpacer(wifiCopy); - if (!Objects.equals(wifiCopy, mWifiIconState)) { - updateWifiIconWithState(wifiCopy); - mWifiIconState = wifiCopy; - } - } } private CallIndicatorIconState getNoCallingState(int subId) { @@ -308,15 +237,6 @@ public class StatusBarSignalPolicy implements SignalCallback, return null; } - private MobileIconState getFirstMobileState() { - if (mMobileStates.size() > 0) { - return mMobileStates.get(0); - } - - return null; - } - - /** * It is expected that a call to setSubs will be immediately followed by setMobileDataIndicators * so we don't have to update the icon manager at this point, just remove the old ones @@ -504,60 +424,6 @@ public class StatusBarSignalPolicy implements SignalCallback, } } - public static class WifiIconState extends SignalIconState{ - public int resId; - public boolean airplaneSpacerVisible; - public boolean signalSpacerVisible; - public boolean noDefaultNetwork; - public boolean noValidatedNetwork; - public boolean noNetworksAvailable; - - @Override - public boolean equals(Object o) { - // Skipping reference equality bc this should be more of a value type - if (o == null || getClass() != o.getClass()) { - return false; - } - if (!super.equals(o)) { - return false; - } - WifiIconState that = (WifiIconState) o; - return resId == that.resId - && airplaneSpacerVisible == that.airplaneSpacerVisible - && signalSpacerVisible == that.signalSpacerVisible - && noDefaultNetwork == that.noDefaultNetwork - && noValidatedNetwork == that.noValidatedNetwork - && noNetworksAvailable == that.noNetworksAvailable; - } - - public void copyTo(WifiIconState other) { - super.copyTo(other); - other.resId = resId; - other.airplaneSpacerVisible = airplaneSpacerVisible; - other.signalSpacerVisible = signalSpacerVisible; - other.noDefaultNetwork = noDefaultNetwork; - other.noValidatedNetwork = noValidatedNetwork; - other.noNetworksAvailable = noNetworksAvailable; - } - - public WifiIconState copy() { - WifiIconState newState = new WifiIconState(); - copyTo(newState); - return newState; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), - resId, airplaneSpacerVisible, signalSpacerVisible, noDefaultNetwork, - noValidatedNetwork, noNetworksAvailable); - } - - @Override public String toString() { - return "WifiIconState(resId=" + resId + ", visible=" + visible + ")"; - } - } - /** * A little different. This one delegates to SignalDrawable instead of a specific resId */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java index 97a1bd1851d9..c8e73d33c452 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java @@ -26,6 +26,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.StatusBarBoundsProvider; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.phone.SystemBarAttributesListener; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; @@ -72,6 +73,13 @@ public interface StatusBarFragmentModule { /** */ @Provides @StatusBarFragmentScope + static StatusBarLocation getStatusBarLocation() { + return StatusBarLocation.HOME; + } + + /** */ + @Provides + @StatusBarFragmentScope @Named(START_SIDE_CONTENT) static View startSideContent(@RootView PhoneStatusBarView view) { return view.findViewById(R.id.status_bar_start_side_content); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt index 4a684d9f8e36..29829e46cda7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt @@ -45,18 +45,6 @@ constructor( fun runNewMobileIconsBackend(): Boolean = featureFlags.isEnabled(Flags.NEW_STATUS_BAR_MOBILE_ICONS_BACKEND) || useNewMobileIcons() - /** True if we should display the wifi icon using the new status bar data pipeline. */ - fun useNewWifiIcon(): Boolean = featureFlags.isEnabled(Flags.NEW_STATUS_BAR_WIFI_ICON) - - /** - * True if we should run the new wifi icon backend to get the logging. - * - * Does *not* affect whether we render the wifi icon using the new backend data. See - * [useNewWifiIcon] for that. - */ - fun runNewWifiIconBackend(): Boolean = - featureFlags.isEnabled(Flags.NEW_STATUS_BAR_WIFI_ICON_BACKEND) || useNewWifiIcon() - /** * Returns true if we should apply some coloring to the icons that were rendered with the new * pipeline to help with debugging. @@ -71,5 +59,5 @@ constructor( * @return true if this icon is controlled by any of the status bar pipeline flags */ fun isIconControlledByFlags(slotName: String): Boolean = - slotName == wifiSlot && useNewWifiIcon() || slotName == mobileSlot && useNewMobileIcons() + slotName == wifiSlot || (slotName == mobileSlot && useNewMobileIcons()) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt index 83738544ee84..a1b96dd327e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt @@ -101,12 +101,7 @@ open class ModernStatusBarView(context: Context, attrs: AttributeSet?) : this.binding = bindingCreator.invoke() } - /** - * Creates a [StatusBarIconView] that is always in DOT mode and adds it to this view. - * - * Mostly duplicated from [com.android.systemui.statusbar.StatusBarWifiView] and - * [com.android.systemui.statusbar.StatusBarMobileView]. - */ + /** Creates a [StatusBarIconView] that is always in DOT mode and adds it to this view. */ private fun initDotView() { // TODO(b/238425913): Could we just have this dot view be part of the layout with a dot // drawable so we don't need to inflate it manually? Would that not work with animations? diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt index 174298ab6490..6d7182376fab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt @@ -68,12 +68,7 @@ constructor( launch { locationViewModel.wifiIcon.collect { wifiIcon -> // Only notify the icon controller if we want to *render* the new icon. - // Note that this flow may still run if - // [statusBarPipelineFlags.runNewWifiIconBackend] is true because we may - // want to get the logging data without rendering. - if ( - wifiIcon is WifiIcon.Visible && statusBarPipelineFlags.useNewWifiIcon() - ) { + if (wifiIcon is WifiIcon.Visible) { iconController.setNewWifiIcon() } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java index 3d165912a09c..7df083afcd19 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -19,6 +19,9 @@ package com.android.systemui.statusbar.policy; import android.annotation.Nullable; import android.view.View; +import androidx.annotation.NonNull; + +import com.android.systemui.Dumpable; import com.android.systemui.demomode.DemoMode; import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; @@ -136,7 +139,7 @@ public interface BatteryController extends DemoMode, * A listener that will be notified whenever a change in battery level or power save mode has * occurred. */ - interface BatteryStateChangeCallback { + interface BatteryStateChangeCallback extends Dumpable { default void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { } @@ -158,6 +161,11 @@ public interface BatteryController extends DemoMode, default void onIsBatteryDefenderChanged(boolean isBatteryDefender) { } + + @Override + default void dump(@NonNull PrintWriter pw, @NonNull String[] args) { + pw.println(this); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index e69d86c31abc..d5d8f4d7598e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -22,6 +22,7 @@ import static android.os.BatteryManager.EXTRA_CHARGING_STATUS; import static android.os.BatteryManager.EXTRA_PRESENT; import static com.android.settingslib.fuelgauge.BatterySaverLogging.SAVER_ENABLED_QS; +import static com.android.systemui.util.DumpUtilsKt.asIndenting; import android.annotation.WorkerThread; import android.content.BroadcastReceiver; @@ -33,6 +34,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.PowerManager; import android.os.PowerSaveState; +import android.util.IndentingPrintWriter; import android.util.Log; import android.view.View; @@ -157,15 +159,29 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC } @Override - public void dump(PrintWriter pw, String[] args) { - pw.println("BatteryController state:"); - pw.print(" mLevel="); pw.println(mLevel); - pw.print(" mPluggedIn="); pw.println(mPluggedIn); - pw.print(" mCharging="); pw.println(mCharging); - pw.print(" mCharged="); pw.println(mCharged); - pw.print(" mIsBatteryDefender="); pw.println(mIsBatteryDefender); - pw.print(" mPowerSave="); pw.println(mPowerSave); - pw.print(" mStateUnknown="); pw.println(mStateUnknown); + public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { + IndentingPrintWriter ipw = asIndenting(pw); + ipw.println("BatteryController state:"); + ipw.increaseIndent(); + ipw.print("mHasReceivedBattery="); ipw.println(mHasReceivedBattery); + ipw.print("mLevel="); ipw.println(mLevel); + ipw.print("mPluggedIn="); ipw.println(mPluggedIn); + ipw.print("mCharging="); ipw.println(mCharging); + ipw.print("mCharged="); ipw.println(mCharged); + ipw.print("mIsBatteryDefender="); ipw.println(mIsBatteryDefender); + ipw.print("mPowerSave="); ipw.println(mPowerSave); + ipw.print("mStateUnknown="); ipw.println(mStateUnknown); + ipw.println("Callbacks:------------------"); + // Since the above lines are already indented, we need to indent twice for the callbacks. + ipw.increaseIndent(); + synchronized (mChangeCallbacks) { + final int n = mChangeCallbacks.size(); + for (int i = 0; i < n; i++) { + mChangeCallbacks.get(i).dump(ipw, args); + } + } + ipw.decreaseIndent(); + ipw.println("------------------"); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt index 3e1c13c1cba8..c1ac800b8159 100644 --- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt +++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt @@ -64,9 +64,9 @@ constructor( // These values must only be accessed on the handler. private var batteryCapacity = 1.0f private var suppressed = false - private var inputDeviceId: Int? = null private var instanceId: InstanceId? = null - + @VisibleForTesting var inputDeviceId: Int? = null + private set @VisibleForTesting var instanceIdSequence = InstanceIdSequence(1 shl 13) fun init() { @@ -110,10 +110,10 @@ constructor( fun updateBatteryState(deviceId: Int, batteryState: BatteryState) { handler.post updateBattery@{ + inputDeviceId = deviceId if (batteryState.capacity == batteryCapacity || batteryState.capacity <= 0f) return@updateBattery - inputDeviceId = deviceId batteryCapacity = batteryState.capacity debugLog { "Updating notification battery state to $batteryCapacity " + diff --git a/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java b/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java deleted file mode 100644 index b54d1566eba3..000000000000 --- a/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.tracing; - -import static com.android.systemui.tracing.nano.SystemUiTraceFileProto.MAGIC_NUMBER_H; -import static com.android.systemui.tracing.nano.SystemUiTraceFileProto.MAGIC_NUMBER_L; - -import android.content.Context; -import android.os.SystemClock; - -import androidx.annotation.NonNull; - -import com.android.systemui.Dumpable; -import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.shared.tracing.FrameProtoTracer; -import com.android.systemui.shared.tracing.FrameProtoTracer.ProtoTraceParams; -import com.android.systemui.shared.tracing.ProtoTraceable; -import com.android.systemui.tracing.nano.SystemUiTraceEntryProto; -import com.android.systemui.tracing.nano.SystemUiTraceFileProto; -import com.android.systemui.tracing.nano.SystemUiTraceProto; - -import com.google.protobuf.nano.MessageNano; - -import java.io.File; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Queue; - -import javax.inject.Inject; - -/** - * Controller for coordinating winscope proto tracing. - */ -@SysUISingleton -public class ProtoTracer implements - Dumpable, - ProtoTraceParams< - MessageNano, - SystemUiTraceFileProto, - SystemUiTraceEntryProto, - SystemUiTraceProto> { - - private static final String TAG = "ProtoTracer"; - private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; - - private final Context mContext; - private final FrameProtoTracer<MessageNano, SystemUiTraceFileProto, SystemUiTraceEntryProto, - SystemUiTraceProto> mProtoTracer; - - @Inject - public ProtoTracer(Context context, DumpManager dumpManager) { - mContext = context; - mProtoTracer = new FrameProtoTracer<>(this); - dumpManager.registerDumpable(this); - } - - @Override - public File getTraceFile() { - return new File(mContext.getFilesDir(), "sysui_trace.pb"); - } - - @Override - public SystemUiTraceFileProto getEncapsulatingTraceProto() { - return new SystemUiTraceFileProto(); - } - - @Override - public SystemUiTraceEntryProto updateBufferProto(SystemUiTraceEntryProto reuseObj, - ArrayList<ProtoTraceable<SystemUiTraceProto>> traceables) { - SystemUiTraceEntryProto proto = reuseObj != null - ? reuseObj - : new SystemUiTraceEntryProto(); - proto.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos(); - proto.systemUi = proto.systemUi != null ? proto.systemUi : new SystemUiTraceProto(); - for (ProtoTraceable t : traceables) { - t.writeToProto(proto.systemUi); - } - return proto; - } - - @Override - public byte[] serializeEncapsulatingProto(SystemUiTraceFileProto encapsulatingProto, - Queue<SystemUiTraceEntryProto> buffer) { - encapsulatingProto.magicNumber = MAGIC_NUMBER_VALUE; - encapsulatingProto.entry = buffer.toArray(new SystemUiTraceEntryProto[0]); - return MessageNano.toByteArray(encapsulatingProto); - } - - @Override - public byte[] getProtoBytes(MessageNano proto) { - return MessageNano.toByteArray(proto); - } - - @Override - public int getProtoSize(MessageNano proto) { - return proto.getCachedSize(); - } - - public void start() { - mProtoTracer.start(); - } - - public void stop() { - mProtoTracer.stop(); - } - - public boolean isEnabled() { - return mProtoTracer.isEnabled(); - } - - public void add(ProtoTraceable<SystemUiTraceProto> traceable) { - mProtoTracer.add(traceable); - } - - public void remove(ProtoTraceable<SystemUiTraceProto> traceable) { - mProtoTracer.remove(traceable); - } - - public void scheduleFrameUpdate() { - mProtoTracer.scheduleFrameUpdate(); - } - - public void update() { - mProtoTracer.update(); - } - - @Override - public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { - pw.println("ProtoTracer:"); - pw.print(" "); pw.println("enabled: " + mProtoTracer.isEnabled()); - pw.print(" "); pw.println("usagePct: " + mProtoTracer.getBufferUsagePct()); - pw.print(" "); pw.println("file: " + getTraceFile()); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto b/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto deleted file mode 100644 index d940a6b5c460..000000000000 --- a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2019 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. - */ - -syntax = "proto2"; - -package com.android.systemui.tracing; - -option java_multiple_files = true; - -message SystemUiTraceProto { - - optional EdgeBackGestureHandlerProto edge_back_gesture_handler = 1; -} - -message EdgeBackGestureHandlerProto { - - optional bool allow_gesture = 1; -} - -/* represents a file full of system ui trace entries. - Encoded, it should start with 0x9 0x53 0x59 0x53 0x55 0x49 0x54 0x52 0x43 (.SYSUITRC), such - that they can be easily identified. */ -message SystemUiTraceFileProto { - - /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L - (this is needed because enums have to be 32 bits and there's no nice way to put 64bit - constants into .proto files. */ - enum MagicNumber { - INVALID = 0; - MAGIC_NUMBER_L = 0x55535953; /* SYSU (little-endian ASCII) */ - MAGIC_NUMBER_H = 0x43525449; /* ITRC (little-endian ASCII) */ - } - - optional fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */ - repeated SystemUiTraceEntryProto entry = 2; -} - -/* one system ui trace entry. */ -message SystemUiTraceEntryProto { - /* required: elapsed realtime in nanos since boot of when this entry was logged */ - optional fixed64 elapsed_realtime_nanos = 1; - - optional SystemUiTraceProto system_ui = 3; -} diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java index d9a8e0cfb53a..38226ec45910 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java @@ -45,7 +45,7 @@ import com.android.systemui.screenshot.ReferenceScreenshotModule; import com.android.systemui.settings.dagger.MultiUserUtilsModule; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; import com.android.systemui.shade.ShadeController; -import com.android.systemui.shade.ShadeControllerImpl; +import com.android.systemui.shade.ShadeControllerEmptyImpl; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationListener; @@ -138,7 +138,7 @@ public abstract class TvSystemUIModule { abstract DockManager bindDockManager(DockManagerImpl dockManager); @Binds - abstract ShadeController provideShadeController(ShadeControllerImpl shadeController); + abstract ShadeController provideShadeController(ShadeControllerEmptyImpl shadeController); @SysUISingleton @Provides diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 93622200ad46..349f3684659c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -168,6 +168,13 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, /** Volume dialog slider animation. */ private static final String TYPE_UPDATE = "update"; + /** + * TODO(b/290612381): remove lingering animations or tolerate them + * When false, this will cause this class to not listen to animator events and not record jank + * events. This should never be false in production code, and only is false for unit tests for + * this class. This flag should be true in Scenario/Integration tests. + */ + private final boolean mShouldListenForJank; private final int mDialogShowAnimationDurationMs; private final int mDialogHideAnimationDurationMs; private int mDialogWidth; @@ -304,6 +311,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, VolumePanelFactory volumePanelFactory, ActivityStarter activityStarter, InteractionJankMonitor interactionJankMonitor, + boolean shouldListenForJank, CsdWarningDialog.Factory csdWarningDialogFactory, DevicePostureController devicePostureController, Looper looper, @@ -311,6 +319,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, mContext = new ContextThemeWrapper(context, R.style.volume_dialog_theme); mHandler = new H(looper); + + mShouldListenForJank = shouldListenForJank; mController = volumeDialogController; mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); @@ -1368,7 +1378,10 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, } private Animator.AnimatorListener getJankListener(View v, String type, long timeout) { - return new Animator.AnimatorListener() { + if (!mShouldListenForJank) { + // TODO(b/290612381): temporary fix to prevent null pointers on leftover JankMonitors + return null; + } else return new Animator.AnimatorListener() { @Override public void onAnimationStart(@NonNull Animator animation) { if (!v.isAttachedToWindow()) { diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java index aa4ee545a500..d0edc6e7ce4c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java @@ -72,6 +72,7 @@ public interface VolumeModule { volumePanelFactory, activityStarter, interactionJankMonitor, + true, /* should listen for jank */ csdFactory, devicePostureController, Looper.getMainLooper(), diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 5144d1966222..943e906a1ebc 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -51,15 +51,11 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.notetask.NoteTaskInitializer; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; -import com.android.systemui.shared.tracing.ProtoTraceable; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.tracing.ProtoTracer; -import com.android.systemui.tracing.nano.SystemUiTraceProto; import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; -import com.android.wm.shell.nano.WmShellTraceProto; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedEventCallback; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; @@ -94,8 +90,7 @@ import javax.inject.Inject; @SysUISingleton public final class WMShell implements CoreStartable, - CommandQueue.Callbacks, - ProtoTraceable<SystemUiTraceProto> { + CommandQueue.Callbacks { private static final String TAG = WMShell.class.getName(); private static final int INVALID_SYSUI_STATE_MASK = SYSUI_STATE_DIALOG_SHOWING @@ -122,7 +117,6 @@ public final class WMShell implements private final ScreenLifecycle mScreenLifecycle; private final SysUiState mSysUiState; private final WakefulnessLifecycle mWakefulnessLifecycle; - private final ProtoTracer mProtoTracer; private final UserTracker mUserTracker; private final DisplayTracker mDisplayTracker; private final NoteTaskInitializer mNoteTaskInitializer; @@ -184,7 +178,6 @@ public final class WMShell implements KeyguardUpdateMonitor keyguardUpdateMonitor, ScreenLifecycle screenLifecycle, SysUiState sysUiState, - ProtoTracer protoTracer, WakefulnessLifecycle wakefulnessLifecycle, UserTracker userTracker, DisplayTracker displayTracker, @@ -203,7 +196,6 @@ public final class WMShell implements mOneHandedOptional = oneHandedOptional; mDesktopModeOptional = desktopMode; mWakefulnessLifecycle = wakefulnessLifecycle; - mProtoTracer = protoTracer; mUserTracker = userTracker; mDisplayTracker = displayTracker; mNoteTaskInitializer = noteTaskInitializer; @@ -223,7 +215,6 @@ public final class WMShell implements // Subscribe to user changes mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor()); - mProtoTracer.add(this); mCommandQueue.addCallback(this); mPipOptional.ifPresent(this::initPip); mSplitScreenOptional.ifPresent(this::initSplitScreen); @@ -361,12 +352,6 @@ public final class WMShell implements } @Override - public void writeToProto(SystemUiTraceProto proto) { - // Dump to WMShell proto here - // TODO: Figure out how we want to synchronize while dumping to proto - } - - @Override public void dump(PrintWriter pw, String[] args) { // Handle commands if provided if (mShell.handleCommand(args, pw)) { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java index fa32835c2695..677d3ff3df69 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java @@ -187,9 +187,7 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase { @Test public void testLockedOut_verifyPasswordAndUnlock_doesNotEnableViewInput() { - mKeyguardAbsKeyInputViewController.handleAttemptLockout( - SystemClock.elapsedRealtime() + 1000); - mKeyguardAbsKeyInputViewController.verifyPasswordAndUnlock(); + mKeyguardAbsKeyInputViewController.handleAttemptLockout(SystemClock.elapsedRealtime()); verify(mAbsKeyInputView).setPasswordEntryInputEnabled(false); verify(mAbsKeyInputView).setPasswordEntryEnabled(false); verify(mAbsKeyInputView, never()).setPasswordEntryInputEnabled(true); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt index 9db267c2c929..d256ee163877 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt @@ -105,6 +105,7 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { `when`(keyguardPinView.findViewById<View>(R.id.key_enter)).thenReturn(enterButton) // For posture tests: `when`(keyguardPinView.buttons).thenReturn(arrayOf()) + `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) pinViewController = KeyguardPinViewController( @@ -167,7 +168,6 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { @Test fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsLessThan5() { `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true) - `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true) `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(3) `when`(passwordTextView.text).thenReturn("") @@ -182,7 +182,6 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { @Test fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsMoreThan5() { `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true) - `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true) `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(6) `when`(passwordTextView.text).thenReturn("") diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java index 1482f291b57a..40b572934f74 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java @@ -33,10 +33,10 @@ import android.provider.Settings; import androidx.test.filters.SmallTest; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.flags.FakeFeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; @@ -61,7 +61,6 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { private Handler mHandler; @Mock private ContentResolver mContentResolver; - private FakeFeatureFlags mFeatureFlags; @Mock private BatteryController mBatteryController; @@ -74,8 +73,8 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { when(mBatteryMeterView.getContext()).thenReturn(mContext); when(mBatteryMeterView.getResources()).thenReturn(mContext.getResources()); - mFeatureFlags = new FakeFeatureFlags(); - mFeatureFlags.set(Flags.BATTERY_SHIELD_ICON, false); + mContext.getOrCreateTestableResources().addOverride( + R.bool.flag_battery_shield_icon, false); } @Test @@ -134,7 +133,8 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { @Test public void shieldFlagDisabled_viewNotified() { - mFeatureFlags.set(Flags.BATTERY_SHIELD_ICON, false); + mContext.getOrCreateTestableResources().addOverride( + R.bool.flag_battery_shield_icon, false); initController(); @@ -143,7 +143,8 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { @Test public void shieldFlagEnabled_viewNotified() { - mFeatureFlags.set(Flags.BATTERY_SHIELD_ICON, true); + mContext.getOrCreateTestableResources().addOverride( + R.bool.flag_battery_shield_icon, true); initController(); @@ -153,12 +154,12 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { private void initController() { mController = new BatteryMeterViewController( mBatteryMeterView, + StatusBarLocation.HOME, mUserTracker, mConfigurationController, mTunerService, mHandler, mContentResolver, - mFeatureFlags, mBatteryController ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt index 023ed061c642..45bb9313264c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt @@ -21,6 +21,10 @@ import android.os.PowerManager import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.shared.model.WakeSleepReason +import com.android.systemui.keyguard.shared.model.WakefulnessModel +import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.data.repository.FakePowerRepository import com.android.systemui.statusbar.phone.ScreenOffAnimationController @@ -44,6 +48,7 @@ class PowerInteractorTest : SysuiTestCase() { private lateinit var underTest: PowerInteractor private lateinit var repository: FakePowerRepository + private val keyguardRepository = FakeKeyguardRepository() @Mock private lateinit var falsingCollector: FalsingCollector @Mock private lateinit var screenOffAnimationController: ScreenOffAnimationController @Mock private lateinit var statusBarStateController: StatusBarStateController @@ -59,6 +64,7 @@ class PowerInteractorTest : SysuiTestCase() { underTest = PowerInteractor( repository, + keyguardRepository, falsingCollector, screenOffAnimationController, statusBarStateController, @@ -125,6 +131,57 @@ class PowerInteractorTest : SysuiTestCase() { verify(falsingCollector).onScreenOnFromTouch() } + @Test + fun wakeUpForFullScreenIntent_notGoingToSleepAndNotDozing_notWoken() { + keyguardRepository.setWakefulnessModel( + WakefulnessModel( + state = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.OTHER, + lastSleepReason = WakeSleepReason.OTHER, + ) + ) + whenever(statusBarStateController.isDozing).thenReturn(false) + + underTest.wakeUpForFullScreenIntent() + + assertThat(repository.lastWakeWhy).isNull() + assertThat(repository.lastWakeReason).isNull() + } + + @Test + fun wakeUpForFullScreenIntent_startingToSleep_woken() { + keyguardRepository.setWakefulnessModel( + WakefulnessModel( + state = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.OTHER, + lastSleepReason = WakeSleepReason.OTHER, + ) + ) + whenever(statusBarStateController.isDozing).thenReturn(false) + + underTest.wakeUpForFullScreenIntent() + + assertThat(repository.lastWakeWhy).isNotNull() + assertThat(repository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_APPLICATION) + } + + @Test + fun wakeUpForFullScreenIntent_dozing_woken() { + whenever(statusBarStateController.isDozing).thenReturn(true) + keyguardRepository.setWakefulnessModel( + WakefulnessModel( + state = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.OTHER, + lastSleepReason = WakeSleepReason.OTHER, + ) + ) + + underTest.wakeUpForFullScreenIntent() + + assertThat(repository.lastWakeWhy).isNotNull() + assertThat(repository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_APPLICATION) + } + companion object { private val IMMEDIATE = Dispatchers.Main.immediate } diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt index df5e7bcd16be..3e9ddcb06389 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt @@ -20,6 +20,9 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.shared.model.WakeSleepReason +import com.android.systemui.keyguard.shared.model.WakefulnessModel +import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneContainerNames import com.android.systemui.scene.shared.model.SceneKey @@ -47,12 +50,18 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { utils.authenticationInteractor( repository = authenticationRepository, ) + private val keyguardRepository = utils.keyguardRepository() + private val keyguardInteractor = + utils.keyguardInteractor( + repository = keyguardRepository, + ) private val underTest = SystemUiDefaultSceneContainerStartable( applicationScope = testScope.backgroundScope, sceneInteractor = sceneInteractor, authenticationInteractor = authenticationInteractor, + keyguardInteractor = keyguardInteractor, featureFlags = featureFlags, ) @@ -280,6 +289,94 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) } + @Test + fun switchToGoneWhenDeviceSleepsUnlocked_featureEnabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Shade, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + underTest.start() + + keyguardRepository.setWakefulnessModel(ASLEEP) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + } + + @Test + fun switchToGoneWhenDeviceSleepsUnlocked_featureDisabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Shade, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + underTest.start() + + keyguardRepository.setWakefulnessModel(ASLEEP) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + } + + @Test + fun switchToLockscreenWhenDeviceSleepsLocked_featureEnabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Shade, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + underTest.start() + + keyguardRepository.setWakefulnessModel(ASLEEP) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + } + + @Test + fun switchToLockscreenWhenDeviceSleepsLocked_featureDisabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Shade, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + underTest.start() + + keyguardRepository.setWakefulnessModel(ASLEEP) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + } + private fun prepareState( isFeatureEnabled: Boolean = true, isDeviceUnlocked: Boolean = false, @@ -293,4 +390,13 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { sceneInteractor.setCurrentScene(SceneContainerNames.SYSTEM_UI_DEFAULT, SceneModel(it)) } } + + companion object { + private val ASLEEP = + WakefulnessModel( + state = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.POWER_BUTTON + ) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt index a4fab1dbac57..77a22ac9b092 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dock.DockManager import com.android.systemui.dump.DumpManager +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.data.repository.FakePowerRepository @@ -89,6 +90,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { dockManager, PowerInteractor( powerRepository, + FakeKeyguardRepository(), falsingCollector, screenOffAnimationController, statusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt index 729c4a9145c2..52e0c9c9936b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt @@ -78,11 +78,11 @@ class ShadeControllerImplTest : SysuiTestCase() { deviceProvisionedController, notificationShadeWindowController, windowManager, + Lazy { shadeViewController }, Lazy { assistManager }, Lazy { gutsManager }, ) shadeController.setNotificationShadeWindowViewController(nswvc) - shadeController.setShadeViewController(shadeViewController) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt index f542ab099517..bf25f2975253 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt @@ -29,6 +29,7 @@ import android.view.DisplayCutout import android.view.View import android.view.ViewPropertyAnimator import android.view.WindowInsets +import android.widget.LinearLayout import android.widget.TextView import androidx.constraintlayout.motion.widget.MotionLayout import androidx.constraintlayout.widget.ConstraintSet @@ -127,6 +128,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { var viewVisibility = View.GONE var viewAlpha = 1f + private val systemIcons = LinearLayout(context) private lateinit var shadeHeaderController: ShadeHeaderController private lateinit var carrierIconSlots: List<String> private val configurationController = FakeConfigurationController() @@ -146,6 +148,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { .thenReturn(batteryMeterView) whenever<StatusIconContainer>(view.findViewById(R.id.statusIcons)).thenReturn(statusIcons) + whenever<View>(view.findViewById(R.id.shade_header_system_icons)).thenReturn(systemIcons) viewContext = Mockito.spy(context) whenever(view.context).thenReturn(viewContext) @@ -451,6 +454,17 @@ class ShadeHeaderControllerTest : SysuiTestCase() { } @Test + fun testLargeScreenActive_collapseActionRun_onSystemIconsClick() { + shadeHeaderController.largeScreenActive = true + var wasRun = false + shadeHeaderController.shadeCollapseAction = Runnable { wasRun = true } + + systemIcons.performClick() + + assertThat(wasRun).isTrue() + } + + @Test fun testShadeExpandedFraction() { // View needs to be visible for this to actually take effect shadeHeaderController.qsVisible = true diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt index ff2f1065049b..4a2518ae6f7d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -101,16 +101,18 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { @Mock lateinit var activityStarter: ActivityStarter @Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback private val disableFlagsRepository = FakeDisableFlagsRepository() + private val keyguardRepository = FakeKeyguardRepository() private val shadeInteractor = ShadeInteractor( testScope.backgroundScope, disableFlagsRepository, - keyguardRepository = FakeKeyguardRepository(), + keyguardRepository, userSetupRepository = FakeUserSetupRepository(), deviceProvisionedController = mock(), userInteractor = mock(), ) private val powerInteractor = PowerInteractor( FakePowerRepository(), + keyguardRepository, FalsingCollectorFake(), screenOffAnimationController = mock(), statusBarStateController = mock(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt index 89faa239c5a2..a56fb2c515a8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt @@ -3,7 +3,11 @@ package com.android.systemui.statusbar.notification import android.view.View import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @@ -15,8 +19,9 @@ import org.mockito.Mockito.verify @SmallTest @RunWith(JUnit4::class) class RoundableTest : SysuiTestCase() { - val targetView: View = mock() - val roundable = FakeRoundable(targetView) + private val targetView: View = mock() + private val featureFlags = FakeFeatureFlags() + private val roundable = FakeRoundable(targetView = targetView, featureFlags = featureFlags) @Test fun defaultConfig_shouldNotHaveRoundedCorner() { @@ -144,16 +149,62 @@ class RoundableTest : SysuiTestCase() { assertEquals(0.2f, roundable.roundableState.bottomRoundness) } + @Test + fun getCornerRadii_radius_maxed_to_height() { + whenever(targetView.height).thenReturn(10) + featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) + roundable.requestRoundness(1f, 1f, SOURCE1) + + assertCornerRadiiEquals(5f, 5f) + } + + @Test + fun getCornerRadii_topRadius_maxed_to_height() { + whenever(targetView.height).thenReturn(5) + featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) + roundable.requestRoundness(1f, 0f, SOURCE1) + + assertCornerRadiiEquals(5f, 0f) + } + + @Test + fun getCornerRadii_bottomRadius_maxed_to_height() { + whenever(targetView.height).thenReturn(5) + featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) + roundable.requestRoundness(0f, 1f, SOURCE1) + + assertCornerRadiiEquals(0f, 5f) + } + + @Test + fun getCornerRadii_radii_kept() { + whenever(targetView.height).thenReturn(100) + featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) + roundable.requestRoundness(1f, 1f, SOURCE1) + + assertCornerRadiiEquals(MAX_RADIUS, MAX_RADIUS) + } + + private fun assertCornerRadiiEquals(top: Float, bottom: Float) { + assertEquals("topCornerRadius", top, roundable.topCornerRadius) + assertEquals("bottomCornerRadius", bottom, roundable.bottomCornerRadius) + } + class FakeRoundable( targetView: View, radius: Float = MAX_RADIUS, + featureFlags: FeatureFlags ) : Roundable { override val roundableState = RoundableState( targetView = targetView, roundable = this, maxRadius = radius, + featureFlags = featureFlags ) + + override val clipHeight: Int + get() = roundableState.targetView.height } companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt index a87dd2d3d670..8881f42783fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt @@ -58,6 +58,7 @@ class NotificationShelfInteractorTest : SysuiTestCase() { private val powerInteractor = PowerInteractor( powerRepository, + keyguardRepository, FalsingCollectorFake(), screenOffAnimationController, statusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt index 7ae150231b98..6221f3e89ad6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt @@ -69,6 +69,7 @@ class NotificationShelfViewModelTest : SysuiTestCase() { private val powerInteractor by lazy { PowerInteractor( powerRepository, + keyguardRepository, FalsingCollectorFake(), screenOffAnimationController, statusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt index 442ba0977cf6..5e0e140563cd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt @@ -95,6 +95,7 @@ class ActivityStarterImplTest : SysuiTestCase() { Lazy { notifShadeWindowController }, activityLaunchAnimator, context, + DISPLAY_ID, lockScreenUserManager, statusBarWindowController, wakefulnessLifecycle, @@ -274,4 +275,8 @@ class ActivityStarterImplTest : SysuiTestCase() { mainExecutor.runAllReady() verify(statusBarStateController).setLeaveOpenOnKeyguardHide(true) } + + private companion object { + private const val DISPLAY_ID = 0 + } } 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 88d8dfc50b47..3d35233ad646 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 @@ -447,10 +447,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mDeviceProvisionedController, mNotificationShadeWindowController, mContext.getSystemService(WindowManager.class), + () -> mNotificationPanelViewController, () -> mAssistManager, () -> mNotificationGutsManager )); - mShadeController.setShadeViewController(mNotificationPanelViewController); mShadeController.setNotificationShadeWindowViewController( mNotificationShadeWindowViewController); mShadeController.setNotificationPresenter(mNotificationPresenter); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java index 8aaa57ffe2cb..9157cd9e4f43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE; -import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI; import static junit.framework.Assert.assertTrue; @@ -41,13 +40,11 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarMobileView; -import com.android.systemui.statusbar.StatusBarWifiView; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter; import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter; @@ -156,13 +153,9 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { assertTrue("Expected StatusBarIconView", (manager.getViewAt(0) instanceof StatusBarIconView)); - holder = holderForType(TYPE_WIFI); - manager.onIconAdded(1, "test_wifi", false, holder); - assertTrue(manager.getViewAt(1) instanceof StatusBarWifiView); - holder = holderForType(TYPE_MOBILE); - manager.onIconAdded(2, "test_mobile", false, holder); - assertTrue(manager.getViewAt(2) instanceof StatusBarMobileView); + manager.onIconAdded(1, "test_mobile", false, holder); + assertTrue(manager.getViewAt(1) instanceof StatusBarMobileView); } private StatusBarIconHolder holderForType(int type) { @@ -170,9 +163,6 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { case TYPE_MOBILE: return StatusBarIconHolder.fromMobileIconState(mock(MobileIconState.class)); - case TYPE_WIFI: - return StatusBarIconHolder.fromWifiIconState(mock(WifiIconState.class)); - case TYPE_ICON: default: return StatusBarIconHolder.fromIcon(mock(StatusBarIcon.class)); @@ -214,13 +204,6 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { } @Override - protected StatusBarWifiView addWifiIcon(int index, String slot, WifiIconState state) { - StatusBarWifiView mock = mock(StatusBarWifiView.class); - mGroup.addView(mock, index); - return mock; - } - - @Override protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) { StatusBarMobileView mock = mock(StatusBarMobileView.class); mGroup.addView(mock, index); @@ -254,13 +237,6 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { } @Override - protected StatusBarWifiView addWifiIcon(int index, String slot, WifiIconState state) { - StatusBarWifiView mock = mock(StatusBarWifiView.class); - mGroup.addView(mock, index); - return mock; - } - - @Override protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) { StatusBarMobileView mock = mock(StatusBarMobileView.class); mGroup.addView(mock, index); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index d44af885a27e..9c7f6190de44 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -21,6 +21,8 @@ import static android.service.notification.NotificationListenerService.REASON_CL import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.AdditionalAnswers.answerVoid; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -61,10 +63,14 @@ import com.android.systemui.ActivityIntentHelper; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.assist.AssistManager; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.power.data.repository.FakePowerRepository; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.NotificationShadeWindowViewController; import com.android.systemui.shade.ShadeControllerImpl; @@ -110,6 +116,8 @@ import java.util.Optional; @TestableLooper.RunWithLooper(setAsMainLooper = true) public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { + private static final int DISPLAY_ID = 0; + @Mock private AssistManager mAssistManager; @Mock @@ -118,13 +126,12 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { private NotificationClickNotifier mClickNotifier; @Mock private StatusBarStateController mStatusBarStateController; + @Mock private ScreenOffAnimationController mScreenOffAnimationController; @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock - private CentralSurfaces mCentralSurfaces; - @Mock private KeyguardStateController mKeyguardStateController; @Mock private NotificationInterruptStateProvider mNotificationInterruptStateProvider; @@ -150,6 +157,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { private ActivityLaunchAnimator mActivityLaunchAnimator; @Mock private InteractionJankMonitor mJankMonitor; + private FakePowerRepository mPowerRepository; + private PowerInteractor mPowerInteractor; @Mock private UserTracker mUserTracker; private final FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -199,6 +208,14 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { when(mUserTracker.getUserHandle()).thenReturn( UserHandle.of(ActivityManager.getCurrentUser())); + mPowerRepository = new FakePowerRepository(); + mPowerInteractor = new PowerInteractor( + mPowerRepository, + new FakeKeyguardRepository(), + new FalsingCollectorFake(), + mScreenOffAnimationController, + mStatusBarStateController); + HeadsUpManagerPhone headsUpManager = mock(HeadsUpManagerPhone.class); NotificationLaunchAnimatorControllerProvider notificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider( @@ -209,6 +226,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mNotificationActivityStarter = new StatusBarNotificationActivityStarter( getContext(), + DISPLAY_ID, mHandler, mUiBgExecutor, mVisibilityProvider, @@ -231,13 +249,13 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mock(MetricsLogger.class), mock(StatusBarNotificationActivityStarterLogger.class), mOnUserInteractionCallback, - mCentralSurfaces, mock(NotificationPresenter.class), mock(ShadeViewController.class), mock(NotificationShadeWindowController.class), mActivityLaunchAnimator, notificationAnimationProvider, mock(LaunchFullScreenIntentProvider.class), + mPowerInteractor, mock(FeatureFlags.class), mUserTracker ); @@ -274,7 +292,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { notification.flags |= Notification.FLAG_AUTO_CANCEL; when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mCentralSurfaces.isOccluded()).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(true); // When mNotificationActivityStarter.onNotificationClicked(entry, mNotificationRow); @@ -340,7 +358,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { // Given sbn.getNotification().contentIntent = null; when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mCentralSurfaces.isOccluded()).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(true); // When mNotificationActivityStarter.onNotificationClicked(entry, mBubbleNotificationRow); @@ -368,7 +386,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { // Given sbn.getNotification().contentIntent = mContentIntent; when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mCentralSurfaces.isOccluded()).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(true); // When mNotificationActivityStarter.onNotificationClicked(entry, mBubbleNotificationRow); @@ -402,11 +420,13 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { when(entry.getImportance()).thenReturn(NotificationManager.IMPORTANCE_HIGH); when(entry.getSbn()).thenReturn(sbn); - // WHEN + // WHEN the intent is launched while dozing + when(mStatusBarStateController.isDozing()).thenReturn(true); mNotificationActivityStarter.launchFullScreenIntent(entry); // THEN display should try wake up for the full screen intent - verify(mCentralSurfaces).wakeUpForFullScreenIntent(); + assertThat(mPowerRepository.getLastWakeReason()).isNotNull(); + assertThat(mPowerRepository.getLastWakeWhy()).isNotNull(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt index 90821bdef0be..d212c026d66e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt @@ -126,6 +126,16 @@ class StylusUsiPowerUiTest : SysuiTestCase() { } @Test + fun updateBatteryState_capacitySame_inputDeviceChanges_updatesInputDeviceId() { + stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f)) + stylusUsiPowerUi.updateBatteryState(1, FixedCapacityBatteryState(0.1f)) + + assertThat(stylusUsiPowerUi.inputDeviceId).isEqualTo(1) + verify(notificationManager, times(1)) + .notify(eq(R.string.stylus_battery_low_percentage), any()) + } + + @Test fun updateBatteryState_existingNotification_capacityAboveThreshold_cancelsNotification() { stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f)) stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.8f)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index 8f725bebfb16..0c77529377ab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -142,6 +142,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { mVolumePanelFactory, mActivityStarter, mInteractionJankMonitor, + false, mCsdWarningDialogFactory, mPostureController, mTestableLooper.getLooper(), @@ -378,6 +379,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { mVolumePanelFactory, mActivityStarter, mInteractionJankMonitor, + false, mCsdWarningDialogFactory, devicePostureController, mTestableLooper.getLooper(), @@ -397,6 +399,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { int gravity = dialog.getWindowGravity(); assertEquals(Gravity.TOP, gravity & Gravity.VERTICAL_GRAVITY_MASK); + + cleanUp(dialog); } @Test @@ -415,6 +419,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { mVolumePanelFactory, mActivityStarter, mInteractionJankMonitor, + false, mCsdWarningDialogFactory, devicePostureController, mTestableLooper.getLooper(), @@ -433,6 +438,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { int gravity = dialog.getWindowGravity(); assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK); + + cleanUp(dialog); } @Test @@ -451,6 +458,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { mVolumePanelFactory, mActivityStarter, mInteractionJankMonitor, + false, mCsdWarningDialogFactory, devicePostureController, mTestableLooper.getLooper(), @@ -469,6 +477,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { int gravity = dialog.getWindowGravity(); assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK); + + cleanUp(dialog); } @Test @@ -489,18 +499,19 @@ public class VolumeDialogImplTest extends SysuiTestCase { mVolumePanelFactory, mActivityStarter, mInteractionJankMonitor, + false, mCsdWarningDialogFactory, mPostureController, mTestableLooper.getLooper(), - mDumpManager - ); + mDumpManager); dialog.init(0, null); verify(mPostureController, never()).removeCallback(any()); - dialog.destroy(); verify(mPostureController).removeCallback(any()); + + cleanUp(dialog); } private void setOrientation(int orientation) { @@ -513,14 +524,18 @@ public class VolumeDialogImplTest extends SysuiTestCase { @After public void teardown() { - if (mDialog != null) { - mDialog.clearInternalHandlerAfterTest(); - } + cleanUp(mDialog); setOrientation(mOriginalOrientation); mTestableLooper.processAllMessages(); reset(mPostureController); } + private void cleanUp(VolumeDialogImpl dialog) { + if (dialog != null) { + dialog.clearInternalHandlerAfterTest(); + } + } + /* @Test public void testContentDescriptions() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index 9de7a87c8b82..ef0adbb91a63 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -34,7 +34,6 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.tracing.ProtoTracer; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; @@ -76,7 +75,6 @@ public class WMShellTest extends SysuiTestCase { @Mock SplitScreen mSplitScreen; @Mock OneHanded mOneHanded; @Mock WakefulnessLifecycle mWakefulnessLifecycle; - @Mock ProtoTracer mProtoTracer; @Mock UserTracker mUserTracker; @Mock ShellExecutor mSysUiMainExecutor; @Mock NoteTaskInitializer mNoteTaskInitializer; @@ -99,7 +97,6 @@ public class WMShellTest extends SysuiTestCase { mKeyguardUpdateMonitor, mScreenLifecycle, mSysUiState, - mProtoTracer, mWakefulnessLifecycle, mUserTracker, displayTracker, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt index 60a4951735c5..63097401bc5a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt @@ -48,7 +48,7 @@ class FakeKeyguardRepository : KeyguardRepository { override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing private val _isKeyguardUnlocked = MutableStateFlow(false) - override val isKeyguardUnlocked: Flow<Boolean> = _isKeyguardUnlocked + override val isKeyguardUnlocked: StateFlow<Boolean> = _isKeyguardUnlocked.asStateFlow() private val _isKeyguardOccluded = MutableStateFlow(false) override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index d797962bae8b..47e1daf4008c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -22,11 +22,20 @@ import com.android.systemui.authentication.data.repository.AuthenticationReposit import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.bouncer.data.repository.BouncerRepository +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel +import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.FakeCommandQueue +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor +import com.android.systemui.keyguard.shared.model.WakeSleepReason +import com.android.systemui.keyguard.shared.model.WakefulnessModel +import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.scene.data.repository.SceneContainerRepository import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneContainerConfig @@ -53,7 +62,11 @@ class SceneTestUtils( ) { val testDispatcher = StandardTestDispatcher() val testScope = TestScope(testDispatcher) - val featureFlags = FakeFeatureFlags().apply { set(Flags.SCENE_CONTAINER, true) } + val featureFlags = + FakeFeatureFlags().apply { + set(Flags.SCENE_CONTAINER, true) + set(Flags.FACE_AUTH_REFACTOR, false) + } private val userRepository: UserRepository by lazy { FakeUserRepository().apply { val users = listOf(UserInfo(/* id= */ 0, "name", /* flags= */ 0)) @@ -67,6 +80,17 @@ class SceneTestUtils( currentTime = { testScope.currentTime }, ) } + val keyguardRepository: FakeKeyguardRepository by lazy { + FakeKeyguardRepository().apply { + setWakefulnessModel( + WakefulnessModel( + WakefulnessState.AWAKE, + WakeSleepReason.OTHER, + WakeSleepReason.OTHER, + ) + ) + } + } private val context = test.context fun fakeSceneContainerRepository( @@ -122,6 +146,20 @@ class SceneTestUtils( ) } + fun keyguardRepository(): FakeKeyguardRepository { + return keyguardRepository + } + + fun keyguardInteractor(repository: KeyguardRepository): KeyguardInteractor { + return KeyguardInteractor( + repository = repository, + commandQueue = FakeCommandQueue(), + featureFlags = featureFlags, + bouncerRepository = FakeKeyguardBouncerRepository(), + configurationRepository = FakeConfigurationRepository() + ) + } + fun bouncerInteractor( authenticationInteractor: AuthenticationInteractor, sceneInteractor: SceneInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java index c664c99cf2a7..56837e8cc7ef 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java @@ -21,7 +21,6 @@ import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import java.util.List; @@ -62,10 +61,6 @@ public class FakeStatusBarIconController extends BaseLeakChecker<IconManager> } @Override - public void setWifiIcon(String slot, WifiIconState state) { - } - - @Override public void setNewWifiIcon() { } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index e51fc0a2cef7..a682c85f03b2 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1713,6 +1713,11 @@ public class OomAdjuster { } } + private boolean isScreenOnOrAnimatingLocked(ProcessStateRecord state) { + return mService.mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE + || state.isRunningRemoteAnimation(); + } + @GuardedBy({"mService", "mProcLock"}) private boolean computeOomAdjLSP(ProcessRecord app, int cachedAdj, ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval, @@ -1794,8 +1799,7 @@ public class OomAdjuster { state.setSystemNoUi(false); } if (!state.isSystemNoUi()) { - if (mService.mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE - || state.isRunningRemoteAnimation()) { + if (isScreenOnOrAnimatingLocked(state)) { // screen on or animating, promote UI state.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI); state.setCurrentSchedulingGroup(SCHED_GROUP_TOP_APP); @@ -3281,8 +3285,10 @@ public class OomAdjuster { } else { setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST); } - initialSchedGroup = SCHED_GROUP_TOP_APP; - initialProcState = PROCESS_STATE_TOP; + if (isScreenOnOrAnimatingLocked(state)) { + initialSchedGroup = SCHED_GROUP_TOP_APP; + initialProcState = PROCESS_STATE_TOP; + } initialCapability = PROCESS_CAPABILITY_ALL; initialCached = false; } catch (Exception e) { diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index ca15dd79adbc..c6d6122aeed6 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -40,6 +40,7 @@ import android.app.GameModeInfo; import android.app.GameState; import android.app.IGameManagerService; import android.app.IGameModeListener; +import android.app.IGameStateListener; import android.app.StatsManager; import android.app.UidObserver; import android.content.BroadcastReceiver; @@ -148,6 +149,7 @@ public final class GameManagerService extends IGameManagerService.Stub { private final Object mLock = new Object(); private final Object mDeviceConfigLock = new Object(); private final Object mGameModeListenerLock = new Object(); + private final Object mGameStateListenerLock = new Object(); @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) final Handler mHandler; private final PackageManager mPackageManager; @@ -164,6 +166,8 @@ public final class GameManagerService extends IGameManagerService.Stub { // listener to caller uid map @GuardedBy("mGameModeListenerLock") private final ArrayMap<IGameModeListener, Integer> mGameModeListeners = new ArrayMap<>(); + @GuardedBy("mGameStateListenerLock") + private final ArrayMap<IGameStateListener, Integer> mGameStateListeners = new ArrayMap<>(); @Nullable private final GameServiceController mGameServiceController; private final Object mUidObserverLock = new Object(); @@ -352,6 +356,16 @@ public final class GameManagerService extends IGameManagerService.Stub { loadingBoostDuration); } } + synchronized (mGameStateListenerLock) { + for (IGameStateListener listener : mGameStateListeners.keySet()) { + try { + listener.onGameStateChanged(packageName, gameState, userId); + } catch (RemoteException ex) { + Slog.w(TAG, "Cannot notify game state change for listener added by " + + mGameStateListeners.get(listener)); + } + } + } break; } case CANCEL_GAME_LOADING_MODE: { @@ -1474,6 +1488,43 @@ public final class GameManagerService extends IGameManagerService.Stub { } /** + * Adds a game state listener. + */ + @Override + public void addGameStateListener(@NonNull IGameStateListener listener) { + try { + final IBinder listenerBinder = listener.asBinder(); + listenerBinder.linkToDeath(new DeathRecipient() { + @Override public void binderDied() { + removeGameStateListenerUnchecked(listener); + listenerBinder.unlinkToDeath(this, 0 /*flags*/); + } + }, 0 /*flags*/); + synchronized (mGameStateListenerLock) { + mGameStateListeners.put(listener, Binder.getCallingUid()); + } + } catch (RemoteException ex) { + Slog.e(TAG, + "Failed to link death recipient for IGameStateListener from caller " + + Binder.getCallingUid() + ", abandoned its listener registration", ex); + } + } + + /** + * Removes a game state listener. + */ + @Override + public void removeGameStateListener(@NonNull IGameStateListener listener) { + removeGameStateListenerUnchecked(listener); + } + + private void removeGameStateListenerUnchecked(IGameStateListener listener) { + synchronized (mGameStateListenerLock) { + mGameStateListeners.remove(listener); + } + } + + /** * Notified when boot is completed. */ @VisibleForTesting diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java index 06417d725e7e..f51b62d77ab9 100644 --- a/services/core/java/com/android/server/biometrics/Utils.java +++ b/services/core/java/com/android/server/biometrics/Utils.java @@ -66,7 +66,6 @@ import com.android.internal.R; import com.android.internal.widget.LockPatternUtils; import com.android.server.biometrics.sensors.BaseClientMonitor; -import java.util.ArrayList; import java.util.List; public class Utils { @@ -98,33 +97,6 @@ public class Utils { } /** - * Get the enabled HAL instances. If virtual is enabled and available it will be returned as - * the only instance, otherwise all other instances will be returned. - * - * @param context system context - * @param declaredInstances known instances - * @return filtered list of enabled instances - */ - @NonNull - public static List<String> filterAvailableHalInstances(@NonNull Context context, - @NonNull List<String> declaredInstances) { - if (declaredInstances.size() <= 1) { - return declaredInstances; - } - - final int virtualAt = declaredInstances.indexOf("virtual"); - if (isVirtualEnabled(context) && virtualAt != -1) { - return List.of(declaredInstances.get(virtualAt)); - } - - declaredInstances = new ArrayList<>(declaredInstances); - if (virtualAt != -1) { - declaredInstances.remove(virtualAt); - } - return declaredInstances; - } - - /** * Combines {@link PromptInfo#setDeviceCredentialAllowed(boolean)} with * {@link PromptInfo#setAuthenticators(int)}, as the former is not flexible enough. */ diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 28cb7d9445ee..7cc6940f4b9d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -871,19 +871,22 @@ public class FingerprintService extends SystemService { super.registerAuthenticators_enforcePermission(); mRegistry.registerAll(() -> { - final List<ServiceProvider> providers = new ArrayList<>(); - providers.addAll(getHidlProviders(hidlSensors)); List<String> aidlSensors = new ArrayList<>(); final String[] instances = mAidlInstanceNameSupplier.get(); if (instances != null) { aidlSensors.addAll(Lists.newArrayList(instances)); } - providers.addAll(getAidlProviders( - Utils.filterAvailableHalInstances(getContext(), aidlSensors))); + + final Pair<List<FingerprintSensorPropertiesInternal>, List<String>> + filteredInstances = filterAvailableHalInstances(hidlSensors, aidlSensors); + + final List<ServiceProvider> providers = new ArrayList<>(); + providers.addAll(getHidlProviders(filteredInstances.first)); + providers.addAll(getAidlProviders(filteredInstances.second)); + return providers; }); } - @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void addAuthenticatorsRegisteredCallback( @@ -1038,6 +1041,33 @@ public class FingerprintService extends SystemService { }); } + private Pair<List<FingerprintSensorPropertiesInternal>, List<String>> + filterAvailableHalInstances( + @NonNull List<FingerprintSensorPropertiesInternal> hidlInstances, + @NonNull List<String> aidlInstances) { + if ((hidlInstances.size() + aidlInstances.size()) <= 1) { + return new Pair(hidlInstances, aidlInstances); + } + + final int virtualAt = aidlInstances.indexOf("virtual"); + if (Utils.isVirtualEnabled(getContext())) { + if (virtualAt != -1) { + //only virtual instance should be returned + return new Pair(new ArrayList<>(), List.of(aidlInstances.get(virtualAt))); + } else { + Slog.e(TAG, "Could not find virtual interface while it is enabled"); + return new Pair(hidlInstances, aidlInstances); + } + } else { + //remove virtual instance + aidlInstances = new ArrayList<>(aidlInstances); + if (virtualAt != -1) { + aidlInstances.remove(virtualAt); + } + return new Pair(hidlInstances, aidlInstances); + } + } + @NonNull private List<ServiceProvider> getHidlProviders( @NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) { diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 02ee96a04b1f..7bda2c1fa5ab 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1175,6 +1175,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this, userId); resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE), false, this, userId); + resolver.registerContentObserver(Settings.Secure.getUriFor( + STYLUS_HANDWRITING_ENABLED), false, this); mRegistered = true; } @@ -1183,6 +1185,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD); final Uri accessibilityRequestingNoImeUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE); + final Uri stylusHandwritingEnabledUri = Settings.Secure.getUriFor( + STYLUS_HANDWRITING_ENABLED); synchronized (ImfLock.class) { if (showImeUri.equals(uri)) { mMenuController.updateKeyboardFromSettingsLocked(); @@ -1200,6 +1204,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub showCurrentInputImplicitLocked(mCurFocusedWindow, SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE); } + } else if (stylusHandwritingEnabledUri.equals(uri)) { + InputMethodManager.invalidateLocalStylusHandwritingAvailabilityCaches(); } else { boolean enabledChanged = false; String newEnabled = mSettings.getEnabledInputMethodsStr(); @@ -2363,7 +2369,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mCurVirtualDisplayToScreenMatrix = null; ImeTracker.forLogging().onFailed(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME); mCurStatsToken = null; - + InputMethodManager.invalidateLocalStylusHandwritingAvailabilityCaches(); mMenuController.hideInputMethodMenuLocked(); } } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index db3b267529e6..0c7d007dc48a 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -723,6 +723,14 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { mFlags |= WindowManager.TRANSIT_FLAG_INVISIBLE; return; } + // Activity doesn't need to capture snapshot if the starting window has associated to task. + if (wc.asActivityRecord() != null) { + final ActivityRecord activityRecord = wc.asActivityRecord(); + if (activityRecord.mStartingData != null + && activityRecord.mStartingData.mAssociatedTask != null) { + return; + } + } if (mContainerFreezer == null) { mContainerFreezer = new ScreenshotFreezer(); diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index d1b00a38701d..4ce150e03558 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -24,6 +24,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS; import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT; +import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT; import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS; @@ -1335,6 +1336,19 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub taskFragment.setAnimationParams(animationParams); break; } + case OP_TYPE_REORDER_TO_FRONT: { + final Task task = taskFragment.getTask(); + if (task != null) { + final TaskFragment topTaskFragment = task.getTaskFragment( + tf -> tf.asTask() == null); + if (topTaskFragment != null && topTaskFragment != taskFragment) { + final int index = task.mChildren.indexOf(topTaskFragment); + task.mChildren.remove(taskFragment); + task.mChildren.add(index, taskFragment); + } + } + break; + } } return effects; } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 991248aceb4b..6960da74dd1a 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -434,6 +434,10 @@ public final class SystemServer implements Dumpable { + "OnDevicePersonalizationSystemService$Lifecycle"; private static final String UPDATABLE_DEVICE_CONFIG_SERVICE_CLASS = "com.android.server.deviceconfig.DeviceConfigInit$Lifecycle"; + private static final String DEVICE_LOCK_SERVICE_CLASS = + "com.android.server.devicelock.DeviceLockService"; + private static final String DEVICE_LOCK_APEX_PATH = + "/apex/com.android.devicelock/javalib/service-devicelock.jar"; private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector"; @@ -2864,6 +2868,13 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(HEALTHCONNECT_MANAGER_SERVICE_CLASS); t.traceEnd(); + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_LOCK)) { + t.traceBegin("DeviceLockService"); + mSystemServiceManager.startServiceFromJar(DEVICE_LOCK_SERVICE_CLASS, + DEVICE_LOCK_APEX_PATH); + t.traceEnd(); + } + // These are needed to propagate to the runnable below. final NetworkManagementService networkManagementF = networkManagement; final NetworkPolicyManagerService networkPolicyF = networkPolicy; diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java index 4aba30a661ad..f660b42457db 100644 --- a/services/midi/java/com/android/server/midi/MidiService.java +++ b/services/midi/java/com/android/server/midi/MidiService.java @@ -16,7 +16,9 @@ package com.android.server.midi; +import android.Manifest; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothUuid; import android.content.BroadcastReceiver; @@ -48,6 +50,7 @@ import android.os.ParcelUuid; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.os.UserManager; import android.util.EventLog; import android.util.Log; @@ -81,6 +84,11 @@ import java.util.concurrent.atomic.AtomicLong; // 2. synchronized (mDeviceConnections) //TODO Introduce a single lock object to lock the whole state and avoid the requirement above. +// All users should be able to connect to USB and Bluetooth MIDI devices. +// All users can create can install an app that provides, a Virtual MIDI Device Service. +// Users can not open virtual MIDI devices created by other users. +// getDevices() surfaces devices that can be opened by that user. +// openDevice() rejects devices that are cannot be opened by that user. public class MidiService extends IMidiManager.Stub { public static class Lifecycle extends SystemService { @@ -97,10 +105,21 @@ public class MidiService extends IMidiManager.Stub { } @Override + @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS}, + anyOf = {Manifest.permission.QUERY_USERS, + Manifest.permission.CREATE_USERS, + Manifest.permission.MANAGE_USERS}) + public void onUserStarting(@NonNull TargetUser user) { + mMidiService.onStartOrUnlockUser(user, false /* matchDirectBootUnaware */); + } + + @Override + @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS}, + anyOf = {Manifest.permission.QUERY_USERS, + Manifest.permission.CREATE_USERS, + Manifest.permission.MANAGE_USERS}) public void onUserUnlocking(@NonNull TargetUser user) { - if (user.getUserIdentifier() == UserHandle.USER_SYSTEM) { - mMidiService.onUnlockUser(); - } + mMidiService.onStartOrUnlockUser(user, true /* matchDirectBootUnaware */); } } @@ -134,6 +153,7 @@ public class MidiService extends IMidiManager.Stub { private int mNextDeviceId = 1; private final PackageManager mPackageManager; + private final UserManager mUserManager; private static final String MIDI_LEGACY_STRING = "MIDI 1.0"; private static final String MIDI_UNIVERSAL_STRING = "MIDI 2.0"; @@ -159,21 +179,24 @@ public class MidiService extends IMidiManager.Stub { private final HashSet<ParcelUuid> mNonMidiUUIDs = new HashSet<ParcelUuid>(); // PackageMonitor for listening to package changes + // uid is the uid of the package so use getChangingUserId() to fetch the userId. private final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override + @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) public void onPackageAdded(String packageName, int uid) { - addPackageDeviceServers(packageName); + addPackageDeviceServers(packageName, getChangingUserId()); } @Override + @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) public void onPackageModified(String packageName) { - removePackageDeviceServers(packageName); - addPackageDeviceServers(packageName); + removePackageDeviceServers(packageName, getChangingUserId()); + addPackageDeviceServers(packageName, getChangingUserId()); } @Override public void onPackageRemoved(String packageName, int uid) { - removePackageDeviceServers(packageName); + removePackageDeviceServers(packageName, getChangingUserId()); } }; @@ -202,6 +225,10 @@ public class MidiService extends IMidiManager.Stub { return mUid; } + private int getUserId() { + return UserHandle.getUserId(mUid); + } + public void addListener(IMidiDeviceListener listener) { if (mListeners.size() >= MAX_LISTENERS_PER_CLIENT) { throw new SecurityException( @@ -219,8 +246,12 @@ public class MidiService extends IMidiManager.Stub { } } - public void addDeviceConnection(Device device, IMidiDeviceOpenCallback callback) { - Log.d(TAG, "addDeviceConnection() device:" + device); + @RequiresPermission(anyOf = {Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Manifest.permission.INTERACT_ACROSS_USERS, + Manifest.permission.INTERACT_ACROSS_PROFILES}) + public void addDeviceConnection(Device device, IMidiDeviceOpenCallback callback, + int userId) { + Log.d(TAG, "addDeviceConnection() device:" + device + " userId:" + userId); if (mDeviceConnections.size() >= MAX_CONNECTIONS_PER_CLIENT) { Log.i(TAG, "too many MIDI connections for UID = " + mUid); throw new SecurityException( @@ -228,7 +259,7 @@ public class MidiService extends IMidiManager.Stub { } DeviceConnection connection = new DeviceConnection(device, this, callback); mDeviceConnections.put(connection.getToken(), connection); - device.addDeviceConnection(connection); + device.addDeviceConnection(connection, userId); } // called from MidiService.closeDevice() @@ -251,8 +282,8 @@ public class MidiService extends IMidiManager.Stub { } public void deviceAdded(Device device) { - // ignore private devices that our client cannot access - if (!device.isUidAllowed(mUid)) return; + // ignore devices that our client cannot access + if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; MidiDeviceInfo deviceInfo = device.getDeviceInfo(); try { @@ -265,8 +296,8 @@ public class MidiService extends IMidiManager.Stub { } public void deviceRemoved(Device device) { - // ignore private devices that our client cannot access - if (!device.isUidAllowed(mUid)) return; + // ignore devices that our client cannot access + if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; MidiDeviceInfo deviceInfo = device.getDeviceInfo(); try { @@ -279,8 +310,8 @@ public class MidiService extends IMidiManager.Stub { } public void deviceStatusChanged(Device device, MidiDeviceStatus status) { - // ignore private devices that our client cannot access - if (!device.isUidAllowed(mUid)) return; + // ignore devices that our client cannot access + if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; try { for (IMidiDeviceListener listener : mListeners.values()) { @@ -354,6 +385,8 @@ public class MidiService extends IMidiManager.Stub { private final ServiceInfo mServiceInfo; // UID of device implementation private final int mUid; + // User Id of the app. Only used for virtual devices + private final int mUserId; // ServiceConnection for implementing Service (virtual devices only) // mServiceConnection is non-null when connected or attempting to connect to the service @@ -375,19 +408,24 @@ public class MidiService extends IMidiManager.Stub { private AtomicInteger mTotalOutputBytes = new AtomicInteger(); public Device(IMidiDeviceServer server, MidiDeviceInfo deviceInfo, - ServiceInfo serviceInfo, int uid) { + ServiceInfo serviceInfo, int uid, int userId) { mDeviceInfo = deviceInfo; mServiceInfo = serviceInfo; mUid = uid; + mUserId = userId; mBluetoothDevice = (BluetoothDevice)deviceInfo.getProperties().getParcelable( MidiDeviceInfo.PROPERTY_BLUETOOTH_DEVICE, android.bluetooth.BluetoothDevice.class);; setDeviceServer(server); } + @RequiresPermission(anyOf = {Manifest.permission.QUERY_USERS, + Manifest.permission.CREATE_USERS, + Manifest.permission.MANAGE_USERS}) public Device(BluetoothDevice bluetoothDevice) { mBluetoothDevice = bluetoothDevice; mServiceInfo = null; mUid = mBluetoothServiceUid; + mUserId = mUserManager.getMainUser().getIdentifier(); } private void setDeviceServer(IMidiDeviceServer server) { @@ -468,11 +506,22 @@ public class MidiService extends IMidiManager.Stub { return mUid; } + public int getUserId() { + return mUserId; + } + public boolean isUidAllowed(int uid) { return (!mDeviceInfo.isPrivate() || mUid == uid); } - public void addDeviceConnection(DeviceConnection connection) { + public boolean isUserIdAllowed(int userId) { + return (mDeviceInfo.getType() != MidiDeviceInfo.TYPE_VIRTUAL || mUserId == userId); + } + + @RequiresPermission(anyOf = {Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Manifest.permission.INTERACT_ACROSS_USERS, + Manifest.permission.INTERACT_ACROSS_PROFILES}) + public void addDeviceConnection(DeviceConnection connection, int userId) { Log.d(TAG, "addDeviceConnection() [A] connection:" + connection); synchronized (mDeviceConnections) { mDeviceConnectionsAdded.incrementAndGet(); @@ -537,8 +586,8 @@ public class MidiService extends IMidiManager.Stub { new ComponentName(mServiceInfo.packageName, mServiceInfo.name)); } - if (!mContext.bindService(intent, mServiceConnection, - Context.BIND_AUTO_CREATE)) { + if (!mContext.bindServiceAsUser(intent, mServiceConnection, + Context.BIND_AUTO_CREATE, UserHandle.of(mUserId))) { Log.e(TAG, "Unable to bind service: " + intent); setDeviceServer(null); mServiceConnection = null; @@ -886,6 +935,8 @@ public class MidiService extends IMidiManager.Stub { public MidiService(Context context) { mContext = context; mPackageManager = context.getPackageManager(); + mUserManager = mContext.getSystemService(UserManager.class); + mPackageMonitor.register(mContext, null, UserHandle.ALL, true); // TEMPORARY - Disable BTL-MIDI //FIXME - b/25689266 @@ -913,32 +964,41 @@ public class MidiService extends IMidiManager.Stub { // mNonMidiUUIDs.add(BluetoothUuid.BATTERY); } - private void onUnlockUser() { - mPackageMonitor.register(mContext, null, true); - + @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS}, + anyOf = {Manifest.permission.QUERY_USERS, + Manifest.permission.CREATE_USERS, + Manifest.permission.MANAGE_USERS}) + private void onStartOrUnlockUser(TargetUser user, boolean matchDirectBootUnaware) { + Log.d(TAG, "onStartOrUnlockUser " + user.getUserIdentifier() + " matchDirectBootUnaware: " + + matchDirectBootUnaware); Intent intent = new Intent(MidiDeviceService.SERVICE_INTERFACE); - List<ResolveInfo> resolveInfos = mPackageManager.queryIntentServices(intent, - PackageManager.GET_META_DATA); + int resolveFlags = PackageManager.GET_META_DATA; + if (matchDirectBootUnaware) { + resolveFlags |= PackageManager.MATCH_DIRECT_BOOT_UNAWARE; + } + List<ResolveInfo> resolveInfos = mPackageManager.queryIntentServicesAsUser(intent, + resolveFlags, user.getUserIdentifier()); if (resolveInfos != null) { int count = resolveInfos.size(); for (int i = 0; i < count; i++) { ServiceInfo serviceInfo = resolveInfos.get(i).serviceInfo; if (serviceInfo != null) { - addPackageDeviceServer(serviceInfo); + addPackageDeviceServer(serviceInfo, user.getUserIdentifier()); } } } - PackageInfo info; - try { - info = mPackageManager.getPackageInfo(MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, 0); - } catch (PackageManager.NameNotFoundException e) { - info = null; - } - if (info != null && info.applicationInfo != null) { - mBluetoothServiceUid = info.applicationInfo.uid; - } else { - mBluetoothServiceUid = -1; + if (user.getUserIdentifier() == mUserManager.getMainUser().getIdentifier()) { + PackageInfo info; + try { + info = mPackageManager.getPackageInfoAsUser( + MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, 0, user.getUserIdentifier()); + } catch (PackageManager.NameNotFoundException e) { + info = null; + } + if (info != null && info.applicationInfo != null) { + mBluetoothServiceUid = info.applicationInfo.uid; + } } } @@ -960,10 +1020,11 @@ public class MidiService extends IMidiManager.Stub { // Inform listener of the status of all known devices. private void updateStickyDeviceStatus(int uid, IMidiDeviceListener listener) { + int userId = UserHandle.getUserId(uid); synchronized (mDevicesByInfo) { for (Device device : mDevicesByInfo.values()) { - // ignore private devices that our client cannot access - if (device.isUidAllowed(uid)) { + // ignore devices that our client cannot access + if (device.isUidAllowed(uid) && device.isUserIdAllowed(userId)) { try { MidiDeviceStatus status = device.getDeviceStatus(); if (status != null) { @@ -989,10 +1050,11 @@ public class MidiService extends IMidiManager.Stub { public MidiDeviceInfo[] getDevicesForTransport(int transport) { ArrayList<MidiDeviceInfo> deviceInfos = new ArrayList<MidiDeviceInfo>(); int uid = Binder.getCallingUid(); + int userId = getCallingUserId(); synchronized (mDevicesByInfo) { for (Device device : mDevicesByInfo.values()) { - if (device.isUidAllowed(uid)) { + if (device.isUidAllowed(uid) && device.isUserIdAllowed(userId)) { // UMP devices have protocols that are not PROTOCOL_UNKNOWN if (transport == MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS) { if (device.getDeviceInfo().getDefaultProtocol() @@ -1029,6 +1091,9 @@ public class MidiService extends IMidiManager.Stub { if (!device.isUidAllowed(Binder.getCallingUid())) { throw new SecurityException("Attempt to open private device with wrong UID"); } + if (!device.isUserIdAllowed(getCallingUserId())) { + throw new SecurityException("Attempt to open virtual device with wrong user id"); + } } if (deviceInfo.getType() == MidiDeviceInfo.TYPE_USB) { @@ -1044,7 +1109,7 @@ public class MidiService extends IMidiManager.Stub { final long identity = Binder.clearCallingIdentity(); try { Log.i(TAG, "addDeviceConnection() [B] device:" + device); - client.addDeviceConnection(device, callback); + client.addDeviceConnection(device, callback, getCallingUserId()); } finally { Binder.restoreCallingIdentity(identity); } @@ -1106,7 +1171,7 @@ public class MidiService extends IMidiManager.Stub { final long identity = Binder.clearCallingIdentity(); try { Log.i(TAG, "addDeviceConnection() [C] device:" + device); - client.addDeviceConnection(device, callback); + client.addDeviceConnection(device, callback, getCallingUserId()); } finally { Binder.restoreCallingIdentity(identity); } @@ -1124,6 +1189,7 @@ public class MidiService extends IMidiManager.Stub { int numOutputPorts, String[] inputPortNames, String[] outputPortNames, Bundle properties, int type, int defaultProtocol) { int uid = Binder.getCallingUid(); + int userId = getCallingUserId(); if (type == MidiDeviceInfo.TYPE_USB && uid != Process.SYSTEM_UID) { throw new SecurityException("only system can create USB devices"); } else if (type == MidiDeviceInfo.TYPE_BLUETOOTH && uid != mBluetoothServiceUid) { @@ -1133,7 +1199,7 @@ public class MidiService extends IMidiManager.Stub { synchronized (mDevicesByInfo) { return addDeviceLocked(type, numInputPorts, numOutputPorts, inputPortNames, outputPortNames, properties, server, null, false, uid, - defaultProtocol); + defaultProtocol, userId); } } @@ -1210,7 +1276,8 @@ public class MidiService extends IMidiManager.Stub { private MidiDeviceInfo addDeviceLocked(int type, int numInputPorts, int numOutputPorts, String[] inputPortNames, String[] outputPortNames, Bundle properties, IMidiDeviceServer server, ServiceInfo serviceInfo, - boolean isPrivate, int uid, int defaultProtocol) { + boolean isPrivate, int uid, int defaultProtocol, int userId) { + Log.d(TAG, "addDeviceLocked()" + uid + " type:" + type); // Limit the number of devices per app. int deviceCountForApp = 0; @@ -1250,7 +1317,7 @@ public class MidiService extends IMidiManager.Stub { } } if (device == null) { - device = new Device(server, deviceInfo, serviceInfo, uid); + device = new Device(server, deviceInfo, serviceInfo, uid, userId); } mDevicesByInfo.put(deviceInfo, device); if (bluetoothDevice != null) { @@ -1281,12 +1348,14 @@ public class MidiService extends IMidiManager.Stub { } } - private void addPackageDeviceServers(String packageName) { + @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) + private void addPackageDeviceServers(String packageName, int userId) { PackageInfo info; try { - info = mPackageManager.getPackageInfo(packageName, - PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); + info = mPackageManager.getPackageInfoAsUser(packageName, + PackageManager.GET_SERVICES | PackageManager.GET_META_DATA + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "handlePackageUpdate could not find package " + packageName, e); return; @@ -1295,13 +1364,14 @@ public class MidiService extends IMidiManager.Stub { ServiceInfo[] services = info.services; if (services == null) return; for (int i = 0; i < services.length; i++) { - addPackageDeviceServer(services[i]); + addPackageDeviceServer(services[i], userId); } } private static final String[] EMPTY_STRING_ARRAY = new String[0]; - private void addPackageDeviceServer(ServiceInfo serviceInfo) { + private void addPackageDeviceServer(ServiceInfo serviceInfo, int userId) { + Log.d(TAG, "addPackageDeviceServer()" + userId); XmlResourceParser parser = null; try { @@ -1404,8 +1474,8 @@ public class MidiService extends IMidiManager.Stub { int uid; try { - ApplicationInfo appInfo = mPackageManager.getApplicationInfo( - serviceInfo.packageName, 0); + ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser( + serviceInfo.packageName, 0, userId); uid = appInfo.uid; } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "could not fetch ApplicationInfo for " @@ -1419,7 +1489,7 @@ public class MidiService extends IMidiManager.Stub { inputPortNames.toArray(EMPTY_STRING_ARRAY), outputPortNames.toArray(EMPTY_STRING_ARRAY), properties, null, serviceInfo, isPrivate, uid, - MidiDeviceInfo.PROTOCOL_UNKNOWN); + MidiDeviceInfo.PROTOCOL_UNKNOWN, userId); } // setting properties to null signals that we are no longer // processing a <device> @@ -1437,12 +1507,13 @@ public class MidiService extends IMidiManager.Stub { } } - private void removePackageDeviceServers(String packageName) { + private void removePackageDeviceServers(String packageName, int userId) { synchronized (mDevicesByInfo) { Iterator<Device> iterator = mDevicesByInfo.values().iterator(); while (iterator.hasNext()) { Device device = iterator.next(); - if (packageName.equals(device.getPackageName())) { + if (packageName.equals(device.getPackageName()) + && (device.getUserId() == userId)) { iterator.remove(); removeDeviceLocked(device); } @@ -1571,4 +1642,11 @@ public class MidiService extends IMidiManager.Stub { String extractUsbDeviceTag(String propertyName) { return propertyName.substring(propertyName.length() - MIDI_LEGACY_STRING.length()); } + + /** + * @return the user id of the calling user. + */ + private int getCallingUserId() { + return UserHandle.getUserId(Binder.getCallingUid()); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java index 212a243c6a9e..cd3a78ed5e17 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -52,6 +52,7 @@ import android.app.GameModeConfiguration; import android.app.GameModeInfo; import android.app.GameState; import android.app.IGameModeListener; +import android.app.IGameStateListener; import android.content.BroadcastReceiver; import android.content.Context; import android.content.ContextWrapper; @@ -1578,6 +1579,71 @@ public class GameManagerServiceTests { assertFalse(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); } + @Test + public void testAddGameStateListener() throws Exception { + mockModifyGameModeGranted(); + GameManagerService gameManagerService = + new GameManagerService(mMockContext, mTestLooper.getLooper()); + mockDeviceConfigAll(); + startUser(gameManagerService, USER_ID_1); + + IGameStateListener mockListener = Mockito.mock(IGameStateListener.class); + IBinder binder = Mockito.mock(IBinder.class); + when(mockListener.asBinder()).thenReturn(binder); + gameManagerService.addGameStateListener(mockListener); + verify(binder).linkToDeath(mDeathRecipientCaptor.capture(), anyInt()); + + mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_AUDIO); + GameState gameState = new GameState(true, GameState.MODE_NONE); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + assertFalse(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); + mTestLooper.dispatchAll(); + verify(mockListener, never()).onGameStateChanged(anyString(), any(), anyInt()); + + mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_GAME); + gameState = new GameState(true, GameState.MODE_NONE); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + assertTrue(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); + mTestLooper.dispatchAll(); + verify(mockListener).onGameStateChanged(mPackageName, gameState, USER_ID_1); + reset(mockListener); + + gameState = new GameState(false, GameState.MODE_CONTENT); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + mTestLooper.dispatchAll(); + verify(mockListener).onGameStateChanged(mPackageName, gameState, USER_ID_1); + reset(mockListener); + + mDeathRecipientCaptor.getValue().binderDied(); + verify(binder).unlinkToDeath(eq(mDeathRecipientCaptor.getValue()), anyInt()); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + assertTrue(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); + mTestLooper.dispatchAll(); + verify(mockListener, never()).onGameStateChanged(anyString(), any(), anyInt()); + } + + @Test + public void testRemoveGameStateListener() throws Exception { + mockModifyGameModeGranted(); + GameManagerService gameManagerService = + new GameManagerService(mMockContext, mTestLooper.getLooper()); + mockDeviceConfigAll(); + startUser(gameManagerService, USER_ID_1); + + IGameStateListener mockListener = Mockito.mock(IGameStateListener.class); + IBinder binder = Mockito.mock(IBinder.class); + when(mockListener.asBinder()).thenReturn(binder); + + gameManagerService.addGameStateListener(mockListener); + gameManagerService.removeGameStateListener(mockListener); + mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_GAME); + GameState gameState = new GameState(false, GameState.MODE_CONTENT); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + assertTrue(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); + mTestLooper.dispatchAll(); + verify(mockListener, never()).onGameStateChanged(anyString(), any(), anyInt()); + } + private List<String> readGameModeInterventionList() throws Exception { final File interventionFile = new File(InstrumentationRegistry.getContext().getFilesDir(), "system/game_mode_intervention.list"); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index 99ab715ab987..54b935132957 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -26,6 +26,7 @@ import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT; +import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT; import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS; @@ -1590,6 +1591,46 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { assertEquals(taskFragmentBounds, mTaskFragment.getBounds()); } + @Test + public void testApplyTransaction_reorderTaskFragmentToFront() { + final Task task = createTask(mDisplayContent); + // Create a TaskFragment. + final IBinder token0 = new Binder(); + final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setFragmentToken(token0) + .setOrganizer(mOrganizer) + .createActivityCount(1) + .build(); + // Create another TaskFragment + final IBinder token1 = new Binder(); + final TaskFragment tf1 = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setFragmentToken(token1) + .setOrganizer(mOrganizer) + .createActivityCount(1) + .build(); + // Create a non-embedded Activity on top. + final ActivityRecord topActivity = new ActivityBuilder(mAtm) + .setTask(task) + .build(); + mWindowOrganizerController.mLaunchTaskFragments.put(token0, tf0); + mWindowOrganizerController.mLaunchTaskFragments.put(token1, tf1); + + // Reorder TaskFragment to front + final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( + OP_TYPE_REORDER_TO_FRONT).build(); + mTransaction.addTaskFragmentOperation(token0, operation); + assertApplyTransactionAllowed(mTransaction); + + // Ensure the non-embedded activity still on top. + assertEquals(topActivity, task.getTopChild().asActivityRecord()); + + // Ensure the TaskFragment is moved to front. + final TaskFragment frontMostTaskFragment = task.getTaskFragment(tf -> tf.asTask() == null); + assertEquals(frontMostTaskFragment, tf0); + } + /** * Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls * {@link WindowOrganizerController#applyTransaction(WindowContainerTransaction)} to apply the |