Merge "Fix notifications wrong corners after "clear all"" into udc-dev
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 785470f..79b68c1 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -571,6 +571,12 @@
*/
public static final int BUBBLE_PREFERENCE_SELECTED = 2;
+ /**
+ * Maximum length of the component name of a registered NotificationListenerService.
+ * @hide
+ */
+ public static int MAX_SERVICE_COMPONENT_NAME_LENGTH = 500;
+
@UnsupportedAppUsage
private static INotificationManager sService;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 06635ee..6d82922 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -683,7 +683,7 @@
public static final int BIND_EXTERNAL_SERVICE = 0x80000000;
/**
- * Works in the same way as {@link #BIND_EXTERNAL_SERVICE}, but it's defined as a (@code long)
+ * Works in the same way as {@link #BIND_EXTERNAL_SERVICE}, but it's defined as a {@code long}
* value that is compatible to {@link BindServiceFlags}.
*/
public static final long BIND_EXTERNAL_SERVICE_LONG = 1L << 62;
@@ -1413,7 +1413,7 @@
* </ul>
* <p>
* If a shared storage device is emulated (as determined by
- * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+ * {@link Environment#isExternalStorageEmulated(File)}), its contents are
* backed by a private user data partition, which means there is little
* benefit to storing data here instead of the private directories returned
* by {@link #getFilesDir()}, etc.
@@ -1501,7 +1501,7 @@
* </ul>
* <p>
* If a shared storage device is emulated (as determined by
- * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+ * {@link Environment#isExternalStorageEmulated(File)}), its contents are
* backed by a private user data partition, which means there is little
* benefit to storing data here instead of the private directories returned
* by {@link #getFilesDir()}, etc.
@@ -1812,7 +1812,7 @@
* </ul>
* <p>
* If a shared storage device is emulated (as determined by
- * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+ * {@link Environment#isExternalStorageEmulated(File)}), its contents are
* backed by a private user data partition, which means there is little
* benefit to storing data here instead of the private directory returned by
* {@link #getCacheDir()}.
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 105b38a..4b883cd 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -3045,10 +3045,6 @@
* The update ownership enforcement can only be enabled on initial installation. Set
* this to {@code true} on package update is a no-op.
*
- * Apps may opt themselves out of update ownership by setting the
- * <a href="https://developer.android.com/guide/topics/manifest/manifest-element.html#allowupdateownership">android:alllowUpdateOwnership</a>
- * attribute in their manifest to <code>false</code>.
- *
* Note: To enable the update ownership enforcement, the installer must have the
* {@link android.Manifest.permission#ENFORCE_UPDATE_OWNERSHIP ENFORCE_UPDATE_OWNERSHIP}
* permission.
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 885060f..c3d5b71 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -2003,13 +2003,25 @@
private int mHashCode = 0;
- private boolean containsValue(int resId, boolean force) {
+ private int findValue(int resId, boolean force) {
for (int i = 0; i < mCount; ++i) {
if (mResId[i] == resId && mForce[i] == force) {
- return true;
+ return i;
}
}
- return false;
+ return -1;
+ }
+
+ private void moveToLast(int index) {
+ if (index < 0 || index >= mCount - 1) {
+ return;
+ }
+ final int id = mResId[index];
+ final boolean force = mForce[index];
+ System.arraycopy(mResId, index + 1, mResId, index, mCount - index - 1);
+ mResId[mCount - 1] = id;
+ System.arraycopy(mForce, index + 1, mForce, index, mCount - index - 1);
+ mForce[mCount - 1] = force;
}
public void append(int resId, boolean force) {
@@ -2022,15 +2034,17 @@
}
// Some apps tend to keep adding same resources over and over, let's protect from it.
- if (containsValue(resId, force)) {
- return;
+ // Note: the order still matters, as the values that come later override the earlier
+ // ones.
+ final int index = findValue(resId, force);
+ if (index >= 0) {
+ moveToLast(index);
+ } else {
+ mResId = GrowingArrayUtils.append(mResId, mCount, resId);
+ mForce = GrowingArrayUtils.append(mForce, mCount, force);
+ mCount++;
+ mHashCode = 31 * (31 * mHashCode + resId) + (force ? 1 : 0);
}
-
- mResId = GrowingArrayUtils.append(mResId, mCount, resId);
- mForce = GrowingArrayUtils.append(mForce, mCount, force);
- mCount++;
-
- mHashCode = 31 * (31 * mHashCode + resId) + (force ? 1 : 0);
}
/**
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 41ef44e..3cac1e5 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2657,17 +2657,6 @@
}
}
- if (windowGainingFocus == null) {
- windowGainingFocus = view.getWindowToken();
- if (windowGainingFocus == null) {
- Log.e(TAG, "ABORT input: ServedView must be attached to a Window");
- return false;
- }
- startInputFlags = getStartInputFlags(view, startInputFlags);
- softInputMode = view.getViewRootImpl().mWindowAttributes.softInputMode;
- windowFlags = view.getViewRootImpl().mWindowAttributes.flags;
- }
-
// Now we need to get an input connection from the served view.
// This is complicated in a couple ways: we can't be holding our lock
// when calling out to the view, and we need to make sure we call into
@@ -2690,6 +2679,17 @@
return false;
}
+ if (windowGainingFocus == null) {
+ windowGainingFocus = view.getWindowToken();
+ if (windowGainingFocus == null) {
+ Log.e(TAG, "ABORT input: ServedView must be attached to a Window");
+ return false;
+ }
+ startInputFlags = getStartInputFlags(view, startInputFlags);
+ softInputMode = view.getViewRootImpl().mWindowAttributes.softInputMode;
+ windowFlags = view.getViewRootImpl().mWindowAttributes.flags;
+ }
+
// Okay we are now ready to call into the served view and have it
// do its stuff.
// Life is good: let's hook everything up!
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 7452daa..65b5979 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -56,6 +56,7 @@
import android.os.UserManager;
import android.provider.Settings;
import android.telecom.TelecomManager;
+import android.util.Log;
import android.util.Slog;
import android.view.View;
import android.widget.Button;
@@ -124,16 +125,19 @@
String className = intentReceived.getComponent().getClassName();
final int targetUserId;
final String userMessage;
+ final UserInfo managedProfile;
if (className.equals(FORWARD_INTENT_TO_PARENT)) {
userMessage = getForwardToPersonalMessage();
targetUserId = getProfileParent();
+ managedProfile = null;
getMetricsLogger().write(
new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE)
.setSubtype(MetricsEvent.PARENT_PROFILE));
} else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
userMessage = getForwardToWorkMessage();
- targetUserId = getManagedProfile();
+ managedProfile = getManagedProfile();
+ targetUserId = managedProfile == null ? UserHandle.USER_NULL : managedProfile.id;
getMetricsLogger().write(
new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE)
@@ -142,6 +146,7 @@
Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly");
userMessage = null;
targetUserId = UserHandle.USER_NULL;
+ managedProfile = null;
}
if (targetUserId == UserHandle.USER_NULL) {
// This covers the case where there is no parent / managed profile.
@@ -185,27 +190,49 @@
finish();
// When switching to the work profile, ask the user for consent before launching
} else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
- maybeShowUserConsentMiniResolver(result, newIntent, targetUserId);
+ maybeShowUserConsentMiniResolver(result, newIntent, managedProfile);
}
}, getApplicationContext().getMainExecutor());
}
private void maybeShowUserConsentMiniResolver(
- ResolveInfo target, Intent launchIntent, int targetUserId) {
+ ResolveInfo target, Intent launchIntent, UserInfo managedProfile) {
if (target == null || isIntentForwarderResolveInfo(target) || !isDeviceProvisioned()) {
finish();
return;
}
- if (launchIntent.getBooleanExtra(EXTRA_SKIP_USER_CONFIRMATION, /* defaultValue= */ false)
- && getCallingPackage() != null
- && PERMISSION_GRANTED == getPackageManager().checkPermission(
- INTERACT_ACROSS_USERS, getCallingPackage())) {
+ int targetUserId = managedProfile == null ? UserHandle.USER_NULL : managedProfile.id;
+ String callingPackage = getCallingPackage();
+ boolean privilegedCallerAskedToSkipUserConsent =
+ launchIntent.getBooleanExtra(
+ EXTRA_SKIP_USER_CONFIRMATION, /* defaultValue= */ false)
+ && callingPackage != null
+ && PERMISSION_GRANTED == getPackageManager().checkPermission(
+ INTERACT_ACROSS_USERS, callingPackage);
+
+ DevicePolicyManager devicePolicyManager =
+ getSystemService(DevicePolicyManager.class);
+ ComponentName profileOwnerName = devicePolicyManager.getProfileOwnerAsUser(targetUserId);
+ boolean intentToLaunchProfileOwner = profileOwnerName != null
+ && profileOwnerName.getPackageName().equals(target.getComponentInfo().packageName);
+
+ if (privilegedCallerAskedToSkipUserConsent || intentToLaunchProfileOwner) {
+ Log.i("IntentForwarderActivity", String.format(
+ "Skipping user consent for redirection into the managed profile for intent [%s]"
+ + ", privilegedCallerAskedToSkipUserConsent=[%s]"
+ + ", intentToLaunchProfileOwner=[%s]",
+ launchIntent, privilegedCallerAskedToSkipUserConsent,
+ intentToLaunchProfileOwner));
startActivityAsCaller(launchIntent, targetUserId);
finish();
return;
}
+ Log.i("IntentForwarderActivity", String.format(
+ "Showing user consent for redirection into the managed profile for intent [%s] and "
+ + " calling package [%s]",
+ launchIntent, callingPackage));
int layoutId = R.layout.miniresolver;
setContentView(layoutId);
@@ -245,8 +272,7 @@
View telephonyInfo = findViewById(R.id.miniresolver_info_section);
- DevicePolicyManager devicePolicyManager =
- getSystemService(DevicePolicyManager.class);
+
// Additional information section is work telephony specific. Therefore, it is only shown
// for telephony related intents, when all sim subscriptions are in the work profile.
if ((isDialerIntent(launchIntent) || isTextMessageIntent(launchIntent))
@@ -507,20 +533,18 @@
}
/**
- * Returns the userId of the managed profile for this device or UserHandle.USER_NULL if there is
- * no managed profile.
+ * Returns the managed profile for this device or null if there is no managed profile.
*
- * TODO: Remove the assumption that there is only one managed profile
- * on the device.
+ * TODO: Remove the assumption that there is only one managed profile on the device.
*/
- private int getManagedProfile() {
+ @Nullable private UserInfo getManagedProfile() {
List<UserInfo> relatedUsers = mInjector.getUserManager().getProfiles(UserHandle.myUserId());
for (UserInfo userInfo : relatedUsers) {
- if (userInfo.isManagedProfile()) return userInfo.id;
+ if (userInfo.isManagedProfile()) return userInfo;
}
Slog.wtf(TAG, FORWARD_INTENT_TO_MANAGED_PROFILE
+ " has been called, but there is no managed profile");
- return UserHandle.USER_NULL;
+ return null;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index b8407c46..6d7f761 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -567,7 +567,16 @@
mPipBoundsState.getDisplayBounds());
mFinishCallback = (wct, wctCB) -> {
mPipOrganizer.onExitPipFinished(taskInfo);
- if (!Transitions.SHELL_TRANSITIONS_ROTATION && toFullscreen) {
+
+ // TODO(b/286346098): remove the OPEN app flicker completely
+ // not checking if we go to fullscreen helps avoid getting pip into an inconsistent
+ // state after the flicker occurs. This is a temp solution until flicker is removed.
+ if (!Transitions.SHELL_TRANSITIONS_ROTATION) {
+ // will help to debug the case when we are not exiting to fullscreen
+ if (!toFullscreen) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: startExitAnimation() not exiting to fullscreen", TAG);
+ }
wct = wct != null ? wct : new WindowContainerTransaction();
wct.setBounds(pipTaskToken, null);
mPipOrganizer.applyWindowingModeChangeOnExit(wct, TRANSITION_DIRECTION_LEAVE_PIP);
@@ -831,7 +840,7 @@
}
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
- final Rect currentBounds = taskInfo.configuration.windowConfiguration.getBounds();
+ final Rect currentBounds = pipChange.getStartAbsBounds();
int rotationDelta = deltaRotation(startRotation, endRotation);
Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
taskInfo.pictureInPictureParams, currentBounds, destinationBounds);
@@ -856,6 +865,9 @@
final int enterAnimationType = mEnterAnimationType;
if (enterAnimationType == ANIM_TYPE_ALPHA) {
startTransaction.setAlpha(leash, 0f);
+ } else {
+ // set alpha to 1, because for multi-activity PiP it will create a new task with alpha 0
+ startTransaction.setAlpha(leash, 1f);
}
startTransaction.apply();
@@ -1047,7 +1059,7 @@
// When the PIP window is visible and being a part of the transition, such as display
// rotation, we need to update its bounds and rounded corner.
final SurfaceControl leash = pipChange.getLeash();
- final Rect destBounds = mPipBoundsState.getBounds();
+ final Rect destBounds = mPipOrganizer.getCurrentOrAnimatingBounds();
final boolean isInPip = mPipTransitionState.isInPip();
mSurfaceTransactionHelper
.crop(startTransaction, leash, destBounds)
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 01d8967..64f7598 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
@@ -2441,6 +2441,8 @@
mSplitLayout.setFreezeDividerWindow(false);
final StageChangeRecord record = new StageChangeRecord();
+ final int transitType = info.getType();
+ boolean hasEnteringPip = false;
for (int iC = 0; iC < info.getChanges().size(); ++iC) {
final TransitionInfo.Change change = info.getChanges().get(iC);
if (change.getMode() == TRANSIT_CHANGE
@@ -2448,6 +2450,10 @@
mSplitLayout.update(startTransaction);
}
+ if (mMixedHandler.isEnteringPip(change, transitType)) {
+ hasEnteringPip = true;
+ }
+
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
if (taskInfo == null) continue;
if (taskInfo.token.equals(mRootTaskInfo.token)) {
@@ -2496,6 +2502,13 @@
}
}
}
+
+ if (hasEnteringPip) {
+ mMixedHandler.animatePendingEnterPipFromSplit(transition, info,
+ startTransaction, finishTransaction, finishCallback);
+ return true;
+ }
+
final ArraySet<StageTaskListener> dismissStages = record.getShouldDismissedStage();
if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0
|| dismissStages.size() == 1) {
@@ -2828,18 +2841,24 @@
}
}
+ final ArrayMap<Integer, SurfaceControl> dismissingTasks = new ArrayMap<>();
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo == null) continue;
+ if (getStageOfTask(taskInfo) != null
+ || getSplitItemPosition(change.getLastParent()) != SPLIT_POSITION_UNDEFINED) {
+ dismissingTasks.put(taskInfo.taskId, change.getLeash());
+ }
+ }
+
+
if (shouldBreakPairedTaskInRecents(dismissReason)) {
// Notify recents if we are exiting in a way that breaks the pair, and disable further
// updates to splits in the recents until we enter split again
mRecentTasks.ifPresent(recentTasks -> {
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (taskInfo != null && (getStageOfTask(taskInfo) != null
- || getSplitItemPosition(change.getLastParent())
- != SPLIT_POSITION_UNDEFINED)) {
- recentTasks.removeSplitPair(taskInfo.taskId);
- }
+ for (int i = dismissingTasks.keySet().size() - 1; i >= 0; --i) {
+ recentTasks.removeSplitPair(dismissingTasks.keyAt(i));
}
});
}
@@ -2857,6 +2876,10 @@
t.hide(toStage == STAGE_TYPE_MAIN ? mSideStage.mRootLeash : mMainStage.mRootLeash);
t.setPosition(toStage == STAGE_TYPE_MAIN
? mMainStage.mRootLeash : mSideStage.mRootLeash, 0, 0);
+ } else {
+ for (int i = dismissingTasks.keySet().size() - 1; i >= 0; --i) {
+ finishT.hide(dismissingTasks.valueAt(i));
+ }
}
if (toStage == STAGE_TYPE_UNDEFINED) {
@@ -2866,7 +2889,7 @@
}
// Hide divider and dim layer on transition finished.
- setDividerVisibility(false, finishT);
+ setDividerVisibility(false, t);
finishT.hide(mMainStage.mDimLayer);
finishT.hide(mSideStage.mDimLayer);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index e83780a..d0a361a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -25,6 +25,7 @@
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
import static com.android.wm.shell.util.TransitionUtil.isOpeningType;
@@ -447,7 +448,8 @@
}
finishCallback.onTransitionFinished(mixed.mFinishWCT, wctCB);
};
- if (isGoingHome) {
+ if (isGoingHome || mSplitHandler.getSplitItemPosition(pipChange.getLastParent())
+ != SPLIT_POSITION_UNDEFINED) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is actually mixed "
+ "since entering-PiP caused us to leave split and return home.");
// We need to split the transition into 2 parts: the pip part (animated by pip)
@@ -476,6 +478,7 @@
}
}
+ mPipHandler.setEnterAnimationType(ANIM_TYPE_ALPHA);
mPipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction,
finishCB);
// Dispatch the rest of the transition normally. This will most-likely be taken by
@@ -516,10 +519,27 @@
}
/**
+ * This is intended to be called by SplitCoordinator as a helper to mix a split handling
+ * transition with an entering-pip change. The use-case for this is when an auto-pip change
+ * gets collected into the transition which has already claimed by
+ * StageCoordinator.handleRequest. This happens when launching a fullscreen app while having an
+ * auto-pip activity in the foreground split pair.
+ */
+ // TODO(b/287704263): Remove when split/mixed are reversed.
+ public boolean animatePendingEnterPipFromSplit(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
+ Transitions.TransitionFinishCallback finishCallback) {
+ final MixedTransition mixed = new MixedTransition(
+ MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, transition);
+ mActiveTransitions.add(mixed);
+ return animateEnterPipFromSplit(mixed, info, startT, finishT, finishCallback);
+ }
+
+ /**
* This is intended to be called by SplitCoordinator as a helper to mix an already-pending
* split transition with a display-change. The use-case for this is when a display
* change/rotation gets collected into a split-screen enter/exit transition which has already
- * been claimed by StageCoordinator.handleRequest . This happens during launcher tests.
+ * been claimed by StageCoordinator.handleRequest. This happens during launcher tests.
*/
public boolean animatePendingSplitWithDisplayChange(@NonNull IBinder transition,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT,
@@ -657,6 +677,13 @@
return mPipHandler.requestHasPipEnter(request);
}
+ /** Whether a particular change is a window that is entering pip. */
+ // TODO(b/287704263): Remove when split/mixed are reversed.
+ public boolean isEnteringPip(TransitionInfo.Change change,
+ @WindowManager.TransitionType int transitType) {
+ return mPipHandler.isEnteringPip(change, transitType);
+ }
+
@Override
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 3b05651..44c76de 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -75,6 +75,7 @@
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitDecorManager;
import com.android.wm.shell.common.split.SplitLayout;
+import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
@@ -101,6 +102,7 @@
@Mock private SurfaceSession mSurfaceSession;
@Mock private IconProvider mIconProvider;
@Mock private ShellExecutor mMainExecutor;
+ @Mock private DefaultMixedHandler mMixedHandler;
private SplitLayout mSplitLayout;
private MainStage mMainStage;
private SideStage mSideStage;
@@ -131,6 +133,7 @@
mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
mTransactionPool, mMainExecutor, Optional.empty());
+ mStageCoordinator.setMixedHandler(mMixedHandler);
mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
.when(mTransitions).startTransition(anyInt(), any(), any());
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 0e9c162..651c732 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -156,6 +156,7 @@
TYPE_REMOTE_GAME_CONSOLE,
TYPE_REMOTE_CAR,
TYPE_REMOTE_SMARTWATCH,
+ TYPE_REMOTE_SMARTPHONE,
TYPE_GROUP
})
@Retention(RetentionPolicy.SOURCE)
@@ -343,6 +344,17 @@
public static final int TYPE_REMOTE_SMARTWATCH = 1009;
/**
+ * Indicates the route is a remote smartphone.
+ *
+ * <p>A remote device uses a routing protocol managed by the application, as opposed to the
+ * routing being done by the system.
+ *
+ * @see #getType
+ * @hide
+ */
+ public static final int TYPE_REMOTE_SMARTPHONE = 1010;
+
+ /**
* Indicates the route is a group of devices.
*
* @see #getType
@@ -546,32 +558,8 @@
return mFeatures;
}
- // TODO (b/278728942): Add the following once the symbols are published in the SDK. Until then,
- // adding them would cause the generated link to be broken.
- // @see #TYPE_REMOTE_TABLET
- // @see #TYPE_REMOTE_TABLET_DOCKED
- // @see #TYPE_REMOTE_COMPUTER
- // @see #TYPE_REMOTE_GAME_CONSOLE
- // @see #TYPE_REMOTE_CAR
- // @see #TYPE_REMOTE_SMARTWATCH
/**
* Returns the type of this route.
- *
- * @see #TYPE_UNKNOWN
- * @see #TYPE_BUILTIN_SPEAKER
- * @see #TYPE_WIRED_HEADSET
- * @see #TYPE_WIRED_HEADPHONES
- * @see #TYPE_BLUETOOTH_A2DP
- * @see #TYPE_HDMI
- * @see #TYPE_DOCK
- * @see #TYPE_USB_DEVICE
- * @see #TYPE_USB_ACCESSORY
- * @see #TYPE_USB_HEADSET
- * @see #TYPE_HEARING_AID
- * @see #TYPE_REMOTE_TV
- * @see #TYPE_REMOTE_SPEAKER
- * @see #TYPE_REMOTE_AUDIO_VIDEO_RECEIVER
- * @see #TYPE_GROUP
*/
@Type
public int getType() {
@@ -954,6 +942,8 @@
return "REMOTE_CAR";
case TYPE_REMOTE_SMARTWATCH:
return "REMOTE_SMARTWATCH";
+ case TYPE_REMOTE_SMARTPHONE:
+ return "REMOTE_SMARTPHONE";
case TYPE_GROUP:
return "GROUP";
case TYPE_UNKNOWN:
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index b5e4fa3..af06d73 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -243,7 +243,9 @@
return mHelper != null ? mHelper.packageName : null;
}
- public void updateState(@NonNull String packageName, int uid, boolean isEnabled) {
+ /** Updates enabled state based on associated package. */
+ public void updateState(
+ @NonNull String packageName, int uid, boolean isEnableAllowed, boolean isEnabled) {
mHelper.updatePackageDetails(packageName, uid);
if (mAppOpsManager == null) {
mAppOpsManager = getContext().getSystemService(AppOpsManager.class);
@@ -254,7 +256,9 @@
final boolean ecmEnabled = getContext().getResources().getBoolean(
com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
final boolean appOpsAllowed = !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
- if (isEnabled) {
+ if (!isEnableAllowed && !isEnabled) {
+ setEnabled(false);
+ } else if (isEnabled) {
setEnabled(true);
} else if (appOpsAllowed && isDisabledByAppOps()) {
setEnabled(true);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index 632120e..b10d794 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -19,6 +19,7 @@
import static android.media.MediaRoute2Info.TYPE_REMOTE_CAR;
import static android.media.MediaRoute2Info.TYPE_REMOTE_COMPUTER;
import static android.media.MediaRoute2Info.TYPE_REMOTE_GAME_CONSOLE;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_SMARTPHONE;
import static android.media.MediaRoute2Info.TYPE_REMOTE_SMARTWATCH;
import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
import static android.media.MediaRoute2Info.TYPE_REMOTE_TABLET;
@@ -103,6 +104,9 @@
case TYPE_REMOTE_SMARTWATCH:
resId = R.drawable.ic_media_smartwatch;
break;
+ case TYPE_REMOTE_SMARTPHONE:
+ resId = R.drawable.ic_smartphone;
+ break;
case TYPE_REMOTE_SPEAKER:
default:
resId = R.drawable.ic_media_speaker_device;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index ffc0479..2a486a9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -26,6 +26,7 @@
import static android.media.MediaRoute2Info.TYPE_REMOTE_CAR;
import static android.media.MediaRoute2Info.TYPE_REMOTE_COMPUTER;
import static android.media.MediaRoute2Info.TYPE_REMOTE_GAME_CONSOLE;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_SMARTPHONE;
import static android.media.MediaRoute2Info.TYPE_REMOTE_SMARTWATCH;
import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
import static android.media.MediaRoute2Info.TYPE_REMOTE_TABLET;
@@ -90,6 +91,7 @@
MediaRouter2Manager mRouterManager;
@VisibleForTesting
String mPackageName;
+ boolean mIsScanning = false;
private MediaDevice mCurrentConnectedDevice;
private LocalBluetoothManager mBluetoothManager;
@@ -109,22 +111,29 @@
@Override
public void startScan() {
- mMediaDevices.clear();
- mRouterManager.registerCallback(mExecutor, mMediaRouterCallback);
- mRouterManager.registerScanRequest();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
- && !TextUtils.isEmpty(mPackageName)) {
- RouteListingPreference routeListingPreference =
- mRouterManager.getRouteListingPreference(mPackageName);
- Api34Impl.onRouteListingPreferenceUpdated(routeListingPreference, mPreferenceItemMap);
+ if (!mIsScanning) {
+ mMediaDevices.clear();
+ mRouterManager.registerCallback(mExecutor, mMediaRouterCallback);
+ mRouterManager.registerScanRequest();
+ mIsScanning = true;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+ && !TextUtils.isEmpty(mPackageName)) {
+ RouteListingPreference routeListingPreference =
+ mRouterManager.getRouteListingPreference(mPackageName);
+ Api34Impl.onRouteListingPreferenceUpdated(routeListingPreference,
+ mPreferenceItemMap);
+ }
+ refreshDevices();
}
- refreshDevices();
}
@Override
public void stopScan() {
- mRouterManager.unregisterCallback(mMediaRouterCallback);
- mRouterManager.unregisterScanRequest();
+ if (mIsScanning) {
+ mRouterManager.unregisterCallback(mMediaRouterCallback);
+ mRouterManager.unregisterScanRequest();
+ mIsScanning = false;
+ }
}
/**
@@ -547,6 +556,7 @@
case TYPE_REMOTE_GAME_CONSOLE:
case TYPE_REMOTE_CAR:
case TYPE_REMOTE_SMARTWATCH:
+ case TYPE_REMOTE_SMARTPHONE:
mediaDevice = new InfoMediaDevice(mContext, mRouterManager, route,
mPackageName, mPreferenceItemMap.get(route.getId()));
break;
@@ -699,20 +709,19 @@
List<MediaRoute2Info> selectedRouteInfos, List<MediaRoute2Info> infolist,
List<RouteListingPreference.Item> preferenceRouteListing) {
final List<MediaRoute2Info> sortedInfoList = new ArrayList<>(selectedRouteInfos);
+ infolist.removeAll(selectedRouteInfos);
+ sortedInfoList.addAll(infolist.stream().filter(
+ MediaRoute2Info::isSystemRoute).collect(Collectors.toList()));
for (RouteListingPreference.Item item : preferenceRouteListing) {
for (MediaRoute2Info info : infolist) {
if (item.getRouteId().equals(info.getId())
- && !selectedRouteInfos.contains(info)) {
+ && !selectedRouteInfos.contains(info)
+ && !info.isSystemRoute()) {
sortedInfoList.add(info);
break;
}
}
}
- if (sortedInfoList.size() != infolist.size()) {
- infolist.removeAll(sortedInfoList);
- sortedInfoList.addAll(infolist.stream().filter(
- MediaRoute2Info::isSystemRoute).collect(Collectors.toList()));
- }
return sortedInfoList;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
index 67a045e..19a3db2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
@@ -20,6 +20,7 @@
import static android.media.MediaRoute2Info.TYPE_REMOTE_CAR;
import static android.media.MediaRoute2Info.TYPE_REMOTE_COMPUTER;
import static android.media.MediaRoute2Info.TYPE_REMOTE_GAME_CONSOLE;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_SMARTPHONE;
import static android.media.MediaRoute2Info.TYPE_REMOTE_SMARTWATCH;
import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
import static android.media.MediaRoute2Info.TYPE_REMOTE_TABLET;
@@ -143,5 +144,9 @@
assertThat(mInfoMediaDevice.getDrawableResIdByType()).isEqualTo(
R.drawable.ic_media_smartwatch);
+
+ when(mRouteInfo.getType()).thenReturn(TYPE_REMOTE_SMARTPHONE);
+
+ assertThat(mInfoMediaDevice.getDrawableResIdByType()).isEqualTo(R.drawable.ic_smartphone);
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 39780f3..7b8815e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -114,6 +114,23 @@
}
@Test
+ public void stopScan_notStartFirst_notCallsUnregister() {
+ mInfoMediaManager.mRouterManager = mRouterManager;
+ mInfoMediaManager.stopScan();
+
+ verify(mRouterManager, never()).unregisterScanRequest();
+ }
+
+ @Test
+ public void stopScan_startFirst_callsUnregister() {
+ mInfoMediaManager.mRouterManager = mRouterManager;
+ mInfoMediaManager.startScan();
+ mInfoMediaManager.stopScan();
+
+ verify(mRouterManager).unregisterScanRequest();
+ }
+
+ @Test
public void onRouteAdded_getAvailableRoutes_shouldAddMediaDevice() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
@@ -327,11 +344,12 @@
routeListingPreference);
mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
- assertThat(mInfoMediaManager.mMediaDevices).hasSize(3);
+ assertThat(mInfoMediaManager.mMediaDevices).hasSize(4);
assertThat(mInfoMediaManager.mMediaDevices.get(0).getId()).isEqualTo(TEST_ID);
- assertThat(mInfoMediaManager.mMediaDevices.get(1).getId()).isEqualTo(TEST_ID_4);
- assertThat(mInfoMediaManager.mMediaDevices.get(1).isSuggestedDevice()).isTrue();
- assertThat(mInfoMediaManager.mMediaDevices.get(2).getId()).isEqualTo(TEST_ID_3);
+ assertThat(mInfoMediaManager.mMediaDevices.get(1).getId()).isEqualTo(TEST_ID_1);
+ assertThat(mInfoMediaManager.mMediaDevices.get(2).getId()).isEqualTo(TEST_ID_4);
+ assertThat(mInfoMediaManager.mMediaDevices.get(2).isSuggestedDevice()).isTrue();
+ assertThat(mInfoMediaManager.mMediaDevices.get(3).getId()).isEqualTo(TEST_ID_3);
}
@Test
@@ -405,8 +423,13 @@
when(availableInfo3.getClientPackageName()).thenReturn(packageName);
availableRoutes.add(availableInfo3);
- when(mRouterManager.getAvailableRoutes(packageName)).thenReturn(
- availableRoutes);
+ final MediaRoute2Info availableInfo4 = mock(MediaRoute2Info.class);
+ when(availableInfo4.getId()).thenReturn(TEST_ID_1);
+ when(availableInfo4.isSystemRoute()).thenReturn(true);
+ when(availableInfo4.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ availableRoutes.add(availableInfo4);
+
+ when(mRouterManager.getAvailableRoutes(packageName)).thenReturn(availableRoutes);
return availableRoutes;
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 48259e1..9da1ab8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -38,6 +38,7 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArraySet;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.LocalePicker;
@@ -238,6 +239,7 @@
// If we fail to apply the setting, by definition nothing happened
sendBroadcast = false;
sendBroadcastSystemUI = false;
+ Log.e(TAG, "Failed to restore setting name: " + name + " + value: " + value, e);
} finally {
// If this was an element of interest, send the "we just restored it"
// broadcast with the historical value now that the new value has
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 6a9c4d8..23b6308 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3558,11 +3558,11 @@
if (isSecureSettingsKey(key)) {
maybeNotifyProfiles(getTypeFromKey(key), userId, uri, name,
sSecureCloneToManagedSettings);
- maybeNotifyProfiles(SETTINGS_TYPE_SYSTEM, userId, uri, name,
- sSystemCloneFromParentOnDependency.values());
} else if (isSystemSettingsKey(key)) {
maybeNotifyProfiles(getTypeFromKey(key), userId, uri, name,
sSystemCloneToManagedSettings);
+ maybeNotifyProfiles(SETTINGS_TYPE_SYSTEM, userId, uri, name,
+ sSystemCloneFromParentOnDependency.keySet());
}
}
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index 1811c02..64c0f99 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -128,6 +128,16 @@
void setDozeAmount(float amount);
/**
+ * Set if dozing is true or false
+ */
+ default void setDozing(boolean dozing) {}
+
+ /**
+ * Set if split shade enabled
+ */
+ default void setSplitShadeEnabled(boolean enabled) {}
+
+ /**
* Set the current keyguard bypass enabled status.
*/
default void setKeyguardBypassEnabled(boolean enabled) {}
diff --git a/packages/SystemUI/res-product/values/strings.xml b/packages/SystemUI/res-product/values/strings.xml
index 13f72af0..42733a2 100644
--- a/packages/SystemUI/res-product/values/strings.xml
+++ b/packages/SystemUI/res-product/values/strings.xml
@@ -122,6 +122,44 @@
Try again in <xliff:g id="number">%3$d</xliff:g> seconds.
</string>
+ <!-- Title for notification & dialog that the user's phone last shut down because it got too hot. [CHAR LIMIT=40] -->
+ <string name="thermal_shutdown_title" product="default">Phone turned off due to heat</string>
+ <!-- Title for notification & dialog that the user's device last shut down because it got too hot. [CHAR LIMIT=40] -->
+ <string name="thermal_shutdown_title" product="device">Device turned off due to heat</string>
+ <!-- Title for notification & dialog that the user's tablet last shut down because it got too hot. [CHAR LIMIT=40] -->
+ <string name="thermal_shutdown_title" product="tablet">Tablet turned off due to heat</string>
+ <!-- Message body for notification that user's phone last shut down because it got too hot. [CHAR LIMIT=120] -->
+ <string name="thermal_shutdown_message" product="default">Your phone is now running normally.\nTap for more info</string>
+ <!-- Message body for notification that user's device last shut down because it got too hot. [CHAR LIMIT=120] -->
+ <string name="thermal_shutdown_message" product="device">Your device is now running normally.\nTap for more info</string>
+ <!-- Message body for notification that user's tablet last shut down because it got too hot. [CHAR LIMIT=120] -->
+ <string name="thermal_shutdown_message" product="tablet">Your tablet is now running normally.\nTap for more info</string>
+ <!-- Text body for dialog alerting user that their phone last shut down because it got too hot. [CHAR LIMIT=500] -->
+ <string name="thermal_shutdown_dialog_message" product="default">Your phone was too hot, so it turned off to cool down. Your phone is now running normally.\n\nYour phone may get too hot if you:\n\t• Use resource-intensive apps (such as gaming, video, or navigation apps)\n\t• Download or upload large files\n\t• Use your phone in high temperatures</string>
+ <!-- Text body for dialog alerting user that their device last shut down because it got too hot. [CHAR LIMIT=500] -->
+ <string name="thermal_shutdown_dialog_message" product="device">Your device was too hot, so it turned off to cool down. Your device is now running normally.\n\nYour device may get too hot if you:\n\t• Use resource-intensive apps (such as gaming, video, or navigation apps)\n\t• Download or upload large files\n\t• Use your device in high temperatures</string>
+ <!-- Text body for dialog alerting user that their tablet last shut down because it got too hot. [CHAR LIMIT=500] -->
+ <string name="thermal_shutdown_dialog_message" product="tablet">Your tablet was too hot, so it turned off to cool down. Your tablet is now running normally.\n\nYour tablet may get too hot if you:\n\t• Use resource-intensive apps (such as gaming, video, or navigation apps)\n\t• Download or upload large files\n\t• Use your tablet in high temperatures</string>
+
+ <!-- Title for notification (and dialog) that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=30] -->
+ <string name="high_temp_title" product="default">Phone is getting warm</string>
+ <!-- Title for notification (and dialog) that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=30] -->
+ <string name="high_temp_title" product="device">Device is getting warm</string>
+ <!-- Title for notification (and dialog) that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=30] -->
+ <string name="high_temp_title" product="tablet">Tablet is getting warm</string>
+ <!-- Message body for notification that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=120] -->
+ <string name="high_temp_notif_message" product="default">Some features limited while phone cools down.\nTap for more info</string>
+ <!-- Message body for notification that user's device has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=120] -->
+ <string name="high_temp_notif_message" product="device">Some features limited while device cools down.\nTap for more info</string>
+ <!-- Message body for notification that user's tablet has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=120] -->
+ <string name="high_temp_notif_message" product="tablet">Some features limited while tablet cools down.\nTap for more info</string>
+ <!-- Text body for dialog alerting user that their phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=350] -->
+ <string name="high_temp_dialog_message" product="default">Your phone will automatically try to cool down. You can still use your phone, but it may run slower.\n\nOnce your phone has cooled down, it will run normally.</string>
+ <!-- Text body for dialog alerting user that their phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=350] -->
+ <string name="high_temp_dialog_message" product="device">Your device will automatically try to cool down. You can still use your device, but it may run slower.\n\nOnce your device has cooled down, it will run normally.</string>
+ <!-- Text body for dialog alerting user that their phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=350] -->
+ <string name="high_temp_dialog_message" product="tablet">Your tablet will automatically try to cool down. You can still use your tablet, but it may run slower.\n\nOnce your tablet has cooled down, it will run normally.</string>
+
<!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, to locate the sensor (tablet) for accessibility (not shown on the screen). [CHAR LIMIT=NONE]-->
<string name="security_settings_sfps_enroll_find_sensor_message" product="tablet">The fingerprint sensor is on the power button. It’s the flat button next to the raised volume button on the edge of the tablet.</string>
<!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, to locate the sensor (device) for accessibility (not shown on the screen). [CHAR LIMIT=NONE]-->
diff --git a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
index 7105721..21e0d2c 100644
--- a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
@@ -164,7 +164,6 @@
/>
<ImageView
android:id="@+id/media_output_item_end_click_icon"
- android:src="@drawable/media_output_status_edit_session"
android:layout_width="24dp"
android:layout_height="24dp"
android:focusable="false"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 663efea..070748c 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2089,23 +2089,11 @@
<!-- Tuner string -->
<!-- Tuner string -->
- <!-- Title for notification & dialog that the user's phone last shut down because it got too hot. [CHAR LIMIT=40] -->
- <string name="thermal_shutdown_title">Phone turned off due to heat</string>
- <!-- Message body for notification that user's phone last shut down because it got too hot. [CHAR LIMIT=120] -->
- <string name="thermal_shutdown_message">Your phone is now running normally.\nTap for more info</string>
- <!-- Text body for dialog alerting user that their phone last shut down because it got too hot. [CHAR LIMIT=500] -->
- <string name="thermal_shutdown_dialog_message">Your phone was too hot, so it turned off to cool down. Your phone is now running normally.\n\nYour phone may get too hot if you:\n\t• Use resource-intensive apps (such as gaming, video, or navigation apps)\n\t• Download or upload large files\n\t• Use your phone in high temperatures</string>
<!-- Text help link for care instructions for overheating devices [CHAR LIMIT=40] -->
<string name="thermal_shutdown_dialog_help_text">See care steps</string>
<!-- URL for care instructions for overheating devices -->
<string name="thermal_shutdown_dialog_help_url" translatable="false"></string>
- <!-- Title for notification (and dialog) that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=30] -->
- <string name="high_temp_title">Phone is getting warm</string>
- <!-- Message body for notification that user's phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=120] -->
- <string name="high_temp_notif_message">Some features limited while phone cools down.\nTap for more info</string>
- <!-- Text body for dialog alerting user that their phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=350] -->
- <string name="high_temp_dialog_message">Your phone will automatically try to cool down. You can still use your phone, but it may run slower.\n\nOnce your phone has cooled down, it will run normally.</string>
<!-- Text help link for care instructions for overheating devices [CHAR LIMIT=40] -->
<string name="high_temp_dialog_help_text">See care steps</string>
<!-- URL for care instructions for overheating devices -->
diff --git a/packages/SystemUI/res/xml/large_screen_shade_header.xml b/packages/SystemUI/res/xml/large_screen_shade_header.xml
index bf576dc..3881e8c 100644
--- a/packages/SystemUI/res/xml/large_screen_shade_header.xml
+++ b/packages/SystemUI/res/xml/large_screen_shade_header.xml
@@ -60,7 +60,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintEnd_toEndOf="@id/carrier_group"/>
+ app:layout_constraintStart_toEndOf="@id/carrier_group"/>
<PropertySet android:alpha="1" />
</Constraint>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 41c1eda..a026130 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -352,6 +352,13 @@
}
/**
+ * Set if the split shade is enabled
+ */
+ public void setSplitShadeEnabled(boolean splitShadeEnabled) {
+ mSmartspaceController.setSplitShadeEnabled(splitShadeEnabled);
+ }
+
+ /**
* Set which clock should be displayed on the keyguard. The other one will be automatically
* hidden.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 00500d6..6854c97 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -323,6 +323,13 @@
}
/**
+ * Set if the split shade is enabled
+ */
+ public void setSplitShadeEnabled(boolean enabled) {
+ mKeyguardClockSwitchController.setSplitShadeEnabled(enabled);
+ }
+
+ /**
* Updates the alignment of the KeyguardStatusView and animates the transition if requested.
*/
public void updateAlignment(
@@ -350,6 +357,9 @@
}
mInteractionJankMonitor.begin(mView, CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION);
+ /* This transition blocks any layout changes while running. For that reason
+ * special logic with setting visibility was added to {@link BcSmartspaceView#setDozing}
+ * for split shade to avoid jump of the media object. */
ChangeBounds transition = new ChangeBounds();
if (splitShadeEnabled) {
// Excluding media from the transition on split-shade, as it doesn't transition
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index ae061c0..de24024 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -296,6 +296,11 @@
private static final ComponentName FALLBACK_HOME_COMPONENT = new ComponentName(
"com.android.settings", "com.android.settings.FallbackHome");
+ private static final List<Integer> ABSENT_SIM_STATE_LIST = Arrays.asList(
+ TelephonyManager.SIM_STATE_ABSENT,
+ TelephonyManager.SIM_STATE_UNKNOWN,
+ TelephonyManager.SIM_STATE_NOT_READY);
+
private final Context mContext;
private final UserTracker mUserTracker;
private final KeyguardUpdateMonitorLogger mLogger;
@@ -3698,8 +3703,7 @@
mLogger.logSimState(subId, slotId, state);
boolean becameAbsent = false;
- if (!SubscriptionManager.isValidSubscriptionId(subId)
- && state != TelephonyManager.SIM_STATE_UNKNOWN) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
mLogger.w("invalid subId in handleSimStateChange()");
/* Only handle No SIM(ABSENT) and Card Error(CARD_IO_ERROR) due to
* handleServiceStateChange() handle other case */
@@ -3717,11 +3721,11 @@
}
} else if (state == TelephonyManager.SIM_STATE_CARD_IO_ERROR) {
updateTelephonyCapable(true);
- } else {
- return;
}
}
+ becameAbsent |= ABSENT_SIM_STATE_LIST.contains(state);
+
SimData data = mSimDatas.get(subId);
final boolean changed;
if (data == null) {
@@ -3734,7 +3738,7 @@
data.subId = subId;
data.slotId = slotId;
}
- if ((changed || becameAbsent) || state == TelephonyManager.SIM_STATE_UNKNOWN) {
+ if ((changed || becameAbsent)) {
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index bc41ab31..2d82c50 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -139,6 +139,7 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.flags.SystemPropertiesHelper;
import com.android.systemui.keyguard.dagger.KeyguardModule;
+import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -538,6 +539,8 @@
private CentralSurfaces mCentralSurfaces;
+ private IRemoteAnimationFinishedCallback mUnoccludeFromDreamFinishedCallback;
+
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
new DeviceConfig.OnPropertiesChangedListener() {
@Override
@@ -642,6 +645,8 @@
switch (simState) {
case TelephonyManager.SIM_STATE_NOT_READY:
case TelephonyManager.SIM_STATE_ABSENT:
+ case TelephonyManager.SIM_STATE_UNKNOWN:
+ mPendingPinLock = false;
// only force lock screen in case of missing sim if user hasn't
// gone through setup wizard
synchronized (KeyguardViewMediator.this) {
@@ -706,9 +711,6 @@
}
}
break;
- case TelephonyManager.SIM_STATE_UNKNOWN:
- mPendingPinLock = false;
- break;
default:
if (DEBUG_SIM_STATES) Log.v(TAG, "Unspecific state: " + simState);
break;
@@ -1165,6 +1167,7 @@
getRemoteSurfaceAlphaApplier().accept(0.0f);
mDreamingToLockscreenTransitionViewModel.get()
.startTransition();
+ mUnoccludeFromDreamFinishedCallback = finishedCallback;
return;
}
@@ -1244,6 +1247,19 @@
};
}
+ private Consumer<TransitionStep> getFinishedCallbackConsumer() {
+ return (TransitionStep step) -> {
+ if (mUnoccludeFromDreamFinishedCallback == null) return;
+ try {
+ mUnoccludeFromDreamFinishedCallback.onAnimationFinished();
+ mUnoccludeFromDreamFinishedCallback = null;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Wasn't able to callback", e);
+ }
+ mInteractionJankMonitor.end(CUJ_LOCKSCREEN_OCCLUSION);
+ };
+ }
+
private DeviceConfigProxy mDeviceConfig;
private DozeParameters mDozeParameters;
@@ -1503,6 +1519,9 @@
collectFlow(viewRootImpl.getView(),
mDreamingToLockscreenTransitionViewModel.get().getDreamOverlayAlpha(),
getRemoteSurfaceAlphaApplier(), mMainDispatcher);
+ collectFlow(viewRootImpl.getView(),
+ mDreamingToLockscreenTransitionViewModel.get().getTransitionEnded(),
+ getFinishedCallbackConsumer(), mMainDispatcher);
}
}
// Most services aren't available until the system reaches the ready state, so we
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 42f12f8..41a81a8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -49,11 +49,15 @@
) {
/** (any)->GONE transition information */
val anyStateToGoneTransition: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.to == KeyguardState.GONE }
+ repository.transitions.filter { step -> step.to == GONE }
/** (any)->AOD transition information */
val anyStateToAodTransition: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.to == KeyguardState.AOD }
+ repository.transitions.filter { step -> step.to == AOD }
+
+ /** DREAMING->(any) transition information. */
+ val fromDreamingTransition: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.from == DREAMING }
/** AOD->LOCKSCREEN transition information. */
val aodToLockscreenTransition: Flow<TransitionStep> = repository.transition(AOD, LOCKSCREEN)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
index 1c2e85b..b92d104 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt
@@ -346,6 +346,10 @@
?.largeClock
?.events
?.onTargetRegionChanged(KeyguardClockSwitch.getLargeClockRegion(parentView))
+ clockController.clock
+ ?.smallClock
+ ?.events
+ ?.onTargetRegionChanged(KeyguardClockSwitch.getSmallClockRegion(parentView))
}
}
parentView.addOnLayoutChangeListener(layoutChangeListener)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index 9ca4bd6..e24d326 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -48,7 +48,7 @@
)
val transitionEnded =
- keyguardTransitionInteractor.dreamingToLockscreenTransition.filter { step ->
+ keyguardTransitionInteractor.fromDreamingTransition.filter { step ->
step.transitionState == TransitionState.FINISHED ||
step.transitionState == TransitionState.CANCELED
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 318cd99..26a7d04 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -20,6 +20,7 @@
import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_NONE;
import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
+import android.annotation.DrawableRes;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.drawable.AnimatedVectorDrawable;
@@ -181,27 +182,23 @@
mController.getSelectedMediaDevice(), device)));
boolean isHost = device.isHostForOngoingSession()
&& isActiveWithOngoingSession;
- if (isHost) {
+ if (isActiveWithOngoingSession) {
mCurrentActivePosition = position;
updateTitleIcon(R.drawable.media_output_icon_volume,
mController.getColorItemContent());
mSubTitleText.setText(device.getSubtextString());
updateTwoLineLayoutContentAlpha(DEVICE_CONNECTED_ALPHA);
- updateEndClickAreaAsSessionEditing(device);
+ updateEndClickAreaAsSessionEditing(device,
+ isHost ? R.drawable.media_output_status_edit_session
+ : R.drawable.ic_sound_bars_anim);
setTwoLineLayout(device, null /* title */, true /* bFocused */,
true /* showSeekBar */, false /* showProgressBar */,
true /* showSubtitle */, false /* showStatus */,
true /* showEndTouchArea */, false /* isFakeActive */);
initSeekbar(device, isCurrentSeekbarInvisible);
} else {
- if (isActiveWithOngoingSession) {
- //Selected device which has ongoing session, disable seekbar since we
- //only allow volume control on Host
+ if (currentlyConnected) {
mCurrentActivePosition = position;
- }
- boolean showSeekbar =
- (!device.hasOngoingSession() && currentlyConnected);
- if (showSeekbar) {
updateTitleIcon(R.drawable.media_output_icon_volume,
mController.getColorItemContent());
initSeekbar(device, isCurrentSeekbarInvisible);
@@ -222,10 +219,10 @@
updateClickActionBasedOnSelectionBehavior(device)
? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA);
setTwoLineLayout(device, currentlyConnected /* bFocused */,
- showSeekbar /* showSeekBar */,
+ currentlyConnected /* showSeekBar */,
false /* showProgressBar */, true /* showSubtitle */,
deviceStatusIcon != null /* showStatus */,
- isActiveWithOngoingSession /* isFakeActive */);
+ false /* isFakeActive */);
}
} else if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
setUpDeviceIcon(device);
@@ -267,25 +264,16 @@
setSingleLineLayout(getItemTitle(device));
} else if (device.hasOngoingSession()) {
mCurrentActivePosition = position;
- if (device.isHostForOngoingSession()) {
- updateTitleIcon(R.drawable.media_output_icon_volume,
- mController.getColorItemContent());
- updateEndClickAreaAsSessionEditing(device);
- mEndClickIcon.setVisibility(View.VISIBLE);
- setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
- false /* showProgressBar */, false /* showCheckBox */,
- true /* showEndTouchArea */);
- initSeekbar(device, isCurrentSeekbarInvisible);
- } else {
- updateDeviceStatusIcon(mContext.getDrawable(
- R.drawable.ic_sound_bars_anim));
- mStatusIcon.setVisibility(View.VISIBLE);
- updateSingleLineLayoutContentAlpha(
- updateClickActionBasedOnSelectionBehavior(device)
- ? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA);
- setSingleLineLayout(getItemTitle(device));
- initFakeActiveDevice();
- }
+ updateTitleIcon(R.drawable.media_output_icon_volume,
+ mController.getColorItemContent());
+ updateEndClickAreaAsSessionEditing(device, device.isHostForOngoingSession()
+ ? R.drawable.media_output_status_edit_session
+ : R.drawable.ic_sound_bars_anim);
+ mEndClickIcon.setVisibility(View.VISIBLE);
+ setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
+ false /* showProgressBar */, false /* showCheckBox */,
+ true /* showEndTouchArea */);
+ initSeekbar(device, isCurrentSeekbarInvisible);
} else if (mController.isCurrentConnectedDeviceRemote()
&& !mController.getSelectableMediaDevice().isEmpty()) {
//If device is connected and there's other selectable devices, layout as
@@ -362,7 +350,7 @@
mStatusIcon.setAlpha(alphaValue);
}
- private void updateEndClickAreaAsSessionEditing(MediaDevice device) {
+ private void updateEndClickAreaAsSessionEditing(MediaDevice device, @DrawableRes int id) {
mEndClickIcon.setOnClickListener(null);
mEndTouchArea.setOnClickListener(null);
updateEndClickAreaColor(mController.getColorSeekbarProgress());
@@ -371,6 +359,11 @@
mEndClickIcon.setOnClickListener(
v -> mController.tryToLaunchInAppRoutingIntent(device.getId(), v));
mEndTouchArea.setOnClickListener(v -> mEndClickIcon.performClick());
+ Drawable drawable = mContext.getDrawable(id);
+ mEndClickIcon.setImageDrawable(drawable);
+ if (drawable instanceof AnimatedVectorDrawable) {
+ ((AnimatedVectorDrawable) drawable).start();
+ }
}
public void updateEndClickAreaColor(int color) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
index c70cce9..2fafba1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
@@ -120,6 +120,7 @@
val tilesToRemove = restoredAutoAdded.filter { it !in restoredTiles }
if (tilesToRemove.isNotEmpty()) {
+ Log.d(TAG, "Removing tiles: $tilesToRemove")
qsHost.removeTiles(tilesToRemove)
}
val tiles = synchronized(autoAdded) {
@@ -255,6 +256,7 @@
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("Current user: $userId")
+ pw.println("Restored tiles: $restoredTiles")
pw.println("Added tiles: $autoAdded")
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 59b94b7..868dbfa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -302,7 +302,7 @@
if (tile != null && (!(tile instanceof CustomTile)
|| ((CustomTile) tile).getUser() == currentUser)) {
if (tile.isAvailable()) {
- if (DEBUG) Log.d(TAG, "Adding " + tile);
+ Log.d(TAG, "Adding " + tile);
tile.removeCallbacks();
if (!(tile instanceof CustomTile) && mCurrentUser != currentUser) {
tile.userSwitch(currentUser);
@@ -421,6 +421,7 @@
// When calling this, you may want to modify mTilesListDirty accordingly.
@MainThread
private void saveTilesToSettings(List<String> tileSpecs) {
+ Log.d(TAG, "Saving tiles: " + tileSpecs + " for user: " + mCurrentUser);
mSecureSettings.putStringForUser(TILES_SETTING, TextUtils.join(",", tileSpecs),
null /* tag */, false /* default */, mCurrentUser,
true /* overrideable by restore */);
@@ -494,7 +495,7 @@
lifecycleManager.flushMessagesAndUnbind();
}
}
- if (DEBUG) Log.d(TAG, "saveCurrentTiles " + newTiles);
+ Log.d(TAG, "saveCurrentTiles " + newTiles);
mTilesListDirty = true;
saveTilesToSettings(newTiles);
}
@@ -565,9 +566,9 @@
if (TextUtils.isEmpty(tileList)) {
tileList = res.getString(R.string.quick_settings_tiles);
- if (DEBUG) Log.d(TAG, "Loaded tile specs from default config: " + tileList);
+ Log.d(TAG, "Loaded tile specs from default config: " + tileList);
} else {
- if (DEBUG) Log.d(TAG, "Loaded tile specs from setting: " + tileList);
+ Log.d(TAG, "Loaded tile specs from setting: " + tileList);
}
final ArrayList<String> tiles = new ArrayList<String>();
boolean addedDefault = false;
@@ -613,6 +614,10 @@
@Override
public void dump(PrintWriter pw, String[] args) {
pw.println("QSTileHost:");
+ pw.println("tile specs: " + mTileSpecs);
+ pw.println("current user: " + mCurrentUser);
+ pw.println("is dirty: " + mTilesListDirty);
+ pw.println("tiles:");
mTiles.values().stream().filter(obj -> obj instanceof Dumpable)
.forEach(o -> ((Dumpable) o).dump(pw, args));
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 776a90d..3acfe6c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1172,6 +1172,7 @@
mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
mKeyguardStatusViewController.init();
+ mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
updateClockAppearance();
if (mKeyguardUserSwitcherController != null) {
@@ -1224,6 +1225,7 @@
private void onSplitShadeEnabledChanged() {
mShadeLog.logSplitShadeChanged(mSplitShadeEnabled);
+ mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
// Reset any left over overscroll state. It is a rare corner case but can happen.
mQsController.setOverScrollAmount(0);
mScrimController.setNotificationsOverScrollAmount(0);
@@ -3560,6 +3562,7 @@
}
private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
+ mShadeLog.logEndMotionEvent("endMotionEvent called", forceCancel, false);
mTrackingPointer = -1;
mAmbientState.setSwipingUp(false);
if ((mTracking && mTouchSlopExceeded) || Math.abs(x - mInitialExpandX) > mTouchSlop
@@ -3581,15 +3584,19 @@
} else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
if (onKeyguard) {
expand = true;
+ mShadeLog.logEndMotionEvent("endMotionEvent: cancel while on keyguard",
+ forceCancel, expand);
} else if (mCentralSurfaces.isBouncerShowingOverDream()) {
expand = false;
} else {
// If we get a cancel, put the shade back to the state it was in when the
// gesture started
expand = !mPanelClosedOnDown;
+ mShadeLog.logEndMotionEvent("endMotionEvent: cancel", forceCancel, expand);
}
} else {
expand = flingExpands(vel, vectorVel, x, y);
+ mShadeLog.logEndMotionEvent("endMotionEvent: flingExpands", forceCancel, expand);
}
mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
@@ -4685,6 +4692,8 @@
mTouchSlopExceeded = mTouchSlopExceededBeforeDown;
mMotionAborted = false;
mPanelClosedOnDown = isFullyCollapsed();
+ mShadeLog.logPanelClosedOnDown("intercept down touch", mPanelClosedOnDown,
+ mExpandedFraction);
mCollapsedAndHeadsUpOnDown = false;
mHasLayoutedSinceDown = false;
mUpdateFlingOnLayout = false;
@@ -4898,6 +4907,8 @@
startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
mMinExpandHeight = 0.0f;
mPanelClosedOnDown = isFullyCollapsed();
+ mShadeLog.logPanelClosedOnDown("handle down touch", mPanelClosedOnDown,
+ mExpandedFraction);
mHasLayoutedSinceDown = false;
mUpdateFlingOnLayout = false;
mMotionAborted = false;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
index 2da8d5f4..4fdd6e1 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
@@ -90,7 +90,7 @@
double1 = event.y.toDouble()
},
{
- "$str1\neventTime=$long1,downTime=$long2,y=$double1,action=$int1,class=$int2"
+ "$str1: eventTime=$long1,downTime=$long2,y=$double1,action=$int1,class=$int2"
}
)
}
@@ -280,6 +280,42 @@
)
}
+ fun logEndMotionEvent(
+ msg: String,
+ forceCancel: Boolean,
+ expand: Boolean,
+ )
+ {
+ buffer.log(
+ TAG,
+ LogLevel.VERBOSE,
+ {
+ str1 = msg
+ bool1 = forceCancel
+ bool2 = expand
+ },
+ { "$str1; force=$bool1; expand=$bool2" }
+ )
+ }
+
+ fun logPanelClosedOnDown(
+ msg: String,
+ panelClosedOnDown: Boolean,
+ expandFraction: Float,
+ )
+ {
+ buffer.log(
+ TAG,
+ LogLevel.VERBOSE,
+ {
+ str1 = msg
+ bool1 = panelClosedOnDown
+ double1 = expandFraction.toDouble()
+ },
+ { "$str1; mPanelClosedOnDown=$bool1; mExpandedFraction=$double1" }
+ )
+ }
+
fun flingQs(flingType: Int, isClick: Boolean) {
buffer.log(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index 06f43f1..906c5ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -534,19 +534,7 @@
new ShortcutKeyGroupMultiMappingInfo(
context.getString(R.string.group_system_access_google_assistant),
Arrays.asList(
- Pair.create(KeyEvent.KEYCODE_A, KeyEvent.META_META_ON))),
- /* Lock screen: Meta + L */
- new ShortcutKeyGroupMultiMappingInfo(
- context.getString(R.string.group_system_lock_screen),
- Arrays.asList(
- Pair.create(KeyEvent.KEYCODE_L, KeyEvent.META_META_ON))),
- /* Pull up Notes app for quick memo: Meta + Ctrl + N */
- new ShortcutKeyGroupMultiMappingInfo(
- context.getString(R.string.group_system_quick_memo),
- Arrays.asList(
- Pair.create(
- KeyEvent.KEYCODE_N,
- KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON)))
+ Pair.create(KeyEvent.KEYCODE_A, KeyEvent.META_META_ON)))
);
for (ShortcutKeyGroupMultiMappingInfo info : infoList) {
systemGroup.addItem(info.getShortcutMultiMappingInfo());
@@ -588,21 +576,12 @@
new ArrayList<>());
// System multitasking shortcuts:
- // Enter Split screen with current app to RHS: Meta + Ctrl + Right arrow
- // Enter Split screen with current app to LHS: Meta + Ctrl + Left arrow
// Switch from Split screen to full screen: Meta + Ctrl + Up arrow
- // During Split screen: replace an app from one to another: Meta + Ctrl + Down arrow
String[] shortcutLabels = {
- context.getString(R.string.system_multitasking_rhs),
- context.getString(R.string.system_multitasking_lhs),
context.getString(R.string.system_multitasking_full_screen),
- context.getString(R.string.system_multitasking_replace)
};
int[] keyCodes = {
- KeyEvent.KEYCODE_DPAD_RIGHT,
- KeyEvent.KEYCODE_DPAD_LEFT,
KeyEvent.KEYCODE_DPAD_UP,
- KeyEvent.KEYCODE_DPAD_DOWN
};
for (int i = 0; i < shortcutLabels.length; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 518825c..6c1dc8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -124,6 +124,7 @@
private var showSensitiveContentForCurrentUser = false
private var showSensitiveContentForManagedUser = false
private var managedUserHandle: UserHandle? = null
+ private var mSplitShadeEnabled = false
// TODO(b/202758428): refactor so that we can test color updates via region samping, similar to
// how we test color updates when theme changes (See testThemeChangeUpdatesTextColor).
@@ -131,6 +132,7 @@
// TODO: Move logic into SmartspaceView
var stateChangeListener = object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {
+ (v as SmartspaceView).setSplitShadeEnabled(mSplitShadeEnabled)
smartspaceViews.add(v as SmartspaceView)
connectSession()
@@ -216,6 +218,11 @@
execution.assertIsMainThread()
smartspaceViews.forEach { it.setDozeAmount(eased) }
}
+
+ override fun onDozingChanged(isDozing: Boolean) {
+ execution.assertIsMainThread()
+ smartspaceViews.forEach { it.setDozing(isDozing) }
+ }
}
private val deviceProvisionedListener =
@@ -421,6 +428,11 @@
reloadSmartspace()
}
+ fun setSplitShadeEnabled(enabled: Boolean) {
+ mSplitShadeEnabled = enabled
+ smartspaceViews.forEach { it.setSplitShadeEnabled(enabled) }
+ }
+
/**
* Requests the smartspace session for an update.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 8aeefee..d2c6d4b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -840,6 +840,7 @@
&& !hasFlag(entry, Notification.FLAG_ONGOING_EVENT)
&& !hasFlag(entry, Notification.FLAG_BUBBLE)
&& !hasFlag(entry, Notification.FLAG_NO_CLEAR)
+ && (entry.getChannel() == null || !entry.getChannel().isImportantConversation())
&& entry.getDismissState() != DISMISSED;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt
index f742645..a61914a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt
@@ -209,7 +209,7 @@
if (this.contains(other) || other.contains(this)) {
return false
}
- return this.intersect(other)
+ return this.intersects(other.left, other.top, other.right, other.bottom)
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
index 21d0338..cac5e32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
@@ -22,6 +22,13 @@
import android.app.RemoteInput
import android.content.Context
import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.ImageDecoder
+import android.graphics.drawable.AdaptiveIconDrawable
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.Icon
import android.os.Build
import android.os.Bundle
import android.os.SystemClock
@@ -48,7 +55,13 @@
import com.android.systemui.statusbar.policy.SmartReplyView.SmartActions
import com.android.systemui.statusbar.policy.SmartReplyView.SmartButtonType
import com.android.systemui.statusbar.policy.SmartReplyView.SmartReplies
+import java.util.concurrent.FutureTask
+import java.util.concurrent.SynchronousQueue
+import java.util.concurrent.ThreadPoolExecutor
+import java.util.concurrent.TimeUnit
import javax.inject.Inject
+import kotlin.system.measureTimeMillis
+
/** Returns whether we should show the smart reply view and its smart suggestions. */
fun shouldShowSmartReplyView(
@@ -281,6 +294,51 @@
): Button
}
+private const val ICON_TASK_TIMEOUT_MS = 500L
+private val iconTaskThreadPool = ThreadPoolExecutor(0, 25, 1, TimeUnit.MINUTES, SynchronousQueue())
+
+private fun loadIconDrawableWithTimeout(
+ icon: Icon,
+ packageContext: Context,
+ targetSize: Int,
+): Drawable? {
+ if (icon.type != Icon.TYPE_URI && icon.type != Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+ return icon.loadDrawable(packageContext)
+ }
+ val bitmapTask = FutureTask {
+ val bitmap: Bitmap?
+ val durationMillis = measureTimeMillis {
+ val source = ImageDecoder.createSource(packageContext.contentResolver, icon.uri)
+ bitmap = ImageDecoder.decodeBitmap(source) { decoder, _, _ ->
+ decoder.setTargetSize(targetSize, targetSize)
+ decoder.allocator = ImageDecoder.ALLOCATOR_DEFAULT
+ }
+ }
+ if (durationMillis > ICON_TASK_TIMEOUT_MS) {
+ Log.w(TAG, "Loading $icon took ${durationMillis / 1000f} sec")
+ }
+ checkNotNull(bitmap) { "ImageDecoder.decodeBitmap() returned null" }
+ }
+ val bitmap = runCatching {
+ iconTaskThreadPool.execute(bitmapTask)
+ bitmapTask.get(ICON_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ }.getOrElse { ex ->
+ Log.e(TAG, "Failed to load $icon: $ex")
+ bitmapTask.cancel(true)
+ return null
+ }
+ // TODO(b/288561520): rewrite Icon so that we don't need to duplicate this logic
+ val bitmapDrawable = BitmapDrawable(packageContext.resources, bitmap)
+ val result = if (icon.type == Icon.TYPE_URI_ADAPTIVE_BITMAP)
+ AdaptiveIconDrawable(null, bitmapDrawable) else bitmapDrawable
+ if (icon.hasTint()) {
+ result.mutate()
+ result.setTintList(icon.tintList)
+ result.setTintBlendMode(icon.tintBlendMode)
+ }
+ return result
+}
+
/* internal */ class SmartActionInflaterImpl @Inject constructor(
private val constants: SmartReplyConstants,
private val activityStarter: ActivityStarter,
@@ -304,12 +362,12 @@
// We received the Icon from the application - so use the Context of the application to
// reference icon resources.
- val iconDrawable = action.getIcon().loadDrawable(packageContext)
- .apply {
- val newIconSize: Int = context.resources.getDimensionPixelSize(
- R.dimen.smart_action_button_icon_size)
- setBounds(0, 0, newIconSize, newIconSize)
- }
+ val newIconSize = context.resources
+ .getDimensionPixelSize(R.dimen.smart_action_button_icon_size)
+ val iconDrawable =
+ loadIconDrawableWithTimeout(action.getIcon(), packageContext, newIconSize)
+ ?: GradientDrawable()
+ iconDrawable.setBounds(0, 0, newIconSize, newIconSize)
// Add the action icon to the Smart Action button.
setCompoundDrawablesRelative(iconDrawable, null, null, null)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 7456d34..9362220 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -476,7 +476,8 @@
mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
- mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index b21cc6d..9e561ed 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -408,4 +408,18 @@
any(ClockRegistry.ClockChangeListener.class));
verify(mClockEventController, times).registerListeners(mView);
}
+
+ @Test
+ public void testSplitShadeEnabledSetToSmartspaceController() {
+ mController.setSplitShadeEnabled(true);
+ verify(mSmartspaceController, times(1)).setSplitShadeEnabled(true);
+ verify(mSmartspaceController, times(0)).setSplitShadeEnabled(false);
+ }
+
+ @Test
+ public void testSplitShadeDisabledSetToSmartspaceController() {
+ mController.setSplitShadeEnabled(false);
+ verify(mSmartspaceController, times(1)).setSplitShadeEnabled(false);
+ verify(mSmartspaceController, times(0)).setSplitShadeEnabled(true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index a2c6329..512e5dc 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -17,6 +17,7 @@
package com.android.keyguard;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -155,4 +156,18 @@
verify(mControllerMock).setProperty(AnimatableProperty.SCALE_X, 20f, true);
verify(mControllerMock).setProperty(AnimatableProperty.SCALE_Y, 20f, true);
}
+
+ @Test
+ public void splitShadeEnabledPassedToClockSwitchController() {
+ mController.setSplitShadeEnabled(true);
+ verify(mKeyguardClockSwitchController, times(1)).setSplitShadeEnabled(true);
+ verify(mKeyguardClockSwitchController, times(0)).setSplitShadeEnabled(false);
+ }
+
+ @Test
+ public void splitShadeDisabledPassedToClockSwitchController() {
+ mController.setSplitShadeEnabled(false);
+ verify(mKeyguardClockSwitchController, times(1)).setSplitShadeEnabled(false);
+ verify(mKeyguardClockSwitchController, times(0)).setSplitShadeEnabled(true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 1e675f8..3cb4c0c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -2935,6 +2935,16 @@
TelephonyManager.SIM_STATE_UNKNOWN);
}
+ @Test
+ public void testOnSimStateChanged_HandleSimStateNotReady() {
+ KeyguardUpdateMonitorCallback keyguardUpdateMonitorCallback = spy(
+ KeyguardUpdateMonitorCallback.class);
+ mKeyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback);
+ mKeyguardUpdateMonitor.handleSimStateChange(-1, 0, TelephonyManager.SIM_STATE_NOT_READY);
+ verify(keyguardUpdateMonitorCallback).onSimStateChanged(-1, 0,
+ TelephonyManager.SIM_STATE_NOT_READY);
+ }
+
private void verifyFingerprintAuthenticateNeverCalled() {
verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), any());
verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index c280538..7a501a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -186,6 +186,8 @@
when(mStatusBarKeyguardViewManager.getViewRootImpl()).thenReturn(testViewRoot);
when(mDreamingToLockscreenTransitionViewModel.getDreamOverlayAlpha())
.thenReturn(mock(Flow.class));
+ when(mDreamingToLockscreenTransitionViewModel.getTransitionEnded())
+ .thenReturn(mock(Flow.class));
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mViewMediator, mKeyguardBypassController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
index a341346..ab994b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
@@ -22,8 +22,16 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
-import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
+import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.util.mockito.mock
import com.google.common.collect.Range
@@ -60,7 +68,7 @@
val job =
underTest.dreamOverlayTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0f, STARTED))
repository.sendTransitionStep(step(0f))
repository.sendTransitionStep(step(0.3f))
repository.sendTransitionStep(step(0.5f))
@@ -82,7 +90,7 @@
val job = underTest.dreamOverlayAlpha.onEach { values.add(it) }.launchIn(this)
// Should start running here...
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0f, STARTED))
repository.sendTransitionStep(step(0f))
repository.sendTransitionStep(step(0.1f))
repository.sendTransitionStep(step(0.5f))
@@ -104,7 +112,7 @@
val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0f, STARTED))
repository.sendTransitionStep(step(0f))
repository.sendTransitionStep(step(0.1f))
repository.sendTransitionStep(step(0.2f))
@@ -126,7 +134,7 @@
val job =
underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0f, STARTED))
repository.sendTransitionStep(step(0f))
repository.sendTransitionStep(step(0.3f))
repository.sendTransitionStep(step(0.5f))
@@ -138,13 +146,44 @@
job.cancel()
}
- private fun step(
- value: Float,
- state: TransitionState = TransitionState.RUNNING
- ): TransitionStep {
+ @Test
+ fun transitionEnded() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<TransitionStep>()
+
+ val job = underTest.transitionEnded.onEach { values.add(it) }.launchIn(this)
+
+ repository.sendTransitionStep(TransitionStep(DOZING, DREAMING, 0.0f, STARTED))
+ repository.sendTransitionStep(TransitionStep(DOZING, DREAMING, 1.0f, FINISHED))
+
+ repository.sendTransitionStep(TransitionStep(DREAMING, LOCKSCREEN, 0.0f, STARTED))
+ repository.sendTransitionStep(TransitionStep(DREAMING, LOCKSCREEN, 0.1f, RUNNING))
+ repository.sendTransitionStep(TransitionStep(DREAMING, LOCKSCREEN, 1.0f, FINISHED))
+
+ repository.sendTransitionStep(TransitionStep(LOCKSCREEN, DREAMING, 0.0f, STARTED))
+ repository.sendTransitionStep(TransitionStep(LOCKSCREEN, DREAMING, 0.5f, RUNNING))
+ repository.sendTransitionStep(TransitionStep(LOCKSCREEN, DREAMING, 1.0f, FINISHED))
+
+ repository.sendTransitionStep(TransitionStep(DREAMING, GONE, 0.0f, STARTED))
+ repository.sendTransitionStep(TransitionStep(DREAMING, GONE, 0.5f, RUNNING))
+ repository.sendTransitionStep(TransitionStep(DREAMING, GONE, 1.0f, CANCELED))
+
+ repository.sendTransitionStep(TransitionStep(DREAMING, AOD, 0.0f, STARTED))
+ repository.sendTransitionStep(TransitionStep(DREAMING, AOD, 1.0f, FINISHED))
+
+ assertThat(values.size).isEqualTo(3)
+ values.forEach {
+ assertThat(it.transitionState == FINISHED || it.transitionState == CANCELED)
+ .isTrue()
+ }
+
+ job.cancel()
+ }
+
+ private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
return TransitionStep(
- from = KeyguardState.DREAMING,
- to = KeyguardState.LOCKSCREEN,
+ from = DREAMING,
+ to = LOCKSCREEN,
value = value,
transitionState = state,
ownerName = "DreamingToLockscreenTransitionViewModelTest"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 7df54d4..e4f89a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -291,13 +291,13 @@
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
- assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
@@ -525,16 +525,16 @@
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mSubTitleText.getText().toString()).isEqualTo(TEST_CUSTOM_SUBTEXT);
assertThat(mViewHolder.mTwoLineTitleText.getText().toString()).isEqualTo(
TEST_DEVICE_NAME_1);
- assertThat(mViewHolder.mContainerLayout.hasOnClickListeners()).isTrue();
+ assertThat(mViewHolder.mContainerLayout.hasOnClickListeners()).isFalse();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 810ab34..587c49d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -297,11 +297,20 @@
StringWriter w = new StringWriter();
PrintWriter pw = new PrintWriter(w);
mQSTileHost.dump(pw, new String[]{});
- String output = "QSTileHost:\n"
- + TestTile1.class.getSimpleName() + ":\n"
- + " " + MOCK_STATE_STRING + "\n"
- + TestTile2.class.getSimpleName() + ":\n"
- + " " + MOCK_STATE_STRING + "\n";
+
+ String output = "QSTileHost:" + "\n"
+ + "tile specs: [spec1, spec2]" + "\n"
+ + "current user: 0" + "\n"
+ + "is dirty: false" + "\n"
+ + "tiles:" + "\n"
+ + "TestTile1:" + "\n"
+ + " MockState" + "\n"
+ + "TestTile2:" + "\n"
+ + " MockState" + "\n";
+
+ System.out.println(output);
+ System.out.println(w.getBuffer().toString());
+
assertEquals(output, w.getBuffer().toString());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 540bda6..9037df8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -1675,11 +1675,21 @@
}
@Test
+ public void testCanDismissOtherNotificationChildren() {
+ // GIVEN an ongoing notification
+ final NotificationEntry container = new NotificationEntryBuilder()
+ .setGroup(mContext, "group")
+ .build();
+
+ // THEN its children are dismissible
+ assertTrue(mCollection.shouldAutoDismissChildren(
+ container, container.getSbn().getGroupKey()));
+ }
+
+ @Test
public void testCannotDismissOngoingNotificationChildren() {
// GIVEN an ongoing notification
final NotificationEntry container = new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE)
- .setId(47)
.setGroup(mContext, "group")
.setFlag(mContext, FLAG_ONGOING_EVENT, true)
.build();
@@ -1693,6 +1703,7 @@
public void testCannotDismissNoClearNotifications() {
// GIVEN an no-clear notification
final NotificationEntry container = new NotificationEntryBuilder()
+ .setGroup(mContext, "group")
.setFlag(mContext, FLAG_NO_CLEAR, true)
.build();
@@ -1702,11 +1713,25 @@
}
@Test
+ public void testCannotDismissPriorityConversations() {
+ // GIVEN an no-clear notification
+ NotificationChannel channel =
+ new NotificationChannel("foo", "Foo", NotificationManager.IMPORTANCE_HIGH);
+ channel.setImportantConversation(true);
+ final NotificationEntry container = new NotificationEntryBuilder()
+ .setGroup(mContext, "group")
+ .setChannel(channel)
+ .build();
+
+ // THEN its children are not dismissible
+ assertFalse(mCollection.shouldAutoDismissChildren(
+ container, container.getSbn().getGroupKey()));
+ }
+
+ @Test
public void testCanDismissFgsNotificationChildren() {
// GIVEN an FGS but not ongoing notification
final NotificationEntry container = new NotificationEntryBuilder()
- .setPkg(TEST_PACKAGE)
- .setId(47)
.setGroup(mContext, "group")
.setFlag(mContext, FLAG_FOREGROUND_SERVICE, true)
.build();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
index c0243dc..b2dc0dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
@@ -105,6 +105,30 @@
expect.that(letterboxAppearance.appearanceRegions).isEqualTo(TEST_APPEARANCE_REGIONS)
}
+ /** Regression test for b/287508741 */
+ @Test
+ fun getLetterboxAppearance_withOverlap_doesNotMutateOriginalBounds() {
+ val statusBarStartSideBounds = Rect(left = 0, top = 0, right = 100, bottom = 100)
+ val statusBarEndSideBounds = Rect(left = 200, top = 0, right = 300, bottom = 100)
+ val letterBoxInnerBounds = Rect(left = 150, top = 50, right = 250, bottom = 150)
+ val statusBarStartSideBoundsCopy = Rect(statusBarStartSideBounds)
+ val statusBarEndSideBoundsCopy = Rect(statusBarEndSideBounds)
+ val letterBoxInnerBoundsCopy = Rect(letterBoxInnerBounds)
+ whenever(statusBarBoundsProvider.visibleStartSideBounds)
+ .thenReturn(statusBarStartSideBounds)
+ whenever(statusBarBoundsProvider.visibleEndSideBounds).thenReturn(statusBarEndSideBounds)
+
+ calculator.getLetterboxAppearance(
+ TEST_APPEARANCE,
+ TEST_APPEARANCE_REGIONS,
+ arrayOf(letterboxWithInnerBounds(letterBoxInnerBounds))
+ )
+
+ expect.that(statusBarStartSideBounds).isEqualTo(statusBarStartSideBoundsCopy)
+ expect.that(statusBarEndSideBounds).isEqualTo(statusBarEndSideBoundsCopy)
+ expect.that(letterBoxInnerBounds).isEqualTo(letterBoxInnerBoundsCopy)
+ }
+
@Test
fun getLetterboxAppearance_noOverlap_BackgroundMultiColor_returnsAppearanceWithScrim() {
whenever(letterboxBackgroundProvider.isLetterboxBackgroundMultiColored).thenReturn(true)
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index eb7fa10..add94b1 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -172,6 +172,7 @@
private DeviceState mRearDisplayState;
// TODO(259328837) Generalize for all pending feature requests in the future
+ @GuardedBy("mLock")
@Nullable
private OverrideRequest mRearDisplayPendingOverrideRequest;
@@ -779,7 +780,7 @@
* {@link StatusBarManagerInternal} to notify SystemUI to display the educational dialog.
*/
@GuardedBy("mLock")
- private void showRearDisplayEducationalOverlayLocked(OverrideRequest request) {
+ private void showRearDisplayEducationalOverlayLocked(@NonNull OverrideRequest request) {
mRearDisplayPendingOverrideRequest = request;
StatusBarManagerInternal statusBar =
@@ -844,8 +845,8 @@
* request if it was dismissed in a way that should cancel the feature.
*/
private void onStateRequestOverlayDismissedInternal(boolean shouldCancelRequest) {
- if (mRearDisplayPendingOverrideRequest != null) {
- synchronized (mLock) {
+ synchronized (mLock) {
+ if (mRearDisplayPendingOverrideRequest != null) {
if (shouldCancelRequest) {
ProcessRecord processRecord = mProcessRecords.get(
mRearDisplayPendingOverrideRequest.getPid());
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 488745c..4bd66f2 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5594,6 +5594,11 @@
boolean granted, boolean userSet) {
Objects.requireNonNull(listener);
checkNotificationListenerAccess();
+ if (granted && listener.flattenToString().length()
+ > NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH) {
+ throw new IllegalArgumentException(
+ "Component name too long: " + listener.flattenToString());
+ }
if (!userSet && isNotificationListenerAccessUserSet(listener)) {
// Don't override user's choice
return;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 3132c5d..cbded89 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3570,19 +3570,17 @@
}
@Override
- public void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition) {
- if (mKeyguardDelegate != null && waitAppTransition) {
+ public void onKeyguardOccludedChangedLw(boolean occluded) {
+ if (mKeyguardDelegate != null) {
mPendingKeyguardOccluded = occluded;
mKeyguardOccludedChanged = true;
- } else {
- setKeyguardOccludedLw(occluded);
}
}
@Override
public int applyKeyguardOcclusionChange() {
if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded commit occluded="
- + mPendingKeyguardOccluded);
+ + mPendingKeyguardOccluded + " changed=" + mKeyguardOccludedChanged);
// TODO(b/276433230): Explicitly save before/after for occlude state in each
// Transition so we don't need to update SysUI every time.
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 887f946..03a7bd3 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -169,7 +169,7 @@
*
* @param occluded Whether Keyguard is currently occluded or not.
*/
- void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition);
+ void onKeyguardOccludedChangedLw(boolean occluded);
/**
* Commit any queued changes to keyguard occlude status that had been deferred during the
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 4a57592a..27329e2 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -14613,17 +14613,13 @@
// Inform StatsLog of setBatteryState changes.
private void reportChangesToStatsLog(final int status, final int plugType, final int level) {
- if (!mHaveBatteryLevel) {
- return;
- }
-
- if (mBatteryStatus != status) {
+ if (!mHaveBatteryLevel || mBatteryStatus != status) {
FrameworkStatsLog.write(FrameworkStatsLog.CHARGING_STATE_CHANGED, status);
}
- if (mBatteryPlugType != plugType) {
+ if (!mHaveBatteryLevel || mBatteryPlugType != plugType) {
FrameworkStatsLog.write(FrameworkStatsLog.PLUGGED_STATE_CHANGED, plugType);
}
- if (mBatteryLevel != level) {
+ if (!mHaveBatteryLevel || mBatteryLevel != level) {
FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_LEVEL_CHANGED, level);
}
}
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index b296ef2..1ff01a6 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -1049,7 +1049,11 @@
for (ComponentName c : possibleServices) {
if (Objects.equals(c.getPackageName(), pkg)) {
- nm.setNotificationListenerAccessGrantedForUser(c, userId, true);
+ try {
+ nm.setNotificationListenerAccessGrantedForUser(c, userId, true);
+ } catch (Exception e) {
+ Slog.w(TAG, "Could not grant NLS access to package " + pkg, e);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index ad9c3b2..83fd725 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -418,13 +418,17 @@
return;
}
- final boolean waitAppTransition = isKeyguardLocked(displayId);
- mWindowManager.mPolicy.onKeyguardOccludedChangedLw(isDisplayOccluded(DEFAULT_DISPLAY),
- waitAppTransition);
- if (waitAppTransition) {
- mService.deferWindowLayout();
- try {
- if (isDisplayOccluded(DEFAULT_DISPLAY)) {
+ final TransitionController tc = mRootWindowContainer.mTransitionController;
+
+ final boolean occluded = isDisplayOccluded(displayId);
+ final boolean performTransition = isKeyguardLocked(displayId);
+ final boolean executeTransition = performTransition && !tc.isCollecting();
+
+ mWindowManager.mPolicy.onKeyguardOccludedChangedLw(occluded);
+ mService.deferWindowLayout();
+ try {
+ if (isKeyguardLocked(displayId)) {
+ if (occluded) {
mRootWindowContainer.getDefaultDisplay().requestTransitionAndLegacyPrepare(
TRANSIT_KEYGUARD_OCCLUDE,
TRANSIT_FLAG_KEYGUARD_OCCLUDING,
@@ -434,11 +438,19 @@
TRANSIT_KEYGUARD_UNOCCLUDE,
TRANSIT_FLAG_KEYGUARD_UNOCCLUDING);
}
- updateKeyguardSleepToken(DEFAULT_DISPLAY);
- mWindowManager.executeAppTransition();
- } finally {
- mService.continueWindowLayout();
+ } else {
+ if (tc.inTransition()) {
+ tc.mStateValidators.add(mWindowManager.mPolicy::applyKeyguardOcclusionChange);
+ } else {
+ mWindowManager.mPolicy.applyKeyguardOcclusionChange();
+ }
}
+ updateKeyguardSleepToken(displayId);
+ if (performTransition && executeTransition) {
+ mWindowManager.executeAppTransition();
+ }
+ } finally {
+ mService.continueWindowLayout();
}
}
@@ -485,6 +497,9 @@
}
}
+ /**
+ * @return true if Keyguard is occluded or the device is dreaming.
+ */
boolean isDisplayOccluded(int displayId) {
return getDisplayState(displayId).mOccluded;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 54fec3e..d9a954f 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1061,8 +1061,10 @@
displayHasContent = true;
} else if (displayContent != null &&
(!mObscureApplicationContentOnSecondaryDisplays
+ || displayContent.isKeyguardAlwaysUnlocked()
|| (obscured && type == TYPE_KEYGUARD_DIALOG))) {
- // Allow full screen keyguard presentation dialogs to be seen.
+ // Allow full screen keyguard presentation dialogs to be seen, or simply ignore the
+ // keyguard if this display is always unlocked.
displayHasContent = true;
}
if ((privateflags & PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE) != 0) {
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index 5860776..c914fa1 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -48,6 +48,7 @@
import android.os.UserHandle;
import android.util.Slog;
import android.view.RemoteAnimationAdapter;
+import android.window.RemoteTransition;
import android.window.WindowContainerToken;
import com.android.internal.annotations.VisibleForTesting;
@@ -385,6 +386,18 @@
throw new SecurityException(msg);
}
+ // Check permission for remote transitions
+ final RemoteTransition transition = options.getRemoteTransition();
+ if (transition != null && supervisor.mService.checkPermission(
+ CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
+ != PERMISSION_GRANTED) {
+ final String msg = "Permission Denial: starting " + getIntentString(intent)
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ") with remoteTransition";
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
// If launched from bubble is specified, then ensure that the caller is system or sysui.
if (options.getLaunchedFromBubble() && !isSystemOrSystemUI(callingPid, callingUid)) {
final String msg = "Permission Denial: starting " + getIntentString(intent)
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 5f2d3ca8..2c582c1 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -27,6 +27,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
+import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -1093,6 +1094,16 @@
final Task task = ar.getTask();
if (task == null) continue;
boolean visibleAtTransitionEnd = mVisibleAtTransitionEndTokens.contains(ar);
+ // visibleAtTransitionEnd is used to guard against pre-maturely committing
+ // invisible on a window which is actually hidden by a later transition and not this
+ // one. However, for a transient launch, we can't use this mechanism because the
+ // visibility is determined at finish. Instead, use a different heuristic: don't
+ // commit invisible if the window is already in a later transition. That later
+ // transition will then handle the commit.
+ if (isTransientLaunch(ar) && !ar.isVisibleRequested()
+ && mController.inCollectingTransition(ar)) {
+ visibleAtTransitionEnd = true;
+ }
// We need both the expected visibility AND current requested-visibility to be
// false. If it is expected-visible but not currently visible, it means that
// another animation is queued-up to animate this to invisibility, so we can't
@@ -2649,7 +2660,7 @@
}
private void validateKeyguardOcclusion() {
- if ((mFlags & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
+ if ((mFlags & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0) {
mController.mStateValidators.add(
mController.mAtm.mWindowManager.mPolicy::applyKeyguardOcclusionChange);
}
@@ -2666,7 +2677,10 @@
mController.mStateValidators.add(() -> {
for (int i = mTargets.size() - 1; i >= 0; --i) {
final ChangeInfo change = mTargets.get(i);
- if (!change.mContainer.isVisibleRequested()) continue;
+ if (!change.mContainer.isVisibleRequested()
+ || change.mContainer.mSurfaceControl == null) {
+ continue;
+ }
Slog.e(TAG, "Force show for visible " + change.mContainer
+ " which may be hidden by transition unexpectedly");
change.mContainer.getSyncTransaction().show(change.mContainer.mSurfaceControl);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cc9ac76..5de5bab 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3389,12 +3389,20 @@
// apps won't always be considered as foreground state.
// Exclude private presentations as they can only be shown on private virtual displays and
// shouldn't be the cause of an app be considered foreground.
- if (mAttrs.type >= FIRST_SYSTEM_WINDOW && mAttrs.type != TYPE_TOAST
- && mAttrs.type != TYPE_PRIVATE_PRESENTATION) {
+ // Exclude presentations on virtual displays as they are not actually visible.
+ if (mAttrs.type >= FIRST_SYSTEM_WINDOW
+ && mAttrs.type != TYPE_TOAST
+ && mAttrs.type != TYPE_PRIVATE_PRESENTATION
+ && !(mAttrs.type == TYPE_PRESENTATION && isOnVirtualDisplay())
+ ) {
mWmService.mAtmService.mActiveUids.onNonAppSurfaceVisibilityChanged(mOwnerUid, shown);
}
}
+ private boolean isOnVirtualDisplay() {
+ return getDisplayContent().mDisplay.getType() == Display.TYPE_VIRTUAL;
+ }
+
private void logExclusionRestrictions(int side) {
if (!logsGestureExclusionRestrictions(this)
|| SystemClock.uptimeMillis() < mLastExclusionLogUptimeMillis[side]
diff --git a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
index 46c90b4..bafa4a5 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
@@ -126,6 +126,7 @@
mElementKeys = new HashSet<>(requestOption
.getCredentialRetrievalData()
.getStringArrayList(CredentialOption.SUPPORTED_ELEMENT_KEYS));
+ mStatus = Status.PENDING;
}
protected ProviderRegistryGetSession(@NonNull Context context,
@@ -143,6 +144,7 @@
mElementKeys = new HashSet<>(requestOption
.getCredentialRetrievalData()
.getStringArrayList(CredentialOption.SUPPORTED_ELEMENT_KEYS));
+ mStatus = Status.PENDING;
}
private List<Entry> prepareUiCredentialEntries(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index cebc540..4347dd6 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4148,6 +4148,30 @@
}
@Test
+ public void testSetListenerAccessForUser_grantWithNameTooLong_throws() {
+ UserHandle user = UserHandle.of(mContext.getUserId() + 10);
+ ComponentName c = new ComponentName("com.example.package",
+ com.google.common.base.Strings.repeat("Blah", 150));
+
+ assertThrows(IllegalArgumentException.class,
+ () -> mBinderService.setNotificationListenerAccessGrantedForUser(
+ c, user.getIdentifier(), /* enabled= */ true, true));
+ }
+
+ @Test
+ public void testSetListenerAccessForUser_revokeWithNameTooLong_okay() throws Exception {
+ UserHandle user = UserHandle.of(mContext.getUserId() + 10);
+ ComponentName c = new ComponentName("com.example.package",
+ com.google.common.base.Strings.repeat("Blah", 150));
+
+ mBinderService.setNotificationListenerAccessGrantedForUser(
+ c, user.getIdentifier(), /* enabled= */ false, true);
+
+ verify(mListeners).setPackageOrComponentEnabled(
+ c.flattenToString(), user.getIdentifier(), true, /* enabled= */ false, true);
+ }
+
+ @Test
public void testSetAssistantAccessForUser() throws Exception {
UserInfo ui = new UserInfo();
ui.id = mContext.getUserId() + 10;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index adf3f39..bd111ad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -233,7 +233,7 @@
}
@Override
- public void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition) {
+ public void onKeyguardOccludedChangedLw(boolean occluded) {
}
public void setSafeMode(boolean safeMode) {
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
index 7fe8582..c508fa9 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
@@ -59,6 +59,8 @@
private String mDeviceName = "";
private String mDeviceDescription = "";
+ private boolean mHasJackDetect = true;
+
public UsbAlsaDevice(IAudioService audioService, int card, int device, String deviceAddress,
boolean hasOutput, boolean hasInput,
boolean isInputHeadset, boolean isOutputHeadset, boolean isDock) {
@@ -168,8 +170,14 @@
if (mJackDetector != null) {
return;
}
+ if (!mHasJackDetect) {
+ return;
+ }
// If no jack detect capabilities exist, mJackDetector will be null.
mJackDetector = UsbAlsaJackDetector.startJackDetect(this);
+ if (mJackDetector == null) {
+ mHasJackDetect = false;
+ }
}
/** Stops a jack-detection thread. */
@@ -182,8 +190,8 @@
/** Start using this device as the selected USB Audio Device. */
public synchronized void start() {
- startInput();
startOutput();
+ startInput();
}
/** Start using this device as the selected USB input device. */
@@ -208,8 +216,8 @@
/** Stop using this device as the selected USB Audio Device. */
public synchronized void stop() {
- stopInput();
stopOutput();
+ stopInput();
}
/** Stop using this device as the selected USB input device. */
diff --git a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
index db36975..346622f 100644
--- a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
+++ b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
@@ -61,8 +61,11 @@
options.setTestMethodName("testCollectAllApexInfo");
// Collect APEX package names from /apex, then pass them as expectation to be verified.
+ // The package names are collected from the find name with deduplication (NB: we used to
+ // deduplicate by dropping directory names with '@', but there's a DCLA case where it only
+ // has one directory with '@'. So we have to keep it and deduplicate the current way).
CommandResult result = getDevice().executeShellV2Command(
- "ls -d /apex/*/ |grep -v @ |grep -v /apex/sharedlibs |cut -d/ -f3");
+ "ls -d /apex/*/ |grep -v /apex/sharedlibs |cut -d/ -f3 |cut -d@ -f1 |sort |uniq");
assertTrue(result.getStatus() == CommandStatus.SUCCESS);
String[] packageNames = result.getStdout().split("\n");
for (var i = 0; i < packageNames.length; i++) {