Merge "Add a getter method for expectedPresentationTimeNanos"
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java b/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java
index 8a5d094..ae86afb 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java
@@ -123,21 +123,21 @@
if (oldDetails == null) {
if (jobStatus.startedAsUserInitiatedJob) {
Counter.logIncrementWithUid(
- "job_scheduler.value_cntr_w_uid_initial_setNotification_call_required",
+ "job_scheduler.value_cntr_w_uid_initial_set_notification_call_required",
jobStatus.getUid());
} else {
Counter.logIncrementWithUid(
- "job_scheduler.value_cntr_w_uid_initial_setNotification_call_optional",
+ "job_scheduler.value_cntr_w_uid_initial_set_notification_call_optional",
jobStatus.getUid());
}
} else {
if (jobStatus.startedAsUserInitiatedJob) {
Counter.logIncrementWithUid(
- "job_scheduler.value_cntr_w_uid_subsequent_setNotification_call_required",
+ "job_scheduler.value_cntr_w_uid_subsequent_set_notification_call_required",
jobStatus.getUid());
} else {
Counter.logIncrementWithUid(
- "job_scheduler.value_cntr_w_uid_subsequent_setNotification_call_optional",
+ "job_scheduler.value_cntr_w_uid_subsequent_set_notification_call_optional",
jobStatus.getUid());
}
if (oldDetails.notificationId != notificationId) {
@@ -145,7 +145,7 @@
removeNotificationAssociation(hostingContext, JobParameters.STOP_REASON_UNDEFINED,
jobStatus);
Counter.logIncrementWithUid(
- "job_scheduler.value_cntr_w_uid_setNotification_changed_notification_ids",
+ "job_scheduler.value_cntr_w_uid_set_notification_changed_notification_ids",
jobStatus.getUid());
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 0b08b6f..5f795b6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -1331,7 +1331,7 @@
// FINISHED/NO-RETRY.
onSlowAppResponseLocked(/* reschedule */ false, /* updateStopReasons */ true,
/* texCounterMetricId */
- "job_scheduler.value_cntr_w_uid_slow_app_response_onStartJob",
+ "job_scheduler.value_cntr_w_uid_slow_app_response_on_start_job",
/* debugReason */ "timed out while starting",
/* anrMessage */ "No response to onStartJob",
CompatChanges.isChangeEnabled(ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES,
@@ -1343,7 +1343,7 @@
// other reason.
onSlowAppResponseLocked(/* reschedule */ true, /* updateStopReasons */ false,
/* texCounterMetricId */
- "job_scheduler.value_cntr_w_uid_slow_app_response_onStopJob",
+ "job_scheduler.value_cntr_w_uid_slow_app_response_on_stop_job",
/* debugReason */ "timed out while stopping",
/* anrMessage */ "No response to onStopJob",
CompatChanges.isChangeEnabled(ANR_PRE_UDC_APIS_ON_SLOW_RESPONSES,
@@ -1400,7 +1400,7 @@
} else if (mAwaitingNotification) {
onSlowAppResponseLocked(/* reschedule */ true, /* updateStopReasons */ true,
/* texCounterMetricId */
- "job_scheduler.value_cntr_w_uid_slow_app_response_setNotification",
+ "job_scheduler.value_cntr_w_uid_slow_app_response_set_notification",
/* debugReason */ "timed out while stopping",
/* anrMessage */ "required notification not provided",
/* triggerAnr */ true);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a360fb5..f21851f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5462,7 +5462,7 @@
}
private void updateRenderHdrSdrRatio() {
- mRenderHdrSdrRatio = mDisplay.getHdrSdrRatio();
+ mRenderHdrSdrRatio = Math.min(mDesiredHdrSdrRatio, mDisplay.getHdrSdrRatio());
mUpdateHdrSdrRatioInfo = true;
}
@@ -5490,22 +5490,14 @@
mHdrSdrRatioChangedListener = null;
} else {
mHdrSdrRatioChangedListener = display -> {
- setTargetHdrSdrRatio(display.getHdrSdrRatio());
+ updateRenderHdrSdrRatio();
+ invalidate();
};
mDisplay.registerHdrSdrRatioChangedListener(mExecutor, mHdrSdrRatioChangedListener);
}
}
}
- /** happylint */
- public void setTargetHdrSdrRatio(float ratio) {
- if (mRenderHdrSdrRatio != ratio) {
- mRenderHdrSdrRatio = ratio;
- mUpdateHdrSdrRatioInfo = true;
- invalidate();
- }
- }
-
@Override
public void requestChildFocus(View child, View focused) {
if (DEBUG_INPUT_RESIZE) {
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 59238b4..d2a16a3 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -550,6 +550,16 @@
}
/**
+ * Updates the callsites of all the surfaces in this transition, which aids in the debugging of
+ * lingering surfaces.
+ */
+ public void setUnreleasedWarningCallSiteForAllSurfaces(String callsite) {
+ for (int i = mChanges.size() - 1; i >= 0; --i) {
+ mChanges.get(i).getLeash().setUnreleasedWarningCallSite(callsite);
+ }
+ }
+
+ /**
* Makes a copy of this as if it were parcel'd and unparcel'd. This implies that surfacecontrol
* refcounts are incremented which allows the "remote" receiver to release them without breaking
* the caller's references. Use this only if you need to "send" this to a local function which
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index d6e1a82..467e9c7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -375,16 +375,15 @@
insetsState.getDisplayFrame(),
WindowInsets.Type.navigationBars(),
false /* ignoreVisibility */);
- outInsets.set(insets.left, insets.top, insets.right, insets.bottom);
int position = navigationBarPosition(res, displayWidth, displayHeight, displayRotation);
int navBarSize =
getNavigationBarSize(res, position, displayWidth > displayHeight, uiMode);
if (position == NAV_BAR_BOTTOM) {
- outInsets.bottom = Math.max(outInsets.bottom , navBarSize);
+ outInsets.bottom = Math.max(insets.bottom , navBarSize);
} else if (position == NAV_BAR_RIGHT) {
- outInsets.right = Math.max(outInsets.right , navBarSize);
+ outInsets.right = Math.max(insets.right , navBarSize);
} else if (position == NAV_BAR_LEFT) {
- outInsets.left = Math.max(outInsets.left , navBarSize);
+ outInsets.left = Math.max(insets.left , navBarSize);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 7cc2b9e..34a6e0a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -103,13 +103,13 @@
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
+import java.util.Optional;
+
import dagger.BindsOptionalOf;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
-import java.util.Optional;
-
/**
* Provides basic dependencies from {@link com.android.wm.shell}, these dependencies are only
* accessible from components within the WM subcomponent (can be explicitly exposed to the
@@ -556,13 +556,15 @@
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
@ShellAnimationThread ShellExecutor animExecutor,
- ShellCommandHandler shellCommandHandler) {
+ ShellCommandHandler shellCommandHandler,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
if (!context.getResources().getBoolean(R.bool.config_registerShellTransitionsOnInit)) {
// TODO(b/238217847): Force override shell init if registration is disabled
shellInit = new ShellInit(mainExecutor);
}
return new Transitions(context, shellInit, shellController, organizer, pool,
- displayController, mainExecutor, mainHandler, animExecutor, shellCommandHandler);
+ displayController, mainExecutor, mainHandler, animExecutor, shellCommandHandler,
+ rootTaskDisplayAreaOrganizer);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index a02e066..57111b9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -2521,6 +2521,7 @@
// handling to the mixed-handler to deal with splitting it up.
if (mMixedHandler.animatePendingSplitWithDisplayChange(transition, info,
startTransaction, finishTransaction, finishCallback)) {
+ mSplitLayout.update(startTransaction);
return true;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 715f835..3bf278c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -58,7 +58,6 @@
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_OPEN;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
-import static com.android.wm.shell.transition.TransitionAnimationHelper.addBackgroundToTransition;
import static com.android.wm.shell.transition.TransitionAnimationHelper.edgeExtendWindow;
import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet;
import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
@@ -76,6 +75,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
@@ -103,6 +103,7 @@
import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
@@ -137,6 +138,7 @@
private final Rect mInsets = new Rect(0, 0, 0, 0);
private float mTransitionAnimationScaleSetting = 1.0f;
+ private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
private final int mCurrentUserId;
private Drawable mEnterpriseThumbnailDrawable;
@@ -157,7 +159,8 @@
@NonNull DisplayController displayController,
@NonNull TransactionPool transactionPool,
@NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler,
- @NonNull ShellExecutor animExecutor) {
+ @NonNull ShellExecutor animExecutor,
+ @NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer) {
mDisplayController = displayController;
mTransactionPool = transactionPool;
mContext = context;
@@ -168,6 +171,7 @@
mCurrentUserId = UserHandle.myUserId();
mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
shellInit.addInitCallback(this::onInit, this);
+ mRootTDAOrganizer = rootTDAOrganizer;
}
private void onInit() {
@@ -510,10 +514,8 @@
}
if (backgroundColorForTransition != 0) {
- for (int i = 0; i < info.getRootCount(); ++i) {
- addBackgroundToTransition(info.getRoot(i).getLeash(), backgroundColorForTransition,
- startTransaction, finishTransaction);
- }
+ addBackgroundColorOnTDA(info, backgroundColorForTransition, startTransaction,
+ finishTransaction);
}
if (postStartTransactionCallbacks.size() > 0) {
@@ -543,6 +545,28 @@
return true;
}
+ private void addBackgroundColorOnTDA(@NonNull TransitionInfo info,
+ @ColorInt int color, @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction) {
+ final Color bgColor = Color.valueOf(color);
+ final float[] colorArray = new float[] { bgColor.red(), bgColor.green(), bgColor.blue() };
+
+ for (int i = 0; i < info.getRootCount(); ++i) {
+ final int displayId = info.getRoot(i).getDisplayId();
+ final SurfaceControl.Builder colorLayerBuilder = new SurfaceControl.Builder()
+ .setName("animation-background")
+ .setCallsite("DefaultTransitionHandler")
+ .setColorLayer();
+
+ mRootTDAOrganizer.attachToDisplayArea(displayId, colorLayerBuilder);
+ final SurfaceControl backgroundSurface = colorLayerBuilder.build();
+ startTransaction.setColor(backgroundSurface, colorArray)
+ .setLayer(backgroundSurface, -1)
+ .show(backgroundSurface);
+ finishTransaction.remove(backgroundSurface);
+ }
+ }
+
private static boolean isDreamTransition(@NonNull TransitionInfo info) {
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
@@ -696,9 +720,10 @@
if (a != null) {
if (!a.isInitialized()) {
- final int width = endBounds.width();
- final int height = endBounds.height();
- a.initialize(width, height, width, height);
+ final Rect animationRange = TransitionUtil.isClosingType(changeMode)
+ ? change.getStartAbsBounds() : change.getEndAbsBounds();
+ a.initialize(animationRange.width(), animationRange.height(),
+ endBounds.width(), endBounds.height());
}
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
@@ -886,17 +911,18 @@
}
private static void applyTransformation(long time, SurfaceControl.Transaction t,
- SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix,
+ SurfaceControl leash, Animation anim, Transformation tmpTransformation, float[] matrix,
Point position, float cornerRadius, @Nullable Rect immutableClipRect) {
- anim.getTransformation(time, transformation);
+ tmpTransformation.clear();
+ anim.getTransformation(time, tmpTransformation);
if (position != null) {
- transformation.getMatrix().postTranslate(position.x, position.y);
+ tmpTransformation.getMatrix().postTranslate(position.x, position.y);
}
- t.setMatrix(leash, transformation.getMatrix(), matrix);
- t.setAlpha(leash, transformation.getAlpha());
+ t.setMatrix(leash, tmpTransformation.getMatrix(), matrix);
+ t.setAlpha(leash, tmpTransformation.getAlpha());
final Rect clipRect = immutableClipRect == null ? null : new Rect(immutableClipRect);
- Insets extensionInsets = Insets.min(transformation.getInsets(), Insets.NONE);
+ Insets extensionInsets = Insets.min(tmpTransformation.getInsets(), Insets.NONE);
if (!extensionInsets.equals(Insets.NONE) && clipRect != null && !clipRect.isEmpty()) {
// Clip out any overflowing edge extension
clipRect.inset(extensionInsets);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index de20c2d..cdc82ea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -69,6 +69,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.RemoteCallable;
@@ -265,7 +266,8 @@
@NonNull Handler mainHandler,
@NonNull ShellExecutor animExecutor) {
this(context, shellInit, shellController, organizer, pool, displayController, mainExecutor,
- mainHandler, animExecutor, null);
+ mainHandler, animExecutor, null,
+ new RootTaskDisplayAreaOrganizer(mainExecutor, context));
}
public Transitions(@NonNull Context context,
@@ -277,7 +279,8 @@
@NonNull ShellExecutor mainExecutor,
@NonNull Handler mainHandler,
@NonNull ShellExecutor animExecutor,
- @Nullable ShellCommandHandler shellCommandHandler) {
+ @Nullable ShellCommandHandler shellCommandHandler,
+ @NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer) {
mOrganizer = organizer;
mContext = context;
mMainExecutor = mainExecutor;
@@ -285,7 +288,7 @@
mDisplayController = displayController;
mPlayerImpl = new TransitionPlayerImpl();
mDefaultTransitionHandler = new DefaultTransitionHandler(context, shellInit,
- displayController, pool, mainExecutor, mainHandler, animExecutor);
+ displayController, pool, mainExecutor, mainHandler, animExecutor, rootTDAOrganizer);
mRemoteTransitionHandler = new RemoteTransitionHandler(mMainExecutor);
mShellController = shellController;
// The very last handler (0 in the list) should be the default one.
@@ -641,6 +644,7 @@
@VisibleForTesting
void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
+ info.setUnreleasedWarningCallSiteForAllSurfaces("Transitions.onTransitionReady");
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s",
transitionToken, info);
final int activeIdx = findByToken(mPendingTransitions, transitionToken);
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
new file mode 100644
index 0000000..f7ce870
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2022 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.wm.shell.flicker.pip
+
+import android.app.Instrumentation
+import android.os.SystemClock
+import android.platform.test.annotations.Presubmit
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.FlickerTest
+import android.tools.device.flicker.legacy.FlickerTestFactory
+import android.tools.device.helpers.WindowUtils
+import android.tools.device.traces.parsers.toFlickerComponent
+import androidx.test.filters.RequiresDevice
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.BySelector
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME
+import org.junit.Assume
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test entering pip from an app via auto-enter property when navigating to home from split screen.
+ *
+ * To run this test: `atest WMShellFlickerTests:AutoEnterPipOnGoToHomeTest`
+ *
+ * Actions:
+ * ```
+ * Launch an app in full screen
+ * Select "Auto-enter PiP" radio button
+ * Open all apps and drag another app icon to enter split screen
+ * Press Home button or swipe up to go Home and put [pipApp] in pip mode
+ * ```
+ *
+ * Notes:
+ * ```
+ * 1. All assertions are inherited from [EnterPipTest]
+ * 2. Part of the test setup occurs automatically via
+ * [android.tools.device.flicker.legacy.runner.TransitionRunner],
+ * including configuring navigation mode, initial orientation and ensuring no
+ * apps are running before setup
+ * ```
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class AutoEnterPipFromSplitScreenOnGoToHomeTest(flicker: FlickerTest) :
+ AutoEnterPipOnGoToHomeTest(flicker) {
+ private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
+ /** Second app used to enter split screen mode */
+ protected val secondAppForSplitScreen = getSplitScreenApp(instrumentation)
+ fun getSplitScreenApp(instrumentation: Instrumentation): StandardAppHelper =
+ SimpleAppHelper(
+ instrumentation,
+ ActivityOptions.SplitScreen.Primary.LABEL,
+ ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()
+ )
+
+ /** Defines the transition used to run the test */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ setup {
+ secondAppForSplitScreen.launchViaIntent(wmHelper)
+ pipApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ enterSplitScreen()
+ // wait until split screen is established
+ wmHelper
+ .StateSyncBuilder()
+ .withWindowSurfaceAppeared(pipApp)
+ .withWindowSurfaceAppeared(secondAppForSplitScreen)
+ .withSplitDividerVisible()
+ .waitForAndVerify()
+ pipApp.enableAutoEnterForPipActivity()
+ }
+ teardown {
+ // close gracefully so that onActivityUnpinned() can be called before force exit
+ pipApp.closePipWindow(wmHelper)
+ pipApp.exit(wmHelper)
+ secondAppForSplitScreen.exit(wmHelper)
+ }
+ transitions { tapl.goHome() }
+ }
+
+ // TODO(b/285400227) merge the code in a common utility - this is copied from SplitScreenUtils
+ private val TIMEOUT_MS = 3_000L
+ private val overviewSnapshotSelector: BySelector
+ get() = By.res(LAUNCHER_UI_PACKAGE_NAME, "snapshot")
+ private fun enterSplitScreen() {
+ // Note: The initial split position in landscape is different between tablet and phone.
+ // In landscape, tablet will let the first app split to right side, and phone will
+ // split to left side.
+ if (tapl.isTablet) {
+ // TAPL's currentTask on tablet is sometimes not what we expected if the overview
+ // contains more than 3 task views. We need to use uiautomator directly to find the
+ // second task to split.
+ tapl.workspace.switchToOverview().overviewActions.clickSplit()
+ val snapshots = tapl.device.wait(Until.findObjects(overviewSnapshotSelector),
+ TIMEOUT_MS)
+ if (snapshots == null || snapshots.size < 1) {
+ error("Fail to find a overview snapshot to split.")
+ }
+
+ // Find the second task in the upper right corner in split select mode by sorting
+ // 'left' in descending order and 'top' in ascending order.
+ snapshots.sortWith { t1: UiObject2, t2: UiObject2 ->
+ t2.getVisibleBounds().left - t1.getVisibleBounds().left
+ }
+ snapshots.sortWith { t1: UiObject2, t2: UiObject2 ->
+ t1.getVisibleBounds().top - t2.getVisibleBounds().top
+ }
+ snapshots[0].click()
+ } else {
+ tapl.workspace
+ .switchToOverview()
+ .currentTask
+ .tapMenu()
+ .tapSplitMenuItem()
+ .currentTask
+ .open()
+ }
+ SystemClock.sleep(TIMEOUT_MS)
+ }
+
+ @Presubmit
+ @Test
+ override fun pipOverlayLayerAppearThenDisappear() {
+ // when entering from split screen we use alpha animation, without overlay
+ }
+
+ @Presubmit
+ @Test
+ override fun pipLayerOrOverlayRemainInsideVisibleBounds() {
+ // when entering from split screen we use alpha animation, without overlay
+ }
+
+ @Presubmit
+ @Test
+ override fun pipLayerReduces() {
+ // when entering from split screen we use alpha animation, so there is no size change
+ Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
+ super.pipLayerReduces()
+ }
+
+ @Presubmit
+ @Test
+ override fun pipAppLayerAlwaysVisible() {
+ // pip layer in gesture nav will disappear during transition with alpha animation
+ Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
+ super.pipAppLayerAlwaysVisible()
+ }
+
+ @Presubmit
+ @Test
+ override fun pipWindowRemainInsideVisibleBounds() {
+ if (tapl.isTablet) {
+ flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) }
+ } else {
+ // on phones home does not rotate in landscape, PiP enters back to portrait
+ // orientation so use display bounds from that orientation for assertion
+ flicker.assertWmVisibleRegion(pipApp) { coversAtMost(portraitDisplayBounds) }
+ }
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTest> {
+ return FlickerTestFactory.nonRotationTests(
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL)
+ )
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index ef7bedf..b95732e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -53,7 +53,7 @@
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class AutoEnterPipOnGoToHomeTest(flicker: FlickerTest) : EnterPipViaAppUiButtonTest(flicker) {
+open class AutoEnterPipOnGoToHomeTest(flicker: FlickerTest) : EnterPipViaAppUiButtonTest(flicker) {
override val thisTransition: FlickerBuilder.() -> Unit = {
transitions { tapl.goHome() }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
index 95121de..cdbdb85 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
@@ -62,7 +62,7 @@
*/
@Presubmit
@Test
- fun pipWindowRemainInsideVisibleBounds() {
+ open fun pipWindowRemainInsideVisibleBounds() {
flicker.assertWmVisibleRegion(pipApp) { coversAtMost(displayBounds) }
}
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
index 3394dd0..e8adfaf 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategy.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -134,9 +134,11 @@
/**
* @hide
- * @param audioAttributes to identify AudioProductStrategy with
- * @return legacy stream type associated with matched AudioProductStrategy
- * Defaults to STREAM_MUSIC if no match is found, or if matches is STREAM_DEFAULT
+ * @param audioAttributes to identify {@link AudioProductStrategy} with
+ * @return legacy stream type associated with matched {@link AudioProductStrategy}. If no
+ * strategy found or found {@link AudioProductStrategy} does not have associated
+ * legacy stream (i.e. associated with {@link AudioSystem#STREAM_DEFAULT}) defaults
+ * to {@link AudioSystem#STREAM_MUSIC}
*/
public static int getLegacyStreamTypeForStrategyWithAudioAttributes(
@NonNull AudioAttributes audioAttributes) {
@@ -147,9 +149,9 @@
int streamType = productStrategy.getLegacyStreamTypeForAudioAttributes(
audioAttributes);
if (streamType == AudioSystem.STREAM_DEFAULT) {
- Log.w(TAG, "Attributes " + audioAttributes.toString() + " ported by strategy "
- + productStrategy.getId() + " has no stream type associated, "
- + "DO NOT USE STREAM TO CONTROL THE VOLUME");
+ Log.w(TAG, "Attributes " + audioAttributes + " supported by strategy "
+ + productStrategy.getId() + " have no associated stream type, "
+ + "therefore falling back to STREAM_MUSIC");
return AudioSystem.STREAM_MUSIC;
}
if (streamType < AudioSystem.getNumStreamTypes()) {
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
index c52fde6..4eaa39b 100644
--- a/packages/PackageInstaller/res/values/strings.xml
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -38,11 +38,11 @@
<!-- Message for updating an existing app [CHAR LIMIT=NONE] -->
<string name="install_confirm_question_update">Do you want to update this app?</string>
<!-- Message for updating an existing app with update owner reminder [CHAR LIMIT=NONE] -->
- <string name="install_confirm_question_update_owner_reminder" product="tablet">Update this app from <xliff:g id="new_update_owner">%1$s</xliff:g>?\n\nThis app normally receives updates from <xliff:g id="existing_update_owner">%2$s</xliff:g>. By updating from a different source, you may receive future updates from any source on your tablet. App functionality may change.</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tablet"><p>Update this app from <b><xliff:g id="new_update_owner">%1$s</xliff:g></b>\?</p><p>This app normally receives updates from <b><xliff:g id="existing_update_owner">%2$s</xliff:g></b>. By updating from a different source, you may receive future updates from any source on your tablet. App functionality may change.</p></string>
<!-- Message for updating an existing app with update owner reminder [CHAR LIMIT=NONE] -->
- <string name="install_confirm_question_update_owner_reminder" product="tv">Update this app from <xliff:g id="new_update_owner">%1$s</xliff:g>?\n\nThis app normally receives updates from <xliff:g id="existing_update_owner">%2$s</xliff:g>. By updating from a different source, you may receive future updates from any source on your TV. App functionality may change.</string>
+ <string name="install_confirm_question_update_owner_reminder" product="tv"><p>Update this app from <b><xliff:g id="new_update_owner">%1$s</xliff:g></b>\?</p><p>This app normally receives updates from <b><xliff:g id="existing_update_owner">%2$s</xliff:g></b>. By updating from a different source, you may receive future updates from any source on your TV. App functionality may change.</p></string>
<!-- Message for updating an existing app with update owner reminder [CHAR LIMIT=NONE] -->
- <string name="install_confirm_question_update_owner_reminder" product="default">Update this app from <xliff:g id="new_update_owner">%1$s</xliff:g>?\n\nThis app normally receives updates from <xliff:g id="existing_update_owner">%2$s</xliff:g>. By updating from a different source, you may receive future updates from any source on your phone. App functionality may change.</string>
+ <string name="install_confirm_question_update_owner_reminder" product="default"><p>Update this app from <b><xliff:g id="new_update_owner">%1$s</xliff:g></b>\?</p><p>This app normally receives updates from <b><xliff:g id="existing_update_owner">%2$s</xliff:g></b>. By updating from a different source, you may receive future updates from any source on your phone. App functionality may change.</p></string>
<!-- [CHAR LIMIT=100] -->
<string name="install_failed">App not installed.</string>
<!-- Reason displayed when installation fails because the package was blocked
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index e071c11..411f1fa 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -49,6 +49,8 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.text.Html;
+import android.text.Spanned;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -144,9 +146,12 @@
final CharSequence requestedUpdateOwnerLabel = getApplicationLabel(mCallingPackage);
if (!TextUtils.isEmpty(existingUpdateOwnerLabel)
&& mPendingUserActionReason == PackageInstaller.REASON_REMIND_OWNERSHIP) {
- viewToEnable.setText(
+ String updateOwnerString =
getString(R.string.install_confirm_question_update_owner_reminder,
- requestedUpdateOwnerLabel, existingUpdateOwnerLabel));
+ requestedUpdateOwnerLabel, existingUpdateOwnerLabel);
+ Spanned styledUpdateOwnerString =
+ Html.fromHtml(updateOwnerString, Html.FROM_HTML_MODE_LEGACY);
+ viewToEnable.setText(styledUpdateOwnerString);
mOk.setText(R.string.update_anyway);
} else {
mOk.setText(R.string.update);
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/AnimatedNavHost.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/AnimatedNavHost.kt
index 81bbc24..57bb838 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/AnimatedNavHost.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/AnimatedNavHost.kt
@@ -18,7 +18,7 @@
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
import androidx.compose.animation.AnimatedContent
-import androidx.compose.animation.AnimatedContentTransitionScope
+import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.ContentTransform
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
@@ -78,10 +78,14 @@
modifier: Modifier = Modifier,
contentAlignment: Alignment = Alignment.Center,
route: String? = null,
- enterTransition: (AnimatedScope.() -> EnterTransition) = { fadeIn(animationSpec = tween(700)) },
- exitTransition: (AnimatedScope.() -> ExitTransition) = { fadeOut(animationSpec = tween(700)) },
- popEnterTransition: (AnimatedScope.() -> EnterTransition) = enterTransition,
- popExitTransition: (AnimatedScope.() -> ExitTransition) = exitTransition,
+ enterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition) =
+ { fadeIn(animationSpec = tween(700)) },
+ exitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition) =
+ { fadeOut(animationSpec = tween(700)) },
+ popEnterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition) =
+ enterTransition,
+ popExitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition) =
+ exitTransition,
builder: NavGraphBuilder.() -> Unit
) {
AnimatedNavHost(
@@ -119,10 +123,14 @@
graph: NavGraph,
modifier: Modifier = Modifier,
contentAlignment: Alignment = Alignment.Center,
- enterTransition: (AnimatedScope.() -> EnterTransition) = { fadeIn(animationSpec = tween(700)) },
- exitTransition: (AnimatedScope.() -> ExitTransition) = { fadeOut(animationSpec = tween(700)) },
- popEnterTransition: (AnimatedScope.() -> EnterTransition) = enterTransition,
- popExitTransition: (AnimatedScope.() -> ExitTransition) = exitTransition,
+ enterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition) =
+ { fadeIn(animationSpec = tween(700)) },
+ exitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition) =
+ { fadeOut(animationSpec = tween(700)) },
+ popEnterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition) =
+ enterTransition,
+ popExitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition) =
+ exitTransition,
) {
val lifecycleOwner = LocalLifecycleOwner.current
@@ -160,7 +168,7 @@
val backStackEntry = visibleEntries.lastOrNull()
if (backStackEntry != null) {
- val finalEnter: AnimatedScope.() -> EnterTransition = {
+ val finalEnter: AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition = {
val targetDestination = targetState.destination as AnimatedComposeNavigator.Destination
if (composeNavigator.isPop.value) {
@@ -174,7 +182,7 @@
}
}
- val finalExit: AnimatedScope.() -> ExitTransition = {
+ val finalExit: AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition = {
val initialDestination =
initialState.destination as AnimatedComposeNavigator.Destination
@@ -237,16 +245,19 @@
DialogHost(dialogNavigator)
}
-@OptIn(ExperimentalAnimationApi::class)
-internal typealias AnimatedScope = AnimatedContentTransitionScope<NavBackStackEntry>
+@ExperimentalAnimationApi
+internal val enterTransitions =
+ mutableMapOf<String?,
+ (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?)?>()
@ExperimentalAnimationApi
-internal val enterTransitions = mutableMapOf<String?, (AnimatedScope.() -> EnterTransition?)?>()
+internal val exitTransitions =
+ mutableMapOf<String?, (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?)?>()
@ExperimentalAnimationApi
-internal val exitTransitions = mutableMapOf<String?, (AnimatedScope.() -> ExitTransition?)?>()
-@ExperimentalAnimationApi
-internal val popEnterTransitions = mutableMapOf<String?, (AnimatedScope.() -> EnterTransition?)?>()
+internal val popEnterTransitions =
+ mutableMapOf<String?, (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?)?>()
@ExperimentalAnimationApi
-internal val popExitTransitions = mutableMapOf<String?, (AnimatedScope.() -> ExitTransition?)?>()
+internal val popExitTransitions =
+ mutableMapOf<String?, (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?)?>()
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavGraphBuilder.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavGraphBuilder.kt
index bf92f5d..9e58603 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavGraphBuilder.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavGraphBuilder.kt
@@ -16,6 +16,7 @@
package com.android.settingslib.spa.framework.compose
+import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
@@ -24,7 +25,9 @@
import androidx.navigation.NamedNavArgument
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavDeepLink
+import androidx.navigation.NavGraph
import androidx.navigation.NavGraphBuilder
+import androidx.navigation.compose.navigation
import androidx.navigation.get
/**
@@ -44,10 +47,14 @@
route: String,
arguments: List<NamedNavArgument> = emptyList(),
deepLinks: List<NavDeepLink> = emptyList(),
- enterTransition: (AnimatedScope.() -> EnterTransition?)? = null,
- exitTransition: (AnimatedScope.() -> ExitTransition?)? = null,
- popEnterTransition: (AnimatedScope.() -> EnterTransition?)? = enterTransition,
- popExitTransition: (AnimatedScope.() -> ExitTransition?)? = exitTransition,
+ enterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?)? = null,
+ exitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?)? = null,
+ popEnterTransition: (
+ AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?
+ )? = enterTransition,
+ popExitTransition: (
+ AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?
+ )? = exitTransition,
content: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit
) {
addDestination(
@@ -69,3 +76,43 @@
}
)
}
+
+/**
+ * Construct a nested [NavGraph]
+ *
+ * @param startDestination the starting destination's route for this NavGraph
+ * @param route the destination's unique route
+ * @param arguments list of arguments to associate with destination
+ * @param deepLinks list of deep links to associate with the destinations
+ * @param enterTransition callback to define enter transitions for destination in this NavGraph
+ * @param exitTransition callback to define exit transitions for destination in this NavGraph
+ * @param popEnterTransition callback to define pop enter transitions for destination in this
+ * NavGraph
+ * @param popExitTransition callback to define pop exit transitions for destination in this NavGraph
+ * @param builder the builder used to construct the graph
+ *
+ * @return the newly constructed nested NavGraph
+ */
+@ExperimentalAnimationApi
+public fun NavGraphBuilder.navigation(
+ startDestination: String,
+ route: String,
+ arguments: List<NamedNavArgument> = emptyList(),
+ deepLinks: List<NavDeepLink> = emptyList(),
+ enterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?)? = null,
+ exitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?)? = null,
+ popEnterTransition: (
+ AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?
+ )? = enterTransition,
+ popExitTransition: (
+ AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?
+ )? = exitTransition,
+ builder: NavGraphBuilder.() -> Unit
+) {
+ navigation(startDestination, route, arguments, deepLinks, builder).apply {
+ enterTransition?.let { enterTransitions[route] = enterTransition }
+ exitTransition?.let { exitTransitions[route] = exitTransition }
+ popEnterTransition?.let { popEnterTransitions[route] = popEnterTransition }
+ popExitTransition?.let { popExitTransitions[route] = popExitTransition }
+ }
+}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 0e20444..8cba2ab 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -48,6 +48,18 @@
import kotlinx.coroutines.withContext
private val KEY_TIMESTAMP = "appliedTimestamp"
+private val KNOWN_PLUGINS =
+ mapOf<String, List<ClockMetadata>>(
+ "com.android.systemui.falcon.one" to listOf(ClockMetadata("ANALOG_CLOCK_BIGNUM")),
+ "com.android.systemui.falcon.two" to listOf(ClockMetadata("DIGITAL_CLOCK_CALLIGRAPHY")),
+ "com.android.systemui.falcon.three" to listOf(ClockMetadata("DIGITAL_CLOCK_FLEX")),
+ "com.android.systemui.falcon.four" to listOf(ClockMetadata("DIGITAL_CLOCK_GROWTH")),
+ "com.android.systemui.falcon.five" to listOf(ClockMetadata("DIGITAL_CLOCK_HANDWRITTEN")),
+ "com.android.systemui.falcon.six" to listOf(ClockMetadata("DIGITAL_CLOCK_INFLATE")),
+ "com.android.systemui.falcon.seven" to listOf(ClockMetadata("DIGITAL_CLOCK_METRO")),
+ "com.android.systemui.falcon.eight" to listOf(ClockMetadata("DIGITAL_CLOCK_NUMBEROVERLAP")),
+ "com.android.systemui.falcon.nine" to listOf(ClockMetadata("DIGITAL_CLOCK_WEATHER")),
+ )
private fun <TKey, TVal> ConcurrentHashMap<TKey, TVal>.concurrentGetOrPut(
key: TKey,
@@ -127,8 +139,61 @@
private val pluginListener =
object : PluginListener<ClockProviderPlugin> {
- override fun onPluginAttached(manager: PluginLifecycleManager<ClockProviderPlugin>) {
- manager.loadPlugin()
+ override fun onPluginAttached(
+ manager: PluginLifecycleManager<ClockProviderPlugin>
+ ): Boolean {
+ if (keepAllLoaded) {
+ // Always load new plugins if requested
+ return true
+ }
+
+ val knownClocks = KNOWN_PLUGINS.get(manager.getPackage())
+ if (knownClocks == null) {
+ logBuffer.tryLog(
+ TAG,
+ LogLevel.WARNING,
+ { str1 = manager.getPackage() },
+ { "Loading unrecognized clock package: $str1" }
+ )
+ return true
+ }
+
+ logBuffer.tryLog(
+ TAG,
+ LogLevel.INFO,
+ { str1 = manager.getPackage() },
+ { "Skipping initial load of known clock package package: $str1" }
+ )
+
+ var isClockListChanged = false
+ for (metadata in knownClocks) {
+ val id = metadata.clockId
+ val info =
+ availableClocks.concurrentGetOrPut(id, ClockInfo(metadata, null, manager)) {
+ isClockListChanged = true
+ onConnected(id)
+ }
+
+ if (manager != info.manager) {
+ logBuffer.tryLog(
+ TAG,
+ LogLevel.ERROR,
+ { str1 = id },
+ { "Clock Id conflict on known attach: $str1 is double registered" }
+ )
+ continue
+ }
+
+ info.provider = null
+ }
+
+ if (isClockListChanged) {
+ triggerOnAvailableClocksChanged()
+ }
+ verifyLoadedProviders()
+
+ // Load executed via verifyLoadedProviders
+ return false
}
override fun onPluginLoaded(
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
index 3ae328e..537b7a4 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt
@@ -190,7 +190,9 @@
data class ClockMetadata(
val clockId: ClockId,
val name: String,
-)
+) {
+ constructor(clockId: ClockId) : this(clockId, clockId) {}
+}
/** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */
data class ClockConfig(
diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java
index cc6a46f..56c3f93 100644
--- a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java
+++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java
@@ -16,12 +16,20 @@
package com.android.systemui.plugins;
+import android.content.ComponentName;
+
/**
* Provides the ability for consumers to control plugin lifecycle.
*
* @param <T> is the target plugin type
*/
public interface PluginLifecycleManager<T extends Plugin> {
+ /** Returns the ComponentName of the target plugin. Maybe be called when not loaded. */
+ ComponentName getComponentName();
+
+ /** Returns the package name of the target plugin. May be called when not loaded. */
+ String getPackage();
+
/** Returns the currently loaded plugin instance (if plugin is loaded) */
T getPlugin();
diff --git a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java
index c5f5032..bd0bd89 100644
--- a/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java
+++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java
@@ -60,13 +60,18 @@
/**
* Called when the plugin is first attached to the host application. {@link #onPluginLoaded}
- * will be automatically called as well when first attached. This may be called multiple times
- * if multiple plugins are allowed. It may also be called in the future if the plugin package
- * changes and needs to be reloaded. Each call to {@link #onPluginAttached} will provide a new
- * or different {@link PluginLifecycleManager}.
+ * will be automatically called as well when first attached if true is returned. This may be
+ * called multiple times if multiple plugins are allowed. It may also be called in the future
+ * if the plugin package changes and needs to be reloaded. Each call to
+ * {@link #onPluginAttached} will provide a new or different {@link PluginLifecycleManager}.
+ *
+ * @return returning true will immediately load the plugin and call onPluginLoaded with the
+ * created object. false will skip loading, but the listener can load it at any time using the
+ * provided PluginLifecycleManager. Loading plugins immediately is the default behavior.
*/
- default void onPluginAttached(PluginLifecycleManager<T> manager) {
+ default boolean onPluginAttached(PluginLifecycleManager<T> manager) {
// Optional
+ return true;
}
/**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
index 9a9a242..6b67c09 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java
@@ -79,17 +79,26 @@
/** Alerts listener and plugin that the plugin has been created. */
public void onCreate() {
- mListener.onPluginAttached(this);
+ boolean loadPlugin = mListener.onPluginAttached(this);
+ if (!loadPlugin) {
+ if (mPlugin != null) {
+ unloadPlugin();
+ }
+ return;
+ }
+
if (mPlugin == null) {
loadPlugin();
- } else {
- if (!(mPlugin instanceof PluginFragment)) {
- // Only call onCreate for plugins that aren't fragments, as fragments
- // will get the onCreate as part of the fragment lifecycle.
- mPlugin.onCreate(mAppContext, mPluginContext);
- }
- mListener.onPluginLoaded(mPlugin, mPluginContext, this);
+ return;
}
+
+ mPluginFactory.checkVersion(mPlugin);
+ if (!(mPlugin instanceof PluginFragment)) {
+ // Only call onCreate for plugins that aren't fragments, as fragments
+ // will get the onCreate as part of the fragment lifecycle.
+ mPlugin.onCreate(mAppContext, mPluginContext);
+ }
+ mListener.onPluginLoaded(mPlugin, mPluginContext, this);
}
/** Alerts listener and plugin that the plugin is being shutdown. */
@@ -118,6 +127,7 @@
return;
}
+ mPluginFactory.checkVersion(mPlugin);
if (!(mPlugin instanceof PluginFragment)) {
// Only call onCreate for plugins that aren't fragments, as fragments
// will get the onCreate as part of the fragment lifecycle.
@@ -205,12 +215,8 @@
PluginFactory<T> pluginFactory = new PluginFactory<T>(
context, mInstanceFactory, appInfo, componentName, mVersionChecker, pluginClass,
() -> getClassLoader(appInfo, mBaseClassLoader));
- // TODO: Only create the plugin before version check if we need it for
- // legacy version check.
- T instance = pluginFactory.createPlugin();
- pluginFactory.checkVersion(instance);
return new PluginInstance<T>(
- context, listener, componentName, pluginFactory, instance);
+ context, listener, componentName, pluginFactory, null);
}
private boolean isPluginPackagePrivileged(String packageName) {
@@ -332,7 +338,9 @@
ClassLoader loader = mClassLoaderFactory.get();
Class<T> instanceClass = (Class<T>) Class.forName(
mComponentName.getClassName(), true, loader);
- return (T) mInstanceFactory.create(instanceClass);
+ T result = (T) mInstanceFactory.create(instanceClass);
+ Log.v(TAG, "Created plugin: " + result);
+ return result;
} catch (ClassNotFoundException ex) {
Log.e(TAG, "Failed to load plugin", ex);
} catch (IllegalAccessException ex) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
index a2840fc..056d692 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacy.java
@@ -103,6 +103,13 @@
}
@Override
+ void onSensorRectUpdated(RectF bounds) {
+ super.onSensorRectUpdated(bounds);
+ bounds.round(this.mSensorBounds);
+ postInvalidate();
+ }
+
+ @Override
void onDisplayConfiguring() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 9a88760..ea51915 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -598,7 +598,7 @@
// 1300 - screenshots
// TODO(b/264916608): Tracking Bug
- @JvmField val SCREENSHOT_METADATA = unreleasedFlag(1302, "screenshot_metadata", teamfood = true)
+ @JvmField val SCREENSHOT_METADATA = unreleasedFlag(1302, "screenshot_metadata")
// TODO(b/266955521): Tracking bug
@JvmField val SCREENSHOT_DETECTION = releasedFlag(1303, "screenshot_detection")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index 23b5241..314566e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -633,12 +633,17 @@
mFSIUpdateCandidates.removeAll(toRemoveForFSI)
}
- /** When an action is pressed on a notification, end HeadsUp lifetime extension. */
+ /**
+ * When an action is pressed on a notification, make sure we don't lifetime-extend it in the
+ * future by informing the HeadsUpManager, and make sure we don't keep lifetime-extending it if
+ * we already are.
+ *
+ * @see HeadsUpManager.setUserActionMayIndirectlyRemove
+ * @see HeadsUpManager.canRemoveImmediately
+ */
private val mActionPressListener = Consumer<NotificationEntry> { entry ->
- if (mNotifsExtendingLifetime.contains(entry)) {
- val removeInMillis = mHeadsUpManager.getEarliestRemovalTime(entry.key)
- mExecutor.executeDelayed({ endNotifLifetimeExtensionIfExtended(entry) }, removeInMillis)
- }
+ mHeadsUpManager.setUserActionMayIndirectlyRemove(entry)
+ mExecutor.execute { endNotifLifetimeExtensionIfExtended(entry) }
}
private val mLifetimeExtender = object : NotifLifetimeExtender {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 23afd8b3..d472c35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -393,6 +393,31 @@
}
}
+ /**
+ * Notes that the user took an action on an entry that might indirectly cause the system or the
+ * app to remove the notification.
+ *
+ * @param entry the entry that might be indirectly removed by the user's action
+ *
+ * @see com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator#mActionPressListener
+ * @see #canRemoveImmediately(String)
+ */
+ public void setUserActionMayIndirectlyRemove(@NonNull NotificationEntry entry) {
+ HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey());
+ if (headsUpEntry != null) {
+ headsUpEntry.userActionMayIndirectlyRemove = true;
+ }
+ }
+
+ @Override
+ public boolean canRemoveImmediately(@NonNull String key) {
+ HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
+ if (headsUpEntry != null && headsUpEntry.userActionMayIndirectlyRemove) {
+ return true;
+ }
+ return super.canRemoveImmediately(key);
+ }
+
@NonNull
@Override
protected HeadsUpEntry createAlertEntry() {
@@ -421,6 +446,8 @@
*/
protected class HeadsUpEntry extends AlertEntry {
public boolean remoteInputActive;
+ public boolean userActionMayIndirectlyRemove;
+
protected boolean expanded;
protected boolean wasUnpinned;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 109c1cf..b848d2e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -930,7 +930,6 @@
showRingerDrawer();
}
});
- updateSelectedRingerContainerDescription(mIsRingerDrawerOpen);
mRingerDrawerVibrate.setOnClickListener(
new RingerDrawerItemClickListener(RINGER_MODE_VIBRATE));
@@ -993,18 +992,6 @@
: 0;
}
- @VisibleForTesting String getSelectedRingerContainerDescription() {
- return mSelectedRingerContainer.getContentDescription().toString();
- }
-
- @VisibleForTesting void toggleRingerDrawer(boolean show) {
- if (show) {
- showRingerDrawer();
- } else {
- hideRingerDrawer();
- }
- }
-
/** Animates in the ringer drawer. */
private void showRingerDrawer() {
if (mIsRingerDrawerOpen) {
@@ -1082,7 +1069,12 @@
.start();
}
- updateSelectedRingerContainerDescription(true);
+ // When the ringer drawer is open, tapping the currently selected ringer will set the ringer
+ // to the current ringer mode. Change the content description to that, instead of the 'tap
+ // to change ringer mode' default.
+ mSelectedRingerContainer.setContentDescription(
+ mContext.getString(getStringDescriptionResourceForRingerMode(
+ mState.ringerModeInternal)));
mIsRingerDrawerOpen = true;
}
@@ -1128,38 +1120,14 @@
.translationY(0f)
.start();
- updateSelectedRingerContainerDescription(false);
+ // When the drawer is closed, tapping the selected ringer drawer will open it, allowing the
+ // user to change the ringer.
+ mSelectedRingerContainer.setContentDescription(
+ mContext.getString(R.string.volume_ringer_change));
mIsRingerDrawerOpen = false;
}
-
- /**
- * @param open false to set the description when drawer is closed
- */
- private void updateSelectedRingerContainerDescription(boolean open) {
- if (mState == null) return;
-
- String currentMode = mContext.getString(getStringDescriptionResourceForRingerMode(
- mState.ringerModeInternal));
- String tapToSelect;
-
- if (open) {
- // When the ringer drawer is open, tapping the currently selected ringer will set the
- // ringer to the current ringer mode. Change the content description to that, instead of
- // the 'tap to change ringer mode' default.
- tapToSelect = "";
-
- } else {
- // When the drawer is closed, tapping the selected ringer drawer will open it, allowing
- // the user to change the ringer. The user needs to know that, and also the current mode
- currentMode += ", ";
- tapToSelect = mContext.getString(R.string.volume_ringer_change);
- }
-
- mSelectedRingerContainer.setContentDescription(currentMode + tapToSelect);
- }
-
private void initSettingsH(int lockTaskModeState) {
if (mSettingsView != null) {
mSettingsView.setVisibility(
@@ -1735,7 +1703,7 @@
});
}
- @VisibleForTesting int getStringDescriptionResourceForRingerMode(int mode) {
+ private int getStringDescriptionResourceForRingerMode(int mode) {
switch (mode) {
case RINGER_MODE_SILENT:
return R.string.volume_ringer_status_silent;
@@ -1817,7 +1785,6 @@
updateVolumeRowH(row);
}
updateRingerH();
- updateSelectedRingerContainerDescription(mIsRingerDrawerOpen);
mWindow.setTitle(composeWindowTitle());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index eef4470..04c93cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -298,6 +298,54 @@
}
@Test
+ fun unknownPluginAttached_clockAndListUnchanged_loadRequested() {
+ val mockPluginLifecycle = mock<PluginLifecycleManager<ClockProviderPlugin>>()
+ whenever(mockPluginLifecycle.getPackage()).thenReturn("some.other.package")
+
+ var changeCallCount = 0
+ var listChangeCallCount = 0
+ registry.registerClockChangeListener(object : ClockRegistry.ClockChangeListener {
+ override fun onCurrentClockChanged() { changeCallCount++ }
+ override fun onAvailableClocksChanged() { listChangeCallCount++ }
+ })
+
+ assertEquals(true, pluginListener.onPluginAttached(mockPluginLifecycle))
+ scheduler.runCurrent()
+ assertEquals(0, changeCallCount)
+ assertEquals(0, listChangeCallCount)
+ }
+
+ @Test
+ fun knownPluginAttached_clockAndListChanged_notLoaded() {
+ val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
+ whenever(mockPluginLifecycle1.getPackage()).thenReturn("com.android.systemui.falcon.one")
+ val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
+ whenever(mockPluginLifecycle2.getPackage()).thenReturn("com.android.systemui.falcon.two")
+
+ var changeCallCount = 0
+ var listChangeCallCount = 0
+ registry.registerClockChangeListener(object : ClockRegistry.ClockChangeListener {
+ override fun onCurrentClockChanged() { changeCallCount++ }
+ override fun onAvailableClocksChanged() { listChangeCallCount++ }
+ })
+
+ registry.applySettings(ClockSettings("DIGITAL_CLOCK_CALLIGRAPHY", null))
+ scheduler.runCurrent()
+ assertEquals(1, changeCallCount)
+ assertEquals(0, listChangeCallCount)
+
+ assertEquals(false, pluginListener.onPluginAttached(mockPluginLifecycle1))
+ scheduler.runCurrent()
+ assertEquals(1, changeCallCount)
+ assertEquals(1, listChangeCallCount)
+
+ assertEquals(false, pluginListener.onPluginAttached(mockPluginLifecycle2))
+ scheduler.runCurrent()
+ assertEquals(1, changeCallCount)
+ assertEquals(2, listChangeCallCount)
+ }
+
+ @Test
fun pluginAddRemove_concurrentModification() {
val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
index d5e904c..88d853e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java
@@ -122,6 +122,7 @@
mPluginInstanceFactory.create(
mContext, mAppInfo, wrongVersionTestPluginComponentName,
TestPlugin.class, mPluginListener);
+ mPluginInstance.onCreate();
}
@Test
@@ -135,11 +136,12 @@
@Test
public void testOnDestroy() {
+ mPluginInstance.onCreate();
mPluginInstance.onDestroy();
assertEquals(1, mPluginListener.mDetachedCount);
assertEquals(1, mPluginListener.mUnloadCount);
assertNull(mPluginInstance.getPlugin());
- assertInstances(0, -1); // Destroyed but never created
+ assertInstances(0, 0); // Destroyed but never created
}
@Test
@@ -161,6 +163,16 @@
assertInstances(0, 0);
}
+ @Test
+ public void testOnAttach_SkipLoad() {
+ mPluginListener.mAttachReturn = false;
+ mPluginInstance.onCreate();
+ assertEquals(1, mPluginListener.mAttachedCount);
+ assertEquals(0, mPluginListener.mLoadCount);
+ assertEquals(null, mPluginInstance.getPlugin());
+ assertInstances(0, 0);
+ }
+
// This target class doesn't matter, it just needs to have a Requires to hit the flow where
// the mock version info is called.
@ProvidesInterface(action = TestPlugin.ACTION, version = TestPlugin.VERSION)
@@ -220,15 +232,17 @@
}
public class FakeListener implements PluginListener<TestPlugin> {
+ public boolean mAttachReturn = true;
public int mAttachedCount = 0;
public int mDetachedCount = 0;
public int mLoadCount = 0;
public int mUnloadCount = 0;
@Override
- public void onPluginAttached(PluginLifecycleManager<TestPlugin> manager) {
+ public boolean onPluginAttached(PluginLifecycleManager<TestPlugin> manager) {
mAttachedCount++;
assertEquals(PluginInstanceTest.this.mPluginInstance, manager);
+ return mAttachReturn;
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index 283efe2..257cc5b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -221,16 +221,35 @@
}
@Test
- fun hunExtensionCancelledWhenHunActionPressed() {
+ fun actionPressCancelsExistingLifetimeExtension() {
whenever(headsUpManager.isSticky(anyString())).thenReturn(true)
addHUN(entry)
+
whenever(headsUpManager.canRemoveImmediately(anyString())).thenReturn(false)
whenever(headsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L)
- assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
+ assertTrue(notifLifetimeExtender.maybeExtendLifetime(entry, /* reason = */ 0))
+
actionPressListener.accept(entry)
- executor.advanceClockToLast()
executor.runAllReady()
- verify(headsUpManager, times(1)).removeNotification(eq(entry.key), eq(true))
+ verify(endLifetimeExtension, times(1)).onEndLifetimeExtension(notifLifetimeExtender, entry)
+
+ collectionListener.onEntryRemoved(entry, /* reason = */ 0)
+ verify(headsUpManager, times(1)).removeNotification(eq(entry.key), any())
+ }
+
+ @Test
+ fun actionPressPreventsFutureLifetimeExtension() {
+ whenever(headsUpManager.isSticky(anyString())).thenReturn(true)
+ addHUN(entry)
+
+ actionPressListener.accept(entry)
+ verify(headsUpManager, times(1)).setUserActionMayIndirectlyRemove(entry)
+
+ whenever(headsUpManager.canRemoveImmediately(anyString())).thenReturn(true)
+ assertFalse(notifLifetimeExtender.maybeExtendLifetime(entry, 0))
+
+ collectionListener.onEntryRemoved(entry, /* reason = */ 0)
+ verify(headsUpManager, times(1)).removeNotification(eq(entry.key), any())
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index faef5b4..ee02a7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -89,6 +89,7 @@
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -102,6 +103,7 @@
/**
* Tests for {@link NotificationStackScrollLayout}.
*/
+@Ignore("b/255552856")
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@@ -373,6 +375,20 @@
}
@Test
+ public void testAppearFractionCalculation() {
+ // appear start position
+ when(mNotificationShelf.getIntrinsicHeight()).thenReturn(100);
+ // because it's the same as shelf height, appear start position equals shelf height
+ mStackScroller.mStatusBarHeight = 100;
+ // appear end position
+ when(mEmptyShadeView.getHeight()).thenReturn(200);
+
+ assertEquals(0f, mStackScroller.calculateAppearFraction(100));
+ assertEquals(1f, mStackScroller.calculateAppearFraction(200));
+ assertEquals(0.5f, mStackScroller.calculateAppearFraction(150));
+ }
+
+ @Test
public void testAppearFractionCalculationIsNotNegativeWhenShelfBecomesSmaller() {
// this situation might occur if status bar height is defined in pixels while shelf height
// in dp and screen density changes - appear start position
@@ -574,7 +590,6 @@
@Test
public void testReInflatesFooterViews() {
- when(mEmptyShadeView.getTextResource()).thenReturn(R.string.empty_shade_text);
clearInvocations(mStackScroller);
mStackScroller.reinflateViews();
verify(mStackScroller).setFooterView(any());
@@ -901,7 +916,7 @@
mStackScroller.setHasFilteredOutSeenNotifications(true);
mStackScroller.updateEmptyShadeView(true, false);
- verify(mEmptyShadeView).setFooterText(not(eq(0)));
+ verify(mEmptyShadeView).setFooterText(not(0));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index 487d26d..a797e03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -20,7 +20,6 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -346,4 +345,17 @@
assertEquals(HeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(),
mUiEventLoggerFake.eventId(0));
}
+
+ @Test
+ public void testSetUserActionMayIndirectlyRemove() {
+ NotificationEntry notifEntry = new NotificationEntryBuilder()
+ .setSbn(createNewNotification(/* id= */ 0))
+ .build();
+
+ mHeadsUpManager.showNotification(notifEntry);
+ assertFalse(mHeadsUpManager.canRemoveImmediately(notifEntry.getKey()));
+
+ mHeadsUpManager.setUserActionMayIndirectlyRemove(notifEntry);
+ assertTrue(mHeadsUpManager.canRemoveImmediately(notifEntry.getKey()));
+ }
}
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 2ea6368..8f725be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -16,16 +16,11 @@
package com.android.systemui.volume;
-import static android.media.AudioManager.RINGER_MODE_NORMAL;
-import static android.media.AudioManager.RINGER_MODE_SILENT;
-import static android.media.AudioManager.RINGER_MODE_VIBRATE;
-
import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN;
import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN;
import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS;
import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -298,7 +293,7 @@
@Test
public void testSelectVibrateFromDrawer() {
final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL;
+ initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
mDialog.onStateChangedH(initialUnsetState);
mActiveRinger.performClick();
@@ -312,7 +307,7 @@
@Test
public void testSelectMuteFromDrawer() {
final State initialUnsetState = new State();
- initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL;
+ initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
mDialog.onStateChangedH(initialUnsetState);
mActiveRinger.performClick();
@@ -334,7 +329,7 @@
// Make sure we've actually changed the ringer mode.
verify(mVolumeDialogController, times(1)).setRingerMode(
- RINGER_MODE_NORMAL, false);
+ AudioManager.RINGER_MODE_NORMAL, false);
}
/**
@@ -516,85 +511,6 @@
}
}
- private enum RingerDrawerState {INIT, OPEN, CLOSE}
-
- @Test
- public void ringerModeNormal_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.INIT);
- }
-
- @Test
- public void ringerModeSilent_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.INIT);
- }
-
- @Test
- public void ringerModeVibrate_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.INIT);
- }
-
- @Test
- public void ringerModeNormal_openDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.OPEN);
- }
-
- @Test
- public void ringerModeSilent_openDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.OPEN);
- }
-
- @Test
- public void ringerModeVibrate_openDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.OPEN);
- }
-
- @Test
- public void ringerModeNormal_closeDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.CLOSE);
- }
-
- @Test
- public void ringerModeSilent_closeDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.CLOSE);
- }
-
- @Test
- public void ringerModeVibrate_closeDrawer_ringerContainerDescribesItsState() {
- assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.CLOSE);
- }
-
- /**
- * The content description should include ringer state, and the correct one.
- */
- private void assertRingerContainerDescribesItsState(int ringerMode,
- RingerDrawerState drawerState) {
- State state = createShellState();
- state.ringerModeInternal = ringerMode;
- mDialog.onStateChangedH(state);
-
- mDialog.show(SHOW_REASON_UNKNOWN);
-
- if (drawerState != RingerDrawerState.INIT) {
- // in both cases we first open the drawer
- mDialog.toggleRingerDrawer(true);
-
- if (drawerState == RingerDrawerState.CLOSE) {
- mDialog.toggleRingerDrawer(false);
- }
- }
-
- String ringerContainerDescription = mDialog.getSelectedRingerContainerDescription();
- String ringerDescription = mContext.getString(
- mDialog.getStringDescriptionResourceForRingerMode(ringerMode));
-
- if (drawerState == RingerDrawerState.OPEN) {
- assertEquals(ringerDescription, ringerContainerDescription);
- } else {
- assertNotSame(ringerDescription, ringerContainerDescription);
- assertTrue(ringerContainerDescription.startsWith(ringerDescription));
- }
- }
-
@After
public void teardown() {
if (mDialog != null) {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 1f8a499..9474fc1 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -71,6 +71,7 @@
import android.content.pm.SigningDetails.CertCapabilities;
import android.content.pm.UserInfo;
import android.database.Cursor;
+import android.database.sqlite.SQLiteCantOpenDatabaseException;
import android.database.sqlite.SQLiteFullException;
import android.database.sqlite.SQLiteStatement;
import android.os.Binder;
@@ -1419,7 +1420,13 @@
private void purgeOldGrants(UserAccounts accounts) {
synchronized (accounts.dbLock) {
synchronized (accounts.cacheLock) {
- List<Integer> uids = accounts.accountsDb.findAllUidGrants();
+ List<Integer> uids;
+ try {
+ uids = accounts.accountsDb.findAllUidGrants();
+ } catch (SQLiteCantOpenDatabaseException e) {
+ Log.w(TAG, "Could not delete grants for user = " + accounts.userId);
+ return;
+ }
for (int uid : uids) {
final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
if (packageExists) {
@@ -1445,7 +1452,13 @@
mPackageManager.getPackageUidAsUser(packageName, accounts.userId);
} catch (NameNotFoundException e) {
// package does not exist - remove visibility values
- accounts.accountsDb.deleteAccountVisibilityForPackage(packageName);
+ try {
+ accounts.accountsDb.deleteAccountVisibilityForPackage(packageName);
+ } catch (SQLiteCantOpenDatabaseException sqlException) {
+ Log.w(TAG, "Could not delete account visibility for user = "
+ + accounts.userId, sqlException);
+ continue;
+ }
synchronized (accounts.dbLock) {
synchronized (accounts.cacheLock) {
for (Account account : accounts.visibilityCache.keySet()) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 2ba5223..0e63624 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -2214,6 +2214,7 @@
mDeviceInventory.removePreferredDevicesForStrategyInt(mAccessibilityStrategyId);
}
mDeviceInventory.applyConnectedDevicesRoles();
+ mDeviceInventory.reapplyExternalDevicesRoles();
} else {
mDeviceInventory.setPreferredDevicesForStrategyInt(
mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice));
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index d1cae49..219dda3 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -361,23 +361,34 @@
AudioSystem.DEVICE_STATE_AVAILABLE,
di.mDeviceCodecFormat);
}
- mAppliedStrategyRoles.clear();
mAppliedStrategyRolesInt.clear();
- mAppliedPresetRoles.clear();
mAppliedPresetRolesInt.clear();
applyConnectedDevicesRoles_l();
}
+ reapplyExternalDevicesRoles();
+ }
+
+ /*package*/ void reapplyExternalDevicesRoles() {
+ synchronized (mDevicesLock) {
+ mAppliedStrategyRoles.clear();
+ mAppliedPresetRoles.clear();
+ }
synchronized (mPreferredDevices) {
mPreferredDevices.forEach((strategy, devices) -> {
- setPreferredDevicesForStrategy(strategy, devices); });
+ setPreferredDevicesForStrategy(strategy, devices);
+ });
}
synchronized (mNonDefaultDevices) {
mNonDefaultDevices.forEach((strategy, devices) -> {
addDevicesRoleForStrategy(strategy, AudioSystem.DEVICE_ROLE_DISABLED,
- devices, false /* internal */); });
+ devices, false /* internal */);
+ });
}
synchronized (mPreferredDevicesForCapturePreset) {
- // TODO: call audiosystem to restore
+ mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> {
+ setDevicesRoleForCapturePreset(
+ capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+ });
}
}
@@ -1163,6 +1174,7 @@
return mAudioSystem.removeDevicesRoleForStrategy(s, r, d); });
purgeRoles(mAppliedPresetRolesInt, (p, r, d) -> {
return mAudioSystem.removeDevicesRoleForCapturePreset(p, r, d); });
+ reapplyExternalDevicesRoles();
}
@GuardedBy("mDevicesLock")
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e72fcdf..878da54 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2344,6 +2344,7 @@
mRankingHandler,
mZenModeHelper,
mPermissionHelper,
+ mPermissionManager,
mNotificationChannelLogger,
mAppOps,
new SysUiStatsEvent.BuilderFactory(),
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index c9a6c63..0292a99 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -27,6 +27,7 @@
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IActivityManager;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.Person;
@@ -46,6 +47,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.PowerManager;
import android.os.UserHandle;
import android.os.VibrationEffect;
import android.provider.Settings;
@@ -101,7 +103,8 @@
final int mTargetSdkVersion;
final int mOriginalFlags;
private final Context mContext;
-
+ private final KeyguardManager mKeyguardManager;
+ private final PowerManager mPowerManager;
NotificationUsageStats.SingleNotificationStats stats;
boolean isCanceled;
IBinder permissionOwner;
@@ -228,6 +231,8 @@
mUpdateTimeMs = mCreationTimeMs;
mInterruptionTimeMs = mCreationTimeMs;
mContext = context;
+ mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
+ mPowerManager = mContext.getSystemService(PowerManager.class);
stats = new NotificationUsageStats.SingleNotificationStats();
mChannel = channel;
mPreChannelsNotification = isPreChannelsNotification();
@@ -1619,6 +1624,11 @@
return mPhoneNumbers;
}
+ boolean isLocked() {
+ return mKeyguardManager.isKeyguardLocked()
+ || !mPowerManager.isInteractive(); // Unlocked AOD
+ }
+
@VisibleForTesting
static final class Light {
public final int color;
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
index 0cc4fc4..5ca882c 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java
@@ -31,9 +31,12 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationStats;
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
+import com.android.internal.util.FrameworkStatsLog;
import java.util.ArrayList;
import java.util.Objects;
@@ -500,6 +503,8 @@
final boolean is_foreground_service;
final long timeout_millis;
final boolean is_non_dismissible;
+ final int fsi_state;
+ final boolean is_locked;
@DurationMillisLong long post_duration_millis; // Not final; calculated at the end.
NotificationReported(NotificationRecordPair p,
@@ -530,6 +535,20 @@
this.is_foreground_service = NotificationRecordLogger.isForegroundService(p.r);
this.timeout_millis = p.r.getSbn().getNotification().getTimeoutAfter();
this.is_non_dismissible = NotificationRecordLogger.isNonDismissible(p.r);
+
+ final boolean isStickyHunFlagEnabled = SystemUiSystemPropertiesFlags.getResolver()
+ .isEnabled(NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI);
+
+ final boolean hasFullScreenIntent =
+ p.r.getSbn().getNotification().fullScreenIntent != null;
+
+ final boolean hasFsiRequestedButDeniedFlag = (p.r.getSbn().getNotification().flags
+ & Notification.FLAG_FSI_REQUESTED_BUT_DENIED) != 0;
+
+ this.fsi_state = NotificationRecordLogger.getFsiState(isStickyHunFlagEnabled,
+ hasFullScreenIntent, hasFsiRequestedButDeniedFlag, eventType);
+
+ this.is_locked = p.r.isLocked();
}
}
@@ -558,7 +577,6 @@
}
/**
- * @param r NotificationRecord
* @return Whether the notification is a non-dismissible notification.
*/
static boolean isNonDismissible(@NonNull NotificationRecord r) {
@@ -567,4 +585,28 @@
}
return (r.getNotification().flags & Notification.FLAG_NO_DISMISS) != 0;
}
+
+ /**
+ * @return FrameworkStatsLog enum of the state of the full screen intent posted with this
+ * notification.
+ */
+ static int getFsiState(boolean isStickyHunFlagEnabled,
+ boolean hasFullScreenIntent,
+ boolean hasFsiRequestedButDeniedFlag,
+ NotificationReportedEvent eventType) {
+
+ if (!isStickyHunFlagEnabled
+ || eventType == NotificationReportedEvent.NOTIFICATION_UPDATED) {
+ // Zeroes in protos take zero bandwidth, but non-zero numbers take bandwidth,
+ // so we should log 0 when possible.
+ return 0;
+ }
+ if (hasFullScreenIntent) {
+ return FrameworkStatsLog.NOTIFICATION_REPORTED__FSI_STATE__FSI_ALLOWED;
+ }
+ if (hasFsiRequestedButDeniedFlag) {
+ return FrameworkStatsLog.NOTIFICATION_REPORTED__FSI_STATE__FSI_DENIED;
+ }
+ return FrameworkStatsLog.NOTIFICATION_REPORTED__FSI_STATE__NO_FSI;
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index feb75ef..9da0e98 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -75,7 +75,9 @@
notificationReported.is_foreground_service,
notificationReported.timeout_millis,
notificationReported.is_non_dismissible,
- notificationReported.post_duration_millis);
+ notificationReported.post_duration_millis,
+ notificationReported.fsi_state,
+ notificationReported.is_locked);
}
@Override
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 4399a3c..838a0fb 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -30,6 +30,9 @@
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -41,6 +44,7 @@
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
import android.app.NotificationManager;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
@@ -53,6 +57,7 @@
import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
+import android.permission.PermissionManager;
import android.provider.Settings;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.NotificationListenerService;
@@ -72,6 +77,8 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags;
+import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
@@ -183,6 +190,7 @@
private final RankingHandler mRankingHandler;
private final ZenModeHelper mZenModeHelper;
private final PermissionHelper mPermissionHelper;
+ private final PermissionManager mPermissionManager;
private final NotificationChannelLogger mNotificationChannelLogger;
private final AppOpsManager mAppOps;
@@ -198,7 +206,7 @@
private boolean mAllowInvalidShortcuts = false;
public PreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
- ZenModeHelper zenHelper, PermissionHelper permHelper,
+ ZenModeHelper zenHelper, PermissionHelper permHelper, PermissionManager permManager,
NotificationChannelLogger notificationChannelLogger,
AppOpsManager appOpsManager,
SysUiStatsEvent.BuilderFactory statsEventBuilderFactory,
@@ -207,6 +215,7 @@
mZenModeHelper = zenHelper;
mRankingHandler = rankingHandler;
mPermissionHelper = permHelper;
+ mPermissionManager = permManager;
mPm = pm;
mNotificationChannelLogger = notificationChannelLogger;
mAppOps = appOpsManager;
@@ -2027,6 +2036,43 @@
}
/**
+ * @return State of the full screen intent permission for this package.
+ */
+ @VisibleForTesting
+ int getFsiState(String pkg, int uid, boolean requestedFSIPermission, boolean isFlagEnabled) {
+ if (!isFlagEnabled) {
+ return 0;
+ }
+ if (!requestedFSIPermission) {
+ return PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED;
+ }
+ final AttributionSource attributionSource =
+ new AttributionSource.Builder(uid).setPackageName(pkg).build();
+
+ final int result = mPermissionManager.checkPermissionForPreflight(
+ android.Manifest.permission.USE_FULL_SCREEN_INTENT, attributionSource);
+
+ if (result == PermissionManager.PERMISSION_GRANTED) {
+ return PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED;
+ }
+ return PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED;
+ }
+
+ /**
+ * @return True if the current full screen intent permission state for this package was set by
+ * the user.
+ */
+ @VisibleForTesting
+ boolean isFsiPermissionUserSet(String pkg, int uid, int fsiState, int currentPermissionFlags,
+ boolean isStickyHunFlagEnabled) {
+ if (!isStickyHunFlagEnabled
+ || fsiState == PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED) {
+ return false;
+ }
+ return (currentPermissionFlags & PackageManager.FLAG_PERMISSION_USER_SET) != 0;
+ }
+
+ /**
* Fills out {@link PackageNotificationPreferences} proto and wraps it in a {@link StatsEvent}.
*/
public void pullPackagePreferencesStats(List<StatsEvent> events,
@@ -2070,7 +2116,33 @@
event.writeInt(r.visibility);
event.writeInt(r.lockedAppFields);
- event.writeBoolean(importanceIsUserSet); // optional bool user_set_importance = 5;
+
+ // optional bool user_set_importance = 5;
+ event.writeBoolean(importanceIsUserSet);
+
+ // optional FsiState fsi_state = 6;
+ final boolean isStickyHunFlagEnabled = SystemUiSystemPropertiesFlags.getResolver()
+ .isEnabled(NotificationFlags.SHOW_STICKY_HUN_FOR_DENIED_FSI);
+
+ final boolean requestedFSIPermission = mPermissionHelper.hasRequestedPermission(
+ android.Manifest.permission.USE_FULL_SCREEN_INTENT, r.pkg, r.uid);
+
+ final int fsiState = getFsiState(r.pkg, r.uid, requestedFSIPermission,
+ isStickyHunFlagEnabled);
+
+ event.writeInt(fsiState);
+
+ // optional bool is_fsi_permission_user_set = 7;
+ final int currentPermissionFlags = mPm.getPermissionFlags(
+ android.Manifest.permission.USE_FULL_SCREEN_INTENT, r.pkg,
+ UserHandle.getUserHandleForUid(r.uid));
+
+ final boolean isUserSet =
+ isFsiPermissionUserSet(r.pkg, r.uid, fsiState, currentPermissionFlags,
+ isStickyHunFlagEnabled);
+
+ event.writeBoolean(isUserSet);
+
events.add(event.build());
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index cfdb3fb..2edf978 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3742,11 +3742,16 @@
if (parser.getName().equals(TAG_PERMISSIONS)) {
final LegacyPermissionState legacyState;
if (ps.hasSharedUser()) {
- legacyState = getSettingLPr(ps.getSharedUserAppId()).getLegacyPermissionState();
+ final SettingBase sharedUserSettings = getSettingLPr(
+ ps.getSharedUserAppId());
+ legacyState = sharedUserSettings != null
+ ? sharedUserSettings.getLegacyPermissionState() : null;
} else {
legacyState = ps.getLegacyPermissionState();
}
- readInstallPermissionsLPr(parser, legacyState, users);
+ if (legacyState != null) {
+ readInstallPermissionsLPr(parser, legacyState, users);
+ }
} else if (parser.getName().equals(TAG_USES_STATIC_LIB)) {
readUsesStaticLibLPw(parser, ps);
} else if (parser.getName().equals(TAG_USES_SDK_LIB)) {
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index a020728..5b3514c 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -2547,10 +2547,7 @@
enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS,
"getShareTargets");
final ComponentName chooser = injectChooserActivity();
- final String pkg = (chooser != null
- && mPackageManagerInternal.getComponentEnabledSetting(chooser,
- injectBinderCallingUid(), userId) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)
- ? chooser.getPackageName() : mContext.getPackageName();
+ final String pkg = chooser != null ? chooser.getPackageName() : mContext.getPackageName();
synchronized (mLock) {
throwIfUserLockedL(userId);
final List<ShortcutManager.ShareShortcutInfo> shortcutInfoList = new ArrayList<>();
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index a299b56..a3ab889 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -140,6 +140,7 @@
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.SharedUserApi;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
import com.android.server.pm.pkg.component.ParsedPermission;
import com.android.server.pm.pkg.component.ParsedPermissionGroup;
@@ -4446,8 +4447,13 @@
final int appId = ps.getAppId();
final LegacyPermissionState legacyState;
if (ps.hasSharedUser()) {
- legacyState = mPackageManagerInt.getSharedUserApi(
- ps.getSharedUserAppId()).getSharedUserLegacyPermissionState();
+ final int sharedUserId = ps.getSharedUserAppId();
+ SharedUserApi sharedUserApi = mPackageManagerInt.getSharedUserApi(sharedUserId);
+ if (sharedUserApi == null) {
+ Slog.wtf(TAG, "Missing shared user Api for " + sharedUserId);
+ return;
+ }
+ legacyState = sharedUserApi.getSharedUserLegacyPermissionState();
} else {
legacyState = ps.getLegacyPermissionState();
}
@@ -4492,8 +4498,13 @@
ps.setInstallPermissionsFixed(false);
final LegacyPermissionState legacyState;
if (ps.hasSharedUser()) {
- legacyState = mPackageManagerInt.getSharedUserApi(
- ps.getSharedUserAppId()).getSharedUserLegacyPermissionState();
+ final int sharedUserId = ps.getSharedUserAppId();
+ SharedUserApi sharedUserApi = mPackageManagerInt.getSharedUserApi(sharedUserId);
+ if (sharedUserApi == null) {
+ Slog.wtf(TAG, "Missing shared user Api for " + sharedUserId);
+ return;
+ }
+ legacyState = sharedUserApi.getSharedUserLegacyPermissionState();
} else {
legacyState = ps.getLegacyPermissionState();
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperData.java b/services/core/java/com/android/server/wallpaper/WallpaperData.java
index 9ff6a0d..d87fca4 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperData.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperData.java
@@ -133,16 +133,14 @@
*/
final Rect cropHint = new Rect(0, 0, 0, 0);
- WallpaperData(int userId, File wallpaperDir, String inputFileName, String cropFileName) {
- this.userId = userId;
- wallpaperFile = new File(wallpaperDir, inputFileName);
- cropFile = new File(wallpaperDir, cropFileName);
- }
-
WallpaperData(int userId, @SetWallpaperFlags int wallpaperType) {
- this(userId, getWallpaperDir(userId),
- (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_ORIG : WALLPAPER,
- (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_CROP : WALLPAPER_CROP);
+ this.userId = userId;
+ this.mWhich = wallpaperType;
+ File wallpaperDir = getWallpaperDir(userId);
+ String wallpaperFileName = (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_ORIG : WALLPAPER;
+ String cropFileName = (wallpaperType == FLAG_LOCK) ? WALLPAPER_LOCK_CROP : WALLPAPER_CROP;
+ this.wallpaperFile = new File(wallpaperDir, wallpaperFileName);
+ this.cropFile = new File(wallpaperDir, cropFileName);
}
/**
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 21004ab..e1c865b 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -454,7 +454,7 @@
if (mSource.getType() == WindowInsets.Type.ime()) {
setClientVisible(target.isRequestedVisible(WindowInsets.Type.ime()));
}
- final Transaction t = mDisplayContent.getSyncTransaction();
+ final Transaction t = mWindowContainer.getSyncTransaction();
mWindowContainer.startAnimation(t, mAdapter, !mClientVisible /* hidden */,
ANIMATION_TYPE_INSETS_CONTROL);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 4995236..48ca694 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -102,6 +102,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
+import android.content.pm.UserProperties;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
@@ -148,6 +149,7 @@
import com.android.server.am.ActivityManagerService;
import com.android.server.am.AppTimeTracker;
import com.android.server.am.UserState;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.utils.Slogf;
@@ -1879,6 +1881,14 @@
rootTask.switchUser(userId);
});
+
+ if (topFocusedRootTask != null && isAlwaysVisibleUser(topFocusedRootTask.mUserId)) {
+ Slog.i(TAG, "Persisting top task because it belongs to an always-visible user");
+ // For a normal user-switch, we will restore the new user's task. But if the pre-switch
+ // top task is an always-visible (Communal) one, keep it even after the switch.
+ mUserRootTaskInFront.put(mCurrentUser, focusRootTaskId);
+ }
+
final int restoreRootTaskId = mUserRootTaskInFront.get(userId);
Task rootTask = getRootTask(restoreRootTaskId);
if (rootTask == null) {
@@ -1894,6 +1904,13 @@
return homeInFront;
}
+ /** Returns whether the given user is to be always-visible (e.g. a communal profile). */
+ private boolean isAlwaysVisibleUser(@UserIdInt int userId) {
+ final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
+ final UserProperties properties = umi.getUserProperties(userId);
+ return properties != null && properties.getAlwaysVisible();
+ }
+
void removeUser(int userId) {
mUserRootTaskInFront.delete(userId);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 1ae1e03..bfdf84e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3298,6 +3298,12 @@
scheduleAnimation();
}
+ // Let organizer manage task visibility for shell transition. So don't change it's
+ // visibility during collecting.
+ if (mTransitionController.isCollecting() && mCreatedByOrganizer) {
+ return;
+ }
+
// We intend to let organizer manage task visibility but it doesn't
// have enough information until we finish shell transitions.
// In the mean time we do an easy fix here.
@@ -5695,17 +5701,28 @@
}
private boolean moveTaskToBackInner(@NonNull Task task) {
- moveToBack("moveTaskToBackInner", task);
-
- if (inPinnedWindowingMode()) {
- mTaskSupervisor.removeRootTask(this);
- return true;
+ if (mTransitionController.isShellTransitionsEnabled()) {
+ // Preventing from update surface position for WindowState if configuration changed,
+ // because the position is depends on WindowFrame, so update the position before
+ // relayout will only update it to "old" position.
+ mAtmService.deferWindowLayout();
}
+ try {
+ moveToBack("moveTaskToBackInner", task);
- mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */,
- mDisplayContent.mDisplayId, false /* markFrozenIfConfigChanged */,
- false /* deferResume */);
+ if (inPinnedWindowingMode()) {
+ mTaskSupervisor.removeRootTask(this);
+ return true;
+ }
+ mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */,
+ mDisplayContent.mDisplayId, false /* markFrozenIfConfigChanged */,
+ false /* deferResume */);
+ } finally {
+ if (mTransitionController.isShellTransitionsEnabled()) {
+ mAtmService.continueWindowLayout();
+ }
+ }
ActivityRecord topActivity = getDisplayArea().topRunningActivity();
Task topRootTask = topActivity.getRootTask();
if (topRootTask != null && topRootTask != this && topActivity.isState(RESUMED)) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 9a5f766..82b0086 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1190,7 +1190,11 @@
// processed all the participants first (in particular, we want to trigger pip-enter first)
for (int i = 0; i < mParticipants.size(); ++i) {
final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord();
- if (ar != null) {
+ // If the activity was just inserted to an invisible task, it will keep INITIALIZING
+ // state. Then no need to notify the callback to avoid clearing some states
+ // unexpectedly, e.g. launch-task-behind.
+ if (ar != null && (ar.isVisibleRequested()
+ || !ar.isState(ActivityRecord.State.INITIALIZING))) {
mController.dispatchLegacyAppTransitionFinished(ar);
}
}
@@ -2244,11 +2248,17 @@
WindowContainer<?> ancestor = findCommonAncestor(sortedTargets, wc);
// Make leash based on highest (z-order) direct child of ancestor with a participant.
+ // Check whether the ancestor is belonged to last parent, shouldn't happen.
+ final boolean hasReparent = !wc.isDescendantOf(ancestor);
WindowContainer leashReference = wc;
- while (leashReference.getParent() != ancestor) {
- leashReference = leashReference.getParent();
+ if (hasReparent) {
+ Slog.e(TAG, "Did not find common ancestor! Ancestor= " + ancestor
+ + " target= " + wc);
+ } else {
+ while (leashReference.getParent() != ancestor) {
+ leashReference = leashReference.getParent();
+ }
}
-
final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName(
"Transition Root: " + leashReference.getName()).build();
rootLeash.setUnreleasedWarningCallSite("Transition.calculateTransitionRoots");
@@ -2453,6 +2463,20 @@
// Skip the non-app window or windows on a different display
continue;
}
+ // Re-initiate the last parent as the initial ancestor instead of the top target.
+ // When move a leaf task from organized task to display area, try to keep the transition
+ // root be the original organized task for close transition animation.
+ // Otherwise, shell will use wrong root layer to play animation.
+ // Note: Since the target is sorted, so only need to do this at the lowest target.
+ if (change.mStartParent != null && wc.getParent() != null
+ && change.mStartParent.isAttached() && wc.getParent() != change.mStartParent
+ && i == targets.size() - 1) {
+ final int transitionMode = change.getTransitMode(wc);
+ if (transitionMode == TRANSIT_CLOSE || transitionMode == TRANSIT_TO_BACK) {
+ ancestor = change.mStartParent;
+ continue;
+ }
+ }
while (!wc.isDescendantOf(ancestor)) {
ancestor = ancestor.getParent();
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 3453e2b..88e57f3 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1413,7 +1413,6 @@
policy.mAdminList.remove(i);
policy.mAdminMap.remove(aa.info.getComponent());
pushActiveAdminPackagesLocked(userHandle);
- pushMeteredDisabledPackages(userHandle);
}
}
} catch (RemoteException re) {
@@ -1454,6 +1453,7 @@
if (removedAdmin) {
// The removed admin might have disabled camera, so update user restrictions.
pushUserRestrictions(userHandle);
+ pushMeteredDisabledPackages(userHandle);
}
}
@@ -7844,27 +7844,29 @@
throw new SecurityException("Cannot wipe data. " + restriction
+ " restriction is set for user " + userId);
}
+ });
- boolean isSystemUser = userId == UserHandle.USER_SYSTEM;
- boolean wipeDevice;
- if (factoryReset == null || !mInjector.isChangeEnabled(EXPLICIT_WIPE_BEHAVIOUR,
- adminPackage,
- userId)) {
- // Legacy mode
- wipeDevice = isSystemUser;
+ boolean isSystemUser = userId == UserHandle.USER_SYSTEM;
+ boolean wipeDevice;
+ if (factoryReset == null || !mInjector.isChangeEnabled(EXPLICIT_WIPE_BEHAVIOUR,
+ adminPackage,
+ userId)) {
+ // Legacy mode
+ wipeDevice = isSystemUser;
+ } else {
+ // Explicit behaviour
+ if (factoryReset) {
+ EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin(
+ /*admin=*/ null,
+ /*permission=*/ new String[]{MANAGE_DEVICE_POLICY_WIPE_DATA,
+ MASTER_CLEAR},
+ USES_POLICY_WIPE_DATA,
+ adminPackage,
+ factoryReset ? UserHandle.USER_ALL :
+ getAffectedUser(calledOnParentInstance));
+ wipeDevice = true;
} else {
- // Explicit behaviour
- if (factoryReset) {
- EnforcingAdmin enforcingAdmin = enforcePermissionsAndGetEnforcingAdmin(
- /*admin=*/ null,
- /*permission=*/ new String[]{MANAGE_DEVICE_POLICY_WIPE_DATA,
- MASTER_CLEAR},
- USES_POLICY_WIPE_DATA,
- adminPackage,
- factoryReset ? UserHandle.USER_ALL :
- getAffectedUser(calledOnParentInstance));
- wipeDevice = true;
- } else {
+ mInjector.binderWithCleanCallingIdentity(() -> {
Preconditions.checkCallAuthorization(!isSystemUser,
"User %s is a system user and cannot be removed", userId);
boolean isLastNonHeadlessUser = getUserInfo(userId).isFull()
@@ -7875,9 +7877,11 @@
"Removing user %s would leave the device without any active users. "
+ "Consider factory resetting the device instead.",
userId);
- wipeDevice = false;
- }
+ });
+ wipeDevice = false;
}
+ }
+ mInjector.binderWithCleanCallingIdentity(() -> {
if (wipeDevice) {
forceWipeDeviceNoLock(
(flags & WIPE_EXTERNAL_STORAGE) != 0,
@@ -17882,41 +17886,44 @@
if (!mHasFeature) {
return packageNames;
}
- synchronized (getLockObject()) {
- final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
- return mInjector.binderWithCleanCallingIdentity(() -> {
- final List<String> excludedPkgs = removeInvalidPkgsForMeteredDataRestriction(
- caller.getUserId(), packageNames);
+ return mInjector.binderWithCleanCallingIdentity(() -> {
+ final List<String> excludedPkgs = removeInvalidPkgsForMeteredDataRestriction(
+ caller.getUserId(), packageNames);
+
+ synchronized (getLockObject()) {
+ final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller.getUserId());
admin.meteredDisabledPackages = packageNames;
- pushMeteredDisabledPackages(caller.getUserId());
saveSettingsLocked(caller.getUserId());
- return excludedPkgs;
- });
- }
+ }
+ pushMeteredDisabledPackages(caller.getUserId());
+ return excludedPkgs;
+ });
}
private List<String> removeInvalidPkgsForMeteredDataRestriction(
int userId, List<String> pkgNames) {
- final Set<String> activeAdmins = getActiveAdminPackagesLocked(userId);
- final List<String> excludedPkgs = new ArrayList<>();
- for (int i = pkgNames.size() - 1; i >= 0; --i) {
- final String pkgName = pkgNames.get(i);
- // If the package is an active admin, don't restrict it.
- if (activeAdmins.contains(pkgName)) {
- excludedPkgs.add(pkgName);
- continue;
- }
- // If the package doesn't exist, don't restrict it.
- try {
- if (!mInjector.getIPackageManager().isPackageAvailable(pkgName, userId)) {
+ synchronized (getLockObject()) {
+ final Set<String> activeAdmins = getActiveAdminPackagesLocked(userId);
+ final List<String> excludedPkgs = new ArrayList<>();
+ for (int i = pkgNames.size() - 1; i >= 0; --i) {
+ final String pkgName = pkgNames.get(i);
+ // If the package is an active admin, don't restrict it.
+ if (activeAdmins.contains(pkgName)) {
excludedPkgs.add(pkgName);
+ continue;
}
- } catch (RemoteException e) {
- // Should not happen
+ // If the package doesn't exist, don't restrict it.
+ try {
+ if (!mInjector.getIPackageManager().isPackageAvailable(pkgName, userId)) {
+ excludedPkgs.add(pkgName);
+ }
+ } catch (RemoteException e) {
+ // Should not happen
+ }
}
+ pkgNames.removeAll(excludedPkgs);
+ return excludedPkgs;
}
- pkgNames.removeAll(excludedPkgs);
- return excludedPkgs;
}
@Override
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index c6a5260..bc5e720 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -17,8 +17,10 @@
package com.android.server.wallpaper;
import static android.app.WallpaperManager.COMMAND_REAPPLY;
+import static android.app.WallpaperManager.FLAG_LOCK;
import static android.app.WallpaperManager.FLAG_SYSTEM;
import static android.os.FileObserver.CLOSE_WRITE;
+import static android.os.UserHandle.MIN_SECONDARY_USER_ID;
import static android.os.UserHandle.USER_SYSTEM;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -105,6 +107,7 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
+import java.util.List;
/**
* Tests for the {@link WallpaperManagerService} class.
@@ -261,6 +264,25 @@
}
/**
+ * Tests that the fundamental fields are set by the main WallpaperData constructor
+ */
+ @Test
+ public void testWallpaperDataConstructor() {
+ final int testUserId = MIN_SECONDARY_USER_ID;
+ for (int which: List.of(FLAG_LOCK, FLAG_SYSTEM)) {
+ WallpaperData newWallpaperData = new WallpaperData(testUserId, which);
+ assertEquals(which, newWallpaperData.mWhich);
+ assertEquals(testUserId, newWallpaperData.userId);
+
+ WallpaperData wallpaperData = mService.getWallpaperSafeLocked(testUserId, which);
+ assertEquals(wallpaperData.cropFile.getAbsolutePath(),
+ newWallpaperData.cropFile.getAbsolutePath());
+ assertEquals(wallpaperData.wallpaperFile.getAbsolutePath(),
+ newWallpaperData.wallpaperFile.getAbsolutePath());
+ }
+ }
+
+ /**
* Tests that internal basic data should be correct after boot up.
*/
@Test
@@ -402,10 +424,7 @@
fail("exception occurred while writing system wallpaper attributes");
}
- WallpaperData shouldMatchSystem = new WallpaperData(systemWallpaperData.userId,
- systemWallpaperData.wallpaperFile.getParentFile(),
- systemWallpaperData.wallpaperFile.getAbsolutePath(),
- systemWallpaperData.cropFile.getAbsolutePath());
+ WallpaperData shouldMatchSystem = new WallpaperData(0, FLAG_SYSTEM);
try {
TypedXmlPullParser parser = Xml.newBinaryPullParser();
mService.mWallpaperDataParser.parseWallpaperAttributes(parser, shouldMatchSystem, true);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
index beab107..0b147c3 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java
@@ -18,6 +18,8 @@
import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED;
+import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -33,6 +35,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
+import com.android.internal.util.FrameworkStatsLog;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -155,4 +158,54 @@
// Then: should return false
assertFalse(NotificationRecordLogger.isNonDismissible(p.r));
}
+
+ @Test
+ public void testGetFsiState_stickyHunFlagDisabled_zero() {
+ final int fsiState = NotificationRecordLogger.getFsiState(
+ /* isStickyHunFlagEnabled= */ false,
+ /* hasFullScreenIntent= */ true,
+ /* hasFsiRequestedButDeniedFlag= */ true,
+ /* eventType= */ NOTIFICATION_POSTED);
+ assertEquals(0, fsiState);
+ }
+
+ @Test
+ public void testGetFsiState_isUpdate_zero() {
+ final int fsiState = NotificationRecordLogger.getFsiState(
+ /* isStickyHunFlagEnabled= */ true,
+ /* hasFullScreenIntent= */ true,
+ /* hasFsiRequestedButDeniedFlag= */ true,
+ /* eventType= */ NOTIFICATION_UPDATED);
+ assertEquals(0, fsiState);
+ }
+
+ @Test
+ public void testGetFsiState_hasFsi_allowedEnum() {
+ final int fsiState = NotificationRecordLogger.getFsiState(
+ /* isStickyHunFlagEnabled= */ true,
+ /* hasFullScreenIntent= */ true,
+ /* hasFsiRequestedButDeniedFlag= */ false,
+ /* eventType= */ NOTIFICATION_POSTED);
+ assertEquals(FrameworkStatsLog.NOTIFICATION_REPORTED__FSI_STATE__FSI_ALLOWED, fsiState);
+ }
+
+ @Test
+ public void testGetFsiState_fsiPermissionDenied_deniedEnum() {
+ final int fsiState = NotificationRecordLogger.getFsiState(
+ /* isStickyHunFlagEnabled= */ true,
+ /* hasFullScreenIntent= */ false,
+ /* hasFsiRequestedButDeniedFlag= */ true,
+ /* eventType= */ NOTIFICATION_POSTED);
+ assertEquals(FrameworkStatsLog.NOTIFICATION_REPORTED__FSI_STATE__FSI_DENIED, fsiState);
+ }
+
+ @Test
+ public void testGetFsiState_noFsi_noFsiEnum() {
+ final int fsiState = NotificationRecordLogger.getFsiState(
+ /* isStickyHunFlagEnabled= */ true,
+ /* hasFullScreenIntent= */ false,
+ /* hasFsiRequestedButDeniedFlag= */ false,
+ /* eventType= */ NOTIFICATION_POSTED);
+ assertEquals(FrameworkStatsLog.NOTIFICATION_REPORTED__FSI_STATE__NO_FSI, fsiState);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 48ad86d..47340c1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -36,6 +36,9 @@
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED;
+import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED;
import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_ID_FIELD_NUMBER;
import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.CHANNEL_NAME_FIELD_NUMBER;
import static com.android.os.AtomsProto.PackageNotificationChannelPreferences.IMPORTANCE_FIELD_NUMBER;
@@ -48,7 +51,6 @@
import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_COUNT_LIMIT;
import static com.android.server.notification.PreferencesHelper.NOTIFICATION_CHANNEL_GROUP_COUNT_LIMIT;
import static com.android.server.notification.PreferencesHelper.UNKNOWN_UID;
-
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertNull;
@@ -102,6 +104,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.permission.PermissionManager;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
@@ -185,6 +188,7 @@
@Mock Context mContext;
@Mock ZenModeHelper mMockZenModeHelper;
@Mock AppOpsManager mAppOpsManager;
+ @Mock PermissionManager mPermissionManager;
private NotificationManager.Policy mTestNotificationPolicy;
@@ -218,6 +222,8 @@
InstrumentationRegistry.getContext().getResources());
when(mContext.getContentResolver()).thenReturn(
InstrumentationRegistry.getContext().getContentResolver());
+ when(mPm.getPermissionFlags(any(), any(), any()))
+ .thenReturn(PackageManager.FLAG_PERMISSION_USER_SET);
when(mContext.getPackageManager()).thenReturn(mPm);
when(mContext.getApplicationInfo()).thenReturn(legacy);
// most tests assume badging is enabled
@@ -303,7 +309,8 @@
mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory();
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager,
+ mStatsEventBuilderFactory, false);
resetZenModeHelper();
mAudioAttributes = new AudioAttributes.Builder()
@@ -645,7 +652,7 @@
@Test
public void testReadXml_oldXml_migrates() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
String xml = "<ranking version=\"2\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" uid=\"" + UID_N_MR1
@@ -711,7 +718,7 @@
@Test
public void testReadXml_oldXml_backup_migratesWhenPkgInstalled() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
when(mPm.getPackageUidAsUser("pkg1", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
when(mPm.getPackageUidAsUser("pkg2", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
@@ -789,7 +796,7 @@
@Test
public void testReadXml_newXml_noMigration_showPermissionNotification() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
String xml = "<ranking version=\"3\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -846,7 +853,7 @@
@Test
public void testReadXml_newXml_permissionNotificationOff() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
String xml = "<ranking version=\"3\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -903,7 +910,7 @@
@Test
public void testReadXml_newXml_noMigration_noPermissionNotification() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true);
String xml = "<ranking version=\"4\">\n"
+ "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n"
@@ -959,7 +966,7 @@
@Test
public void testReadXml_oldXml_migration_NoUid() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
String xml = "<ranking version=\"2\">\n"
@@ -992,7 +999,7 @@
@Test
public void testReadXml_newXml_noMigration_NoUid() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID);
String xml = "<ranking version=\"3\">\n"
@@ -1024,7 +1031,7 @@
@Test
public void testChannelXmlForNonBackup_postMigration() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false));
@@ -1110,7 +1117,7 @@
@Test
public void testChannelXmlForBackup_postMigration() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false));
@@ -1202,7 +1209,7 @@
@Test
public void testChannelXmlForBackup_postMigration_noExternal() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair<>(UID_P, PKG_P), new Pair<>(true, false));
@@ -1287,7 +1294,7 @@
@Test
public void testChannelXmlForBackup_postMigration_noLocalSettings() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
appPermissions.put(new Pair<>(1, "first"), new Pair<>(true, false));
@@ -1498,7 +1505,7 @@
new FileNotFoundException("")).thenReturn(resId);
mHelper = new PreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
+ mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false);
NotificationChannel channel =
new NotificationChannel("id", "name", IMPORTANCE_LOW);
@@ -2443,7 +2450,7 @@
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
@@ -2474,7 +2481,7 @@
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
// create notification channel that can bypass dnd, but app is blocked
@@ -2498,7 +2505,7 @@
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
// create notification channel that can bypass dnd, but app is blocked
@@ -2552,7 +2559,7 @@
NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any(), anyInt(), anyBoolean());
@@ -2565,7 +2572,7 @@
mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, 0, 0);
when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
assertFalse(mHelper.areChannelsBypassingDnd());
verify(mMockZenModeHelper, never()).setNotificationPolicy(any(), anyInt(), anyBoolean());
@@ -3668,7 +3675,7 @@
+ "</package>\n"
+ "</ranking>\n";
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
loadByteArrayXml(preQXml.getBytes(), true, USER_SYSTEM);
@@ -3682,7 +3689,7 @@
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
@@ -3752,7 +3759,7 @@
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
@@ -3765,7 +3772,7 @@
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
@@ -3779,7 +3786,7 @@
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
@@ -3795,7 +3802,7 @@
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
@@ -3851,7 +3858,7 @@
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
@@ -3889,7 +3896,7 @@
ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL);
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
loadStreamXml(baos, false, UserHandle.USER_ALL);
@@ -4547,7 +4554,7 @@
@Test
public void testPlaceholderConversationId_shortcutRequired() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
final String xml = "<ranking version=\"1\">\n"
@@ -4567,7 +4574,7 @@
@Test
public void testNormalConversationId_shortcutRequired() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
final String xml = "<ranking version=\"1\">\n"
@@ -4587,7 +4594,7 @@
@Test
public void testNoConversationId_shortcutRequired() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
final String xml = "<ranking version=\"1\">\n"
@@ -4607,7 +4614,7 @@
@Test
public void testDeleted_noTime() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
final String xml = "<ranking version=\"1\">\n"
@@ -4627,7 +4634,7 @@
@Test
public void testDeleted_twice() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.createNotificationChannel(
@@ -4642,7 +4649,7 @@
@Test
public void testDeleted_recentTime() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.createNotificationChannel(
@@ -4661,7 +4668,7 @@
null);
parser.nextTag();
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.readXml(parser, true, USER_SYSTEM);
@@ -4673,7 +4680,7 @@
@Test
public void testUnDelete_time() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
mHelper.createNotificationChannel(
@@ -4695,7 +4702,7 @@
@Test
public void testDeleted_longTime() throws Exception {
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
- mPermissionHelper, mLogger,
+ mPermissionHelper, mPermissionManager, mLogger,
mAppOpsManager, mStatsEventBuilderFactory, false);
long time = System.currentTimeMillis() - (DateUtils.DAY_IN_MILLIS * 30);
@@ -5464,4 +5471,93 @@
verifyZeroInteractions(mHandler);
}
+
+ @Test
+ public void testGetFsiState_flagDisabled_zero() {
+ final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0,
+ /* requestedFsiPermission */ true, /* isFlagEnabled= */ false);
+
+ assertEquals(0, fsiState);
+ }
+
+ @Test
+ public void testGetFsiState_appDidNotRequest_enumNotRequested() {
+ final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0,
+ /* requestedFsiPermission */ false, /* isFlagEnabled= */ true);
+
+ assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED, fsiState);
+ }
+
+ @Test
+ public void testGetFsiState_permissionGranted_enumGranted() {
+ when(mPermissionManager.checkPermissionForPreflight(any(), any()))
+ .thenReturn(PermissionManager.PERMISSION_GRANTED);
+
+ final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0,
+ /* requestedFsiPermission= */ true, /* isFlagEnabled= */ true);
+
+ assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED, fsiState);
+ }
+
+ @Test
+ public void testGetFsiState_permissionSoftDenied_enumDenied() {
+ when(mPermissionManager.checkPermissionForPreflight(any(), any()))
+ .thenReturn(PermissionManager.PERMISSION_SOFT_DENIED);
+
+ final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0,
+ /* requestedFsiPermission = */ true, /* isFlagEnabled= */ true);
+
+ assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED, fsiState);
+ }
+
+ @Test
+ public void testGetFsiState_permissionHardDenied_enumDenied() {
+ when(mPermissionManager.checkPermissionForPreflight(any(), any()))
+ .thenReturn(PermissionManager.PERMISSION_HARD_DENIED);
+
+ final int fsiState = mHelper.getFsiState("pkg", /* uid= */ 0,
+ /* requestedFsiPermission = */ true, /* isFlagEnabled= */ true);
+
+ assertEquals(PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED, fsiState);
+ }
+
+ @Test
+ public void testIsFsiPermissionUserSet_appDidNotRequest_false() {
+ final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0,
+ /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED,
+ /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET,
+ /* isStickyHunFlagEnabled= */ true);
+
+ assertFalse(isUserSet);
+ }
+
+ @Test
+ public void testIsFsiPermissionUserSet_flagDisabled_false() {
+ final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0,
+ /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED,
+ /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET,
+ /* isStickyHunFlagEnabled= */ false);
+
+ assertFalse(isUserSet);
+ }
+
+ @Test
+ public void testIsFsiPermissionUserSet_userSet_true() {
+ final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0,
+ /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED,
+ /* currentPermissionFlags= */ PackageManager.FLAG_PERMISSION_USER_SET,
+ /* isStickyHunFlagEnabled= */ true);
+
+ assertTrue(isUserSet);
+ }
+
+ @Test
+ public void testIsFsiPermissionUserSet_notUserSet_false() {
+ final boolean isUserSet = mHelper.isFsiPermissionUserSet("pkg", /* uid= */ 0,
+ /* fsiState = */ PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED,
+ /* currentPermissionFlags= */ ~PackageManager.FLAG_PERMISSION_USER_SET,
+ /* isStickyHunFlagEnabled= */ true);
+
+ assertFalse(isUserSet);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index b59f027..1126726 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1346,6 +1346,16 @@
activity1.setVisible(false);
abortTransition.abort();
assertTrue(activity1.isVisible());
+
+ // The mLaunchTaskBehind flag of an invisible initializing activity should not be cleared.
+ final Transition noChangeTransition = controller.createTransition(TRANSIT_OPEN);
+ noChangeTransition.collect(activity1);
+ activity1.setVisibleRequested(false);
+ activity1.setState(ActivityRecord.State.INITIALIZING, "test");
+ activity1.mLaunchTaskBehind = true;
+ mWm.mSyncEngine.abort(noChangeTransition.getSyncId());
+ noChangeTransition.finishTransition();
+ assertTrue(activity1.mLaunchTaskBehind);
}
@Test
diff --git a/tests/FixVibrateSetting/Android.bp b/tests/FixVibrateSetting/Android.bp
deleted file mode 100644
index bd7c701..0000000
--- a/tests/FixVibrateSetting/Android.bp
+++ /dev/null
@@ -1,15 +0,0 @@
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_app {
- name: "FixVibrateSetting",
- srcs: ["**/*.java"],
- sdk_version: "current",
- certificate: "platform",
-}
diff --git a/tests/FixVibrateSetting/AndroidManifest.xml b/tests/FixVibrateSetting/AndroidManifest.xml
deleted file mode 100644
index c2d5918..0000000
--- a/tests/FixVibrateSetting/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.fixvibratesetting">
- <uses-permission android:name="android.permission.VIBRATE"/>
-
- <application>
- <activity android:name="FixVibrateSetting"
- android:label="@string/app_label"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.DEFAULT"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/tests/FixVibrateSetting/OWNERS b/tests/FixVibrateSetting/OWNERS
deleted file mode 100644
index cc63ceb..0000000
--- a/tests/FixVibrateSetting/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /services/core/java/com/android/server/vibrator/OWNERS
diff --git a/tests/FixVibrateSetting/res/drawable-hdpi/stat_sys_warning.png b/tests/FixVibrateSetting/res/drawable-hdpi/stat_sys_warning.png
deleted file mode 100644
index 37c8853..0000000
--- a/tests/FixVibrateSetting/res/drawable-hdpi/stat_sys_warning.png
+++ /dev/null
Binary files differ
diff --git a/tests/FixVibrateSetting/res/drawable-mdpi/stat_sys_warning.png b/tests/FixVibrateSetting/res/drawable-mdpi/stat_sys_warning.png
deleted file mode 100644
index be00f47..0000000
--- a/tests/FixVibrateSetting/res/drawable-mdpi/stat_sys_warning.png
+++ /dev/null
Binary files differ
diff --git a/tests/FixVibrateSetting/res/layout/fix_vibrate.xml b/tests/FixVibrateSetting/res/layout/fix_vibrate.xml
deleted file mode 100644
index c505e65..0000000
--- a/tests/FixVibrateSetting/res/layout/fix_vibrate.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- >
-
- <TextView android:id="@+id/current_setting"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="left"
- android:layout_marginTop="30dp"
- android:layout_marginBottom="50dp"
- android:paddingLeft="8dp"
- android:paddingTop="4dp"
- android:textSize="20sp"
- android:textColor="#ffffffff"
- />
-
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:orientation="horizontal"
- >
- <Button android:id="@+id/fix"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/fix"
- />
-
- <Button android:id="@+id/unfix"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/unfix"
- />
- </LinearLayout>
-
- <Button android:id="@+id/test"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="@string/test"
- />
-
-</LinearLayout>
-
diff --git a/tests/FixVibrateSetting/res/values/strings.xml b/tests/FixVibrateSetting/res/values/strings.xml
deleted file mode 100644
index 269cef3..0000000
--- a/tests/FixVibrateSetting/res/values/strings.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<resources>
- <string name="app_label">Fix Vibrate</string>
- <string name="title">Fix vibrate setting</string>
- <string name="current_setting">"Ringer: %1$s\nNotification: %2$s"</string>
- <string name="fix">Fix the setting</string>
- <string name="unfix">Break the setting</string>
- <string name="test">Test the setting</string>
-</resources>
-
diff --git a/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java b/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java
deleted file mode 100644
index 761efe4..0000000
--- a/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2008 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.fixvibratesetting;
-
-import android.app.Activity;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.media.AudioManager;
-import android.util.Log;
-import android.view.View;
-import android.widget.TextView;
-import android.os.Bundle;
-
-public class FixVibrateSetting extends Activity implements View.OnClickListener
-{
- AudioManager mAudioManager;
- NotificationManager mNotificationManager;
- TextView mCurrentSetting;
- View mFix;
- View mUnfix;
- View mTest;
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- setContentView(R.layout.fix_vibrate);
-
- mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);
- mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
-
- mCurrentSetting = (TextView)findViewById(R.id.current_setting);
-
- mFix = findViewById(R.id.fix);
- mFix.setOnClickListener(this);
-
- mUnfix = findViewById(R.id.unfix);
- mUnfix.setOnClickListener(this);
-
- mTest = findViewById(R.id.test);
- mTest.setOnClickListener(this);
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- update();
- }
-
- private String getSettingValue(int vibrateType) {
- int setting = mAudioManager.getVibrateSetting(vibrateType);
- switch (setting) {
- case AudioManager.VIBRATE_SETTING_OFF:
- return "off";
- case AudioManager.VIBRATE_SETTING_ON:
- return "on";
- case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
- return "silent-only";
- default:
- return "unknown";
- }
- }
-
- public void onClick(View v) {
- if (v == mFix) {
- fix();
- update();
- } else if (v == mUnfix) {
- unfix();
- update();
- } else if (v == mTest) {
- test();
- update();
- }
- }
-
- private void update() {
- String ringer = getSettingValue(AudioManager.VIBRATE_TYPE_RINGER);
- String notification = getSettingValue(AudioManager.VIBRATE_TYPE_NOTIFICATION);
- String text = getString(R.string.current_setting, ringer, notification);
- mCurrentSetting.setText(text);
- }
-
- private void fix() {
- mAudioManager.setVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION,
- AudioManager.VIBRATE_SETTING_ON);
- }
-
- private void unfix() {
- mAudioManager.setVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION,
- AudioManager.VIBRATE_SETTING_OFF);
- }
-
- private void test() {
- Intent intent = new Intent(this, FixVibrateSetting.class);
- PendingIntent pending = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
-
- Notification n = new Notification.Builder(this)
- .setSmallIcon(R.drawable.stat_sys_warning)
- .setTicker("Test notification")
- .setWhen(System.currentTimeMillis())
- .setContentTitle("Test notification")
- .setContentText("Test notification")
- .setContentIntent(pending)
- .setVibrate(new long[] { 0, 700, 500, 1000 })
- .setAutoCancel(true)
- .build();
-
- mNotificationManager.notify(1, n);
- }
-}
-