Merge "Add first widget Preference for SPA"
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 40a3c92..448ee61 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -316,8 +316,6 @@
mIam.startUserInBackground(userId);
}, Intent.ACTION_USER_STARTED, Intent.ACTION_MEDIA_MOUNTED);
- waitForBroadcastIdle();
-
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -680,8 +678,6 @@
}
private void stopUser(int userId, boolean force) throws RemoteException {
- waitForBroadcastIdle();
-
final CountDownLatch latch = new CountDownLatch(1);
mIam.stopUser(userId, force /* force */, new IStopUserCallback.Stub() {
@Override
@@ -886,8 +882,4 @@
assertEquals("", ShellHelper.runShellCommand("setprop " + name + " " + value));
return oldValue;
}
-
- private void waitForBroadcastIdle() {
- ShellHelper.runShellCommand("am wait-for-broadcast-idle");
- }
}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index cd93551..d8e25b6 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -5218,13 +5218,15 @@
+ TareBill.getName(bill) + " changed to " + canAfford);
}
- ArrayMap<EconomyManagerInternal.ActionBill, Boolean> actionAffordability =
- mAffordabilityCache.get(userId, packageName);
- if (actionAffordability == null) {
- actionAffordability = new ArrayMap<>();
- mAffordabilityCache.add(userId, packageName, actionAffordability);
+ synchronized (mLock) {
+ ArrayMap<EconomyManagerInternal.ActionBill, Boolean> actionAffordability =
+ mAffordabilityCache.get(userId, packageName);
+ if (actionAffordability == null) {
+ actionAffordability = new ArrayMap<>();
+ mAffordabilityCache.add(userId, packageName, actionAffordability);
+ }
+ actionAffordability.put(bill, canAfford);
}
- actionAffordability.put(bill, canAfford);
mHandler.obtainMessage(AlarmHandler.TARE_AFFORDABILITY_CHANGED, userId,
canAfford ? 1 : 0, packageName)
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 29cd996..52882fe 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -546,6 +546,9 @@
if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
requiredConstraints |= CONSTRAINT_DEADLINE;
}
+ if (job.isPrefetch()) {
+ requiredConstraints |= CONSTRAINT_PREFETCH;
+ }
boolean exemptedMediaUrisOnly = false;
if (job.getTriggerContentUris() != null) {
requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
@@ -1135,11 +1138,6 @@
return hasConstraint(CONSTRAINT_IDLE);
}
- /** Returns true if the job has a prefetch constraint */
- public boolean hasPrefetchConstraint() {
- return hasConstraint(CONSTRAINT_PREFETCH);
- }
-
public boolean hasContentTriggerConstraint() {
// No need to check mDynamicConstraints since content trigger will only be in that list if
// it's already in the requiredConstraints list.
diff --git a/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt b/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
index dbc3b51..f5184e7 100644
--- a/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
+++ b/boot/hiddenapi/hiddenapi-max-target-r-loprio.txt
@@ -39,7 +39,6 @@
Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V
Lcom/android/ims/internal/uce/uceservice/IUceListener$Stub;-><init>()V
Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
-Lcom/android/internal/app/IVoiceInteractionManagerService$Stub$Proxy;->showSessionFromSession(Landroid/os/IBinder;Landroid/os/Bundle;I)Z
Lcom/android/internal/appwidget/IAppWidgetService$Stub;->TRANSACTION_bindAppWidgetId:I
Lcom/android/internal/telephony/ITelephony$Stub;->DESCRIPTOR:Ljava/lang/String;
Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_dial:I
diff --git a/core/api/current.txt b/core/api/current.txt
index af18792..1cd8253 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -18904,6 +18904,7 @@
method public void onUnbindInput();
method @Deprecated public void onUpdateCursor(android.graphics.Rect);
method public void onUpdateCursorAnchorInfo(android.view.inputmethod.CursorAnchorInfo);
+ method public void onUpdateEditorToolType(int);
method public void onUpdateExtractedText(int, android.view.inputmethod.ExtractedText);
method public void onUpdateExtractingViews(android.view.inputmethod.EditorInfo);
method public void onUpdateExtractingVisibility(android.view.inputmethod.EditorInfo);
@@ -45246,6 +45247,8 @@
method public static boolean extendRight(android.text.Spannable, android.text.Layout);
method public static final void extendSelection(android.text.Spannable, int);
method public static boolean extendToLeftEdge(android.text.Spannable, android.text.Layout);
+ method public static boolean extendToParagraphEnd(@NonNull android.text.Spannable);
+ method public static boolean extendToParagraphStart(@NonNull android.text.Spannable);
method public static boolean extendToRightEdge(android.text.Spannable, android.text.Layout);
method public static boolean extendUp(android.text.Spannable, android.text.Layout);
method public static final int getSelectionEnd(CharSequence);
@@ -45254,6 +45257,8 @@
method public static boolean moveLeft(android.text.Spannable, android.text.Layout);
method public static boolean moveRight(android.text.Spannable, android.text.Layout);
method public static boolean moveToLeftEdge(android.text.Spannable, android.text.Layout);
+ method public static boolean moveToParagraphEnd(@NonNull android.text.Spannable, @NonNull android.text.Layout);
+ method public static boolean moveToParagraphStart(@NonNull android.text.Spannable, @NonNull android.text.Layout);
method public static boolean moveToRightEdge(android.text.Spannable, android.text.Layout);
method public static boolean moveUp(android.text.Spannable, android.text.Layout);
method public static final void removeSelection(android.text.Spannable);
@@ -45702,6 +45707,7 @@
method protected boolean left(android.widget.TextView, android.text.Spannable);
method protected boolean lineEnd(android.widget.TextView, android.text.Spannable);
method protected boolean lineStart(android.widget.TextView, android.text.Spannable);
+ method public boolean nextParagraph(@NonNull android.widget.TextView, @NonNull android.text.Spannable);
method public boolean onGenericMotionEvent(android.widget.TextView, android.text.Spannable, android.view.MotionEvent);
method public boolean onKeyDown(android.widget.TextView, android.text.Spannable, int, android.view.KeyEvent);
method public boolean onKeyOther(android.widget.TextView, android.text.Spannable, android.view.KeyEvent);
@@ -45711,6 +45717,7 @@
method public boolean onTrackballEvent(android.widget.TextView, android.text.Spannable, android.view.MotionEvent);
method protected boolean pageDown(android.widget.TextView, android.text.Spannable);
method protected boolean pageUp(android.widget.TextView, android.text.Spannable);
+ method public boolean previousParagraph(@NonNull android.widget.TextView, @NonNull android.text.Spannable);
method protected boolean right(android.widget.TextView, android.text.Spannable);
method protected boolean top(android.widget.TextView, android.text.Spannable);
method protected boolean up(android.widget.TextView, android.text.Spannable);
@@ -52968,9 +52975,11 @@
method @Nullable public android.view.inputmethod.SurroundingText getInitialSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
method @Nullable public CharSequence getInitialTextAfterCursor(@IntRange(from=0) int, int);
method @Nullable public CharSequence getInitialTextBeforeCursor(@IntRange(from=0) int, int);
+ method public int getInitialToolType();
method public final void makeCompatible(int);
method public void setInitialSurroundingSubText(@NonNull CharSequence, int);
method public void setInitialSurroundingText(@NonNull CharSequence);
+ method public void setInitialToolType(int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.EditorInfo> CREATOR;
field public static final int IME_ACTION_DONE = 6; // 0x6
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 6fc0c26..f17d5b7 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -16,6 +16,8 @@
package android.app;
+import static android.view.Display.INVALID_DISPLAY;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -369,7 +371,8 @@
* @hide
*/
public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
- return getTasks(maxNum, false /* filterForVisibleRecents */);
+ return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */,
+ INVALID_DISPLAY);
}
/**
@@ -378,7 +381,8 @@
*/
public List<ActivityManager.RunningTaskInfo> getTasks(
int maxNum, boolean filterOnlyVisibleRecents) {
- return getTasks(maxNum, filterOnlyVisibleRecents, false /* keepIntentExtra */);
+ return getTasks(maxNum, filterOnlyVisibleRecents, false /* keepIntentExtra */,
+ INVALID_DISPLAY);
}
/**
@@ -388,8 +392,20 @@
*/
public List<ActivityManager.RunningTaskInfo> getTasks(
int maxNum, boolean filterOnlyVisibleRecents, boolean keepIntentExtra) {
+ return getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra, INVALID_DISPLAY);
+ }
+
+ /**
+ * @return List of running tasks that can be filtered by visibility and displayId in recents
+ * and keep intent extra.
+ * @param displayId the target display id, or {@link INVALID_DISPLAY} not to filter by displayId
+ * @hide
+ */
+ public List<ActivityManager.RunningTaskInfo> getTasks(
+ int maxNum, boolean filterOnlyVisibleRecents, boolean keepIntentExtra, int displayId) {
try {
- return getService().getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra);
+ return getService().getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra,
+ displayId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 0bf16a0..6576a1a 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -158,7 +158,7 @@
boolean removeTask(int taskId);
void removeAllVisibleRecentTasks();
List<ActivityManager.RunningTaskInfo> getTasks(int maxNum, boolean filterOnlyVisibleRecents,
- boolean keepIntentExtra);
+ boolean keepIntentExtra, int displayId);
void moveTaskToFront(in IApplicationThread app, in String callingPackage, int task,
int flags, in Bundle options);
ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
@@ -231,7 +231,7 @@
in IBinder activityToken, int flags);
boolean isAssistDataAllowedOnCurrentActivity();
boolean requestAssistDataForTask(in IAssistDataReceiver receiver, int taskId,
- in String callingPackageName);
+ in String callingPackageName, String callingAttributionTag);
/**
* Notify the system that the keyguard is going away.
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 3dedc41..f47c1e0 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -1974,10 +1974,10 @@
return true;
}
int diff = other.seq - seq;
- if (diff > 0x10000) {
+ if (Math.abs(diff) > 0x10000000) {
// If there has been a sufficiently large jump, assume the
// sequence has wrapped around.
- return false;
+ return diff < 0;
}
return diff > 0;
}
diff --git a/core/java/android/database/TEST_MAPPING b/core/java/android/database/TEST_MAPPING
new file mode 100644
index 0000000..4a7fa66
--- /dev/null
+++ b/core/java/android/database/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsDatabaseTestCases"
+ }
+ ]
+}
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index e960df1..3d59387 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -80,6 +80,7 @@
private static final int DO_START_STYLUS_HANDWRITING = 110;
private static final int DO_INIT_INK_WINDOW = 120;
private static final int DO_FINISH_STYLUS_HANDWRITING = 130;
+ private static final int DO_UPDATE_TOOL_TYPE = 140;
final WeakReference<InputMethodServiceInternal> mTarget;
final Context mContext;
@@ -234,6 +235,10 @@
inputMethod.canStartStylusHandwriting(msg.arg1);
return;
}
+ case DO_UPDATE_TOOL_TYPE: {
+ inputMethod.updateEditorToolType(msg.arg1);
+ return;
+ }
case DO_START_STYLUS_HANDWRITING: {
final SomeArgs args = (SomeArgs) msg.obj;
inputMethod.startStylusHandwriting(msg.arg1, (InputChannel) args.arg1,
@@ -402,6 +407,14 @@
@BinderThread
@Override
+ public void updateEditorToolType(int toolType)
+ throws RemoteException {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageI(DO_UPDATE_TOOL_TYPE, toolType));
+ }
+
+ @BinderThread
+ @Override
public void startStylusHandwriting(int requestId, @NonNull InputChannel channel,
@Nullable List<MotionEvent> stylusEvents)
throws RemoteException {
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index b108490..c2027b1 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -953,6 +953,15 @@
* @hide
*/
@Override
+ public void updateEditorToolType(int toolType) {
+ onUpdateEditorToolType(toolType);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
+ @Override
public void canStartStylusHandwriting(int requestId) {
if (DEBUG) Log.v(TAG, "canStartStylusHandwriting()");
if (mHandwritingRequestId.isPresent()) {
@@ -3012,11 +3021,14 @@
* not call to inform the IME of this interaction.
* @param focusChanged true if the user changed the focused view by this click.
* @see InputMethodManager#viewClicked(View)
+ * @see #onUpdateEditorToolType(int)
* @deprecated The method may not be called for composite {@link View} that works as a giant
* "Canvas", which can host its own UI hierarchy and sub focus state.
* {@link android.webkit.WebView} is a good example. Application / IME developers
* should not rely on this method. If your goal is just being notified when an
* on-going input is interrupted, simply monitor {@link #onFinishInput()}.
+ * If your goal is to know what {@link MotionEvent#getToolType(int)} clicked on
+ * editor, use {@link #onUpdateEditorToolType(int)} instead.
*/
@Deprecated
public void onViewClicked(boolean focusChanged) {
@@ -3024,6 +3036,19 @@
}
/**
+ * Called when the user tapped or clicked an {@link android.widget.Editor}.
+ * This can be useful when IME makes a decision of showing Virtual keyboard based on what
+ * {@link MotionEvent#getToolType(int)} was used to click the editor.
+ * e.g. when toolType is {@link MotionEvent#TOOL_TYPE_STYLUS}, IME may choose to show a
+ * companion widget instead of normal virtual keyboard.
+ * <p> Default implementation does nothing. </p>
+ * @param toolType what {@link MotionEvent#getToolType(int)} was used to click on editor.
+ */
+ public void onUpdateEditorToolType(int toolType) {
+ // Intentionally empty
+ }
+
+ /**
* Called when the application has reported a new location of its text
* cursor. This is only called if explicitly requested by the input method.
* The default implementation does nothing.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6fc2811..9000899 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -14257,7 +14257,7 @@
*
* @hide
*/
- public static final int DEFAULT_ENABLE_TARE = 0;
+ public static final int DEFAULT_ENABLE_TARE = 1;
/**
* Whether to enable the TARE AlarmManager economic policy or not.
diff --git a/core/java/android/service/voice/VoiceInteractionManagerInternal.java b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
index b20dccc..c47fdd3 100644
--- a/core/java/android/service/voice/VoiceInteractionManagerInternal.java
+++ b/core/java/android/service/voice/VoiceInteractionManagerInternal.java
@@ -16,7 +16,9 @@
package android.service.voice;
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.os.Bundle;
import android.os.IBinder;
@@ -32,9 +34,12 @@
* Start a new voice interaction session when requested from within an activity
* by Activity.startLocalVoiceInteraction()
* @param callingActivity The binder token representing the calling activity.
- * @param options
+ * @param attributionTag The attribution tag of the calling context or {@code null} for default
+ * attribution
+ * @param options A Bundle of private arguments to the current voice interaction service
*/
- public abstract void startLocalVoiceInteraction(IBinder callingActivity, Bundle options);
+ public abstract void startLocalVoiceInteraction(@NonNull IBinder callingActivity,
+ @Nullable String attributionTag, @NonNull Bundle options);
/**
* Returns whether the currently selected voice interaction service supports local voice
@@ -65,6 +70,13 @@
public abstract HotwordDetectionServiceIdentity getHotwordDetectionServiceIdentity();
/**
+ * Called by {@code UMS.convertPreCreatedUserIfPossible()} when a new user is not created from
+ * scratched, but converted from the pool of existing pre-created users.
+ */
+ // TODO(b/226201975): remove method once RoleService supports pre-created users
+ public abstract void onPreCreatedUserConversion(@UserIdInt int userId);
+
+ /**
* Provides the uids of the currently active
* {@link android.service.voice.HotwordDetectionService} and its owning package. The
* HotwordDetectionService is an isolated service, so it has a separate uid.
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 1170237..1b46107 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -242,7 +242,7 @@
throw new IllegalStateException("Not available until onReady() is called");
}
try {
- mSystemService.showSession(args, flags);
+ mSystemService.showSession(args, flags, getAttributionTag());
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index b7ec486..d08a67e 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -1384,7 +1384,8 @@
throw new IllegalStateException("Can't call before onCreate()");
}
try {
- mSystemService.showSessionFromSession(mToken, args, flags);
+ mSystemService.showSessionFromSession(mToken, args, flags,
+ mContext.getAttributionTag());
} catch (RemoteException e) {
}
}
diff --git a/core/java/android/text/Selection.java b/core/java/android/text/Selection.java
index 57c1d23..6a54d23 100644
--- a/core/java/android/text/Selection.java
+++ b/core/java/android/text/Selection.java
@@ -16,6 +16,7 @@
package android.text;
+import android.annotation.NonNull;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -346,6 +347,100 @@
return false;
}
+ private static final char PARAGRAPH_SEPARATOR = '\n';
+
+ /**
+ * Move the cusrot to the closest paragraph start offset.
+ *
+ * @param text the spannable text
+ * @param layout layout to be used for drawing.
+ * @return true if the cursor is moved, otherwise false.
+ */
+ public static boolean moveToParagraphStart(@NonNull Spannable text, @NonNull Layout layout) {
+ int start = getSelectionStart(text);
+ int end = getSelectionEnd(text);
+
+ if (start != end) {
+ setSelection(text, chooseHorizontal(layout, -1, start, end));
+ return true;
+ } else {
+ int to = TextUtils.lastIndexOf(text, PARAGRAPH_SEPARATOR, start - 1);
+ if (to == -1) {
+ to = 0; // If not found, use the document start offset as a paragraph start.
+ }
+ if (to != end) {
+ setSelection(text, to);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Move the cursor to the closest paragraph end offset.
+ *
+ * @param text the spannable text
+ * @param layout layout to be used for drawing.
+ * @return true if the cursor is moved, otherwise false.
+ */
+ public static boolean moveToParagraphEnd(@NonNull Spannable text, @NonNull Layout layout) {
+ int start = getSelectionStart(text);
+ int end = getSelectionEnd(text);
+
+ if (start != end) {
+ setSelection(text, chooseHorizontal(layout, 1, start, end));
+ return true;
+ } else {
+ int to = TextUtils.indexOf(text, PARAGRAPH_SEPARATOR, end + 1);
+ if (to == -1) {
+ to = text.length();
+ }
+ if (to != end) {
+ setSelection(text, to);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Extend the selection to the closest paragraph start offset.
+ *
+ * @param text the spannable text
+ * @return true if the selection is extended, otherwise false
+ */
+ public static boolean extendToParagraphStart(@NonNull Spannable text) {
+ int end = getSelectionEnd(text);
+ int to = TextUtils.lastIndexOf(text, PARAGRAPH_SEPARATOR, end - 1);
+ if (to == -1) {
+ to = 0; // If not found, use the document start offset as a paragraph start.
+ }
+ if (to != end) {
+ extendSelection(text, to);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Extend the selection to the closest paragraph end offset.
+ *
+ * @param text the spannable text
+ * @return true if the selection is extended, otherwise false
+ */
+ public static boolean extendToParagraphEnd(@NonNull Spannable text) {
+ int end = getSelectionEnd(text);
+ int to = TextUtils.indexOf(text, PARAGRAPH_SEPARATOR, end + 1);
+ if (to == -1) {
+ to = text.length();
+ }
+ if (to != end) {
+ extendSelection(text, to);
+ return true;
+ }
+ return false;
+ }
+
/**
* Move the selection end to the buffer offset physically above
* the current selection end.
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index 57fe131..d3367d0 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -16,6 +16,7 @@
package android.text.method;
+import android.annotation.NonNull;
import android.graphics.Rect;
import android.text.Layout;
import android.text.Selection;
@@ -222,6 +223,26 @@
}
@Override
+ public boolean previousParagraph(@NonNull TextView widget, @NonNull Spannable buffer) {
+ final Layout layout = widget.getLayout();
+ if (isSelecting(buffer)) {
+ return Selection.extendToParagraphStart(buffer);
+ } else {
+ return Selection.moveToParagraphStart(buffer, layout);
+ }
+ }
+
+ @Override
+ public boolean nextParagraph(@NonNull TextView widget, @NonNull Spannable buffer) {
+ final Layout layout = widget.getLayout();
+ if (isSelecting(buffer)) {
+ return Selection.extendToParagraphEnd(buffer);
+ } else {
+ return Selection.moveToParagraphEnd(buffer, layout);
+ }
+ }
+
+ @Override
public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
int initialScrollX = -1;
int initialScrollY = -1;
diff --git a/core/java/android/text/method/BaseMovementMethod.java b/core/java/android/text/method/BaseMovementMethod.java
index 155a2c4..7a4b3a0 100644
--- a/core/java/android/text/method/BaseMovementMethod.java
+++ b/core/java/android/text/method/BaseMovementMethod.java
@@ -16,6 +16,7 @@
package android.text.method;
+import android.annotation.NonNull;
import android.text.Layout;
import android.text.Spannable;
import android.view.InputDevice;
@@ -190,6 +191,9 @@
} else if (KeyEvent.metaStateHasModifiers(movementMetaState,
KeyEvent.META_ALT_ON)) {
return top(widget, buffer);
+ } else if (KeyEvent.metaStateHasModifiers(movementMetaState,
+ KeyEvent.META_CTRL_ON)) {
+ return previousParagraph(widget, buffer);
}
break;
@@ -199,6 +203,9 @@
} else if (KeyEvent.metaStateHasModifiers(movementMetaState,
KeyEvent.META_ALT_ON)) {
return bottom(widget, buffer);
+ } else if (KeyEvent.metaStateHasModifiers(movementMetaState,
+ KeyEvent.META_CTRL_ON)) {
+ return nextParagraph(widget, buffer);
}
break;
@@ -399,6 +406,28 @@
return false;
}
+ /**
+ * Performs a previous paragraph movement action.
+ *
+ * @param widget the text view
+ * @param buffer the text buffer
+ * @return true if the event was handled, otherwise false.
+ */
+ public boolean previousParagraph(@NonNull TextView widget, @NonNull Spannable buffer) {
+ return false;
+ }
+
+ /**
+ * Performs a next paragraph movement action.
+ *
+ * @param widget the text view
+ * @param buffer the text buffer
+ * @return true if the event was handled, otherwise false.
+ */
+ public boolean nextParagraph(@NonNull TextView widget, @NonNull Spannable buffer) {
+ return false;
+ }
+
private int getTopLine(TextView widget) {
return widget.getLayout().getLineForVertical(widget.getScrollY());
}
diff --git a/core/java/android/view/IWindowFocusObserver.aidl b/core/java/android/view/IWindowFocusObserver.aidl
index d14bb48..3b23c77 100644
--- a/core/java/android/view/IWindowFocusObserver.aidl
+++ b/core/java/android/view/IWindowFocusObserver.aidl
@@ -16,7 +16,7 @@
package android.view;
/** {@hide} */
-interface IWindowFocusObserver
+oneway interface IWindowFocusObserver
{
void focusGained(IBinder inputToken);
void focusLost(IBinder inputToken);
diff --git a/core/java/android/view/RemoteAnimationTarget.java b/core/java/android/view/RemoteAnimationTarget.java
index 44f419a..0db28d4 100644
--- a/core/java/android/view/RemoteAnimationTarget.java
+++ b/core/java/android/view/RemoteAnimationTarget.java
@@ -363,11 +363,11 @@
pw.print(prefix); pw.print("leash="); pw.println(leash);
pw.print(prefix); pw.print("taskInfo="); pw.println(taskInfo);
pw.print(prefix); pw.print("allowEnterPip="); pw.println(allowEnterPip);
- pw.print(prefix); pw.print("windowType="); pw.print(windowType);
- pw.print(prefix); pw.print("hasAnimatingParent="); pw.print(hasAnimatingParent);
- pw.print(prefix); pw.print("backgroundColor="); pw.print(backgroundColor);
- pw.print(prefix); pw.print("showBackdrop="); pw.print(showBackdrop);
- pw.print(prefix); pw.print("willShowImeOnTarget="); pw.print(willShowImeOnTarget);
+ pw.print(prefix); pw.print("windowType="); pw.println(windowType);
+ pw.print(prefix); pw.print("hasAnimatingParent="); pw.println(hasAnimatingParent);
+ pw.print(prefix); pw.print("backgroundColor="); pw.println(backgroundColor);
+ pw.print(prefix); pw.print("showBackdrop="); pw.println(showBackdrop);
+ pw.print(prefix); pw.print("willShowImeOnTarget="); pw.println(willShowImeOnTarget);
}
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 1bedbfc..e1966a0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -734,6 +734,8 @@
final PointF mDragPoint = new PointF();
final PointF mLastTouchPoint = new PointF();
int mLastTouchSource;
+ /** Tracks last {@link MotionEvent#getToolType(int)} with {@link MotionEvent#ACTION_UP}. **/
+ private int mLastClickToolType;
private boolean mProfileRendering;
private Choreographer.FrameCallback mRenderProfiler;
@@ -6417,11 +6419,16 @@
event.offsetLocation(0, mCurScrollY);
}
- // Remember the touch position for possible drag-initiation.
if (event.isTouchEvent()) {
+ // Remember the touch position for possible drag-initiation.
mLastTouchPoint.x = event.getRawX();
mLastTouchPoint.y = event.getRawY();
mLastTouchSource = event.getSource();
+
+ // Register last ACTION_UP. This will be propagated to IME.
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ mLastClickToolType = event.getToolType(event.getActionIndex());
+ }
}
return FORWARD;
}
@@ -8017,6 +8024,14 @@
return mLastTouchSource;
}
+ /**
+ * Used by InputMethodManager.
+ * @hide
+ */
+ public int getLastClickToolType() {
+ return mLastClickToolType;
+ }
+
public void setDragFocus(View newDragTarget, DragEvent event) {
if (mCurrentDragView != newDragTarget && !View.sCascadedDragDrop) {
// Send EXITED and ENTERED notifications to the old and new drag focus views.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index e56c43e..6f69361 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -834,8 +834,8 @@
*
* <p>Default is {@code false}.
*
- * <p>The system enforcement will be added in Android 14, but some devices may start following
- * the requirement before that. The best practice for apps is to always explicitly set this
+ * <p>The system enforcement is added in Android 14, but some devices may start following the
+ * requirement before that. The best practice for apps is to always explicitly set this
* property in AndroidManifest instead of relying on the default value.
*
* <p>Example usage:
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index fdff7a3..55a2d22 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -30,6 +30,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.content.res.Configuration;
+import android.inputmethodservice.InputMethodService;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.LocaleList;
@@ -40,6 +41,7 @@
import android.text.TextUtils;
import android.util.Printer;
import android.util.proto.ProtoOutputStream;
+import android.view.MotionEvent;
import android.view.View;
import android.view.autofill.AutofillId;
@@ -564,6 +566,12 @@
@Nullable
private SurroundingText mInitialSurroundingText = null;
+ /**
+ * Initial {@link MotionEvent#ACTION_UP} tool type {@link MotionEvent#getToolType(int)} that
+ * was used to focus this editor.
+ */
+ private int mInitialToolType = MotionEvent.TOOL_TYPE_UNKNOWN;
+
/**
* Editors may use this method to provide initial input text to IMEs. As the surrounding text
@@ -937,6 +945,31 @@
}
/**
+ * Returns the initial {@link MotionEvent#ACTION_UP} tool type
+ * {@link MotionEvent#getToolType(int)} responsible for focus on the current editor.
+ *
+ * @see #setInitialToolType(int)
+ * @see MotionEvent#getToolType(int)
+ * @see InputMethodService#onUpdateEditorToolType(int)
+ * @return toolType {@link MotionEvent#getToolType(int)}.
+ */
+ public int getInitialToolType() {
+ return mInitialToolType;
+ }
+
+ /**
+ * Set the initial {@link MotionEvent#ACTION_UP} tool type {@link MotionEvent#getToolType(int)}.
+ * that brought focus to the view.
+ *
+ * @see #getInitialToolType()
+ * @see MotionEvent#getToolType(int)
+ * @see InputMethodService#onUpdateEditorToolType(int)
+ */
+ public void setInitialToolType(int toolType) {
+ mInitialToolType = toolType;
+ }
+
+ /**
* Export the state of {@link EditorInfo} into a protocol buffer output stream.
*
* @param proto Stream to write the state to
@@ -972,6 +1005,7 @@
+ " actionId=" + actionId);
pw.println(prefix + "initialSelStart=" + initialSelStart
+ " initialSelEnd=" + initialSelEnd
+ + " initialToolType=" + mInitialToolType
+ " initialCapsMode=0x"
+ Integer.toHexString(initialCapsMode));
pw.println(prefix + "hintText=" + hintText
@@ -1006,6 +1040,7 @@
newEditorInfo.initialSelStart = initialSelStart;
newEditorInfo.initialSelEnd = initialSelEnd;
newEditorInfo.initialCapsMode = initialCapsMode;
+ newEditorInfo.mInitialToolType = mInitialToolType;
newEditorInfo.hintText = TextUtils.stringOrSpannedString(hintText);
newEditorInfo.label = TextUtils.stringOrSpannedString(label);
newEditorInfo.packageName = packageName;
@@ -1036,6 +1071,7 @@
dest.writeInt(initialSelStart);
dest.writeInt(initialSelEnd);
dest.writeInt(initialCapsMode);
+ dest.writeInt(mInitialToolType);
TextUtils.writeToParcel(hintText, dest, flags);
TextUtils.writeToParcel(label, dest, flags);
dest.writeString(packageName);
@@ -1072,6 +1108,7 @@
res.initialSelStart = source.readInt();
res.initialSelEnd = source.readInt();
res.initialCapsMode = source.readInt();
+ res.mInitialToolType = source.readInt();
res.hintText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
res.label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
res.packageName = source.readString();
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
index 78a90d9..429b0b8 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerInvoker.java
@@ -111,10 +111,11 @@
@AnyThread
boolean showSoftInput(@NonNull IInputMethodClient client, @Nullable IBinder windowToken,
- int flags, @Nullable ResultReceiver resultReceiver,
+ int flags, int lastClickToolType, @Nullable ResultReceiver resultReceiver,
@SoftInputShowHideReason int reason) {
try {
- return mTarget.showSoftInput(client, windowToken, flags, resultReceiver, reason);
+ return mTarget.showSoftInput(
+ client, windowToken, flags, lastClickToolType, resultReceiver, reason);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
index 95add29..bfe6ae6 100644
--- a/core/java/android/view/inputmethod/InputMethod.java
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -377,6 +377,15 @@
}
/**
+ * This method is called when the user tapped or clicked an {@link android.widget.Editor}.
+ * @param toolType {@link android.view.MotionEvent#getToolType(int)} used for clicking editor.
+ * @hide
+ */
+ default void updateEditorToolType(int toolType) {
+ // intentionally empty
+ }
+
+ /**
* Start stylus handwriting session.
* @hide
*/
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 899e5fc..0b4a298 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1929,6 +1929,7 @@
mClient,
view.getWindowToken(),
flags,
+ mCurRootView.getLastClickToolType(),
resultReceiver,
reason);
}
@@ -1960,6 +1961,7 @@
mClient,
mCurRootView.getView().getWindowToken(),
flags,
+ mCurRootView.getLastClickToolType(),
resultReceiver,
SoftInputShowHideReason.SHOW_SOFT_INPUT);
}
@@ -2339,6 +2341,9 @@
editorInfo.packageName = view.getContext().getOpPackageName();
editorInfo.autofillId = view.getAutofillId();
editorInfo.fieldId = view.getId();
+ synchronized (mH) {
+ editorInfo.setInitialToolType(mCurRootView.getLastClickToolType());
+ }
InputConnection ic = view.onCreateInputConnection(editorInfo);
if (DEBUG) Log.v(TAG, "Starting input: editorInfo=" + editorInfo + " ic=" + ic);
diff --git a/core/java/android/window/ITaskFragmentOrganizer.aidl b/core/java/android/window/ITaskFragmentOrganizer.aidl
index 3335c9c..79ddadb 100644
--- a/core/java/android/window/ITaskFragmentOrganizer.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizer.aidl
@@ -16,54 +16,9 @@
package android.window;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentTransaction;
/** @hide */
oneway interface ITaskFragmentOrganizer {
- void onTaskFragmentAppeared(in TaskFragmentInfo taskFragmentInfo);
- void onTaskFragmentInfoChanged(in TaskFragmentInfo taskFragmentInfo);
- void onTaskFragmentVanished(in TaskFragmentInfo taskFragmentInfo);
-
- /**
- * Called when the parent leaf Task of organized TaskFragments is changed.
- * When the leaf Task is changed, the organizer may want to update the TaskFragments in one
- * transaction.
- *
- * For case like screen size change, it will trigger onTaskFragmentParentInfoChanged with new
- * Task bounds, but may not trigger onTaskFragmentInfoChanged because there can be an override
- * bounds.
- */
- void onTaskFragmentParentInfoChanged(in IBinder fragmentToken, in Configuration parentConfig);
-
- /**
- * Called when the {@link WindowContainerTransaction} created with
- * {@link WindowContainerTransaction#setErrorCallbackToken(IBinder)} failed on the server side.
- *
- * @param errorCallbackToken Token set through {@link
- * WindowContainerTransaction#setErrorCallbackToken(IBinder)}
- * @param errorBundle Bundle containing the exception, operation type and TaskFragmentInfo
- * if any. Should be created with
- * {@link TaskFragmentOrganizer#putErrorInfoInBundle}.
- */
- void onTaskFragmentError(in IBinder errorCallbackToken, in Bundle errorBundle);
-
- /**
- * Called when an Activity is reparented to the Task with organized TaskFragment. For example,
- * when an Activity enters and then exits Picture-in-picture, it will be reparented back to its
- * orginial Task. In this case, we need to notify the organizer so that it can check if the
- * Activity matches any split rule.
- *
- * @param taskId The Task that the activity is reparented to.
- * @param activityIntent The intent that the activity is original launched with.
- * @param activityToken If the activity belongs to the same process as the organizer, this
- * will be the actual activity token; if the activity belongs to a
- * different process, the server will generate a temporary token that
- * the organizer can use to reparent the activity through
- * {@link WindowContainerTransaction} if needed.
- */
- void onActivityReparentToTask(int taskId, in Intent activityIntent, in IBinder activityToken);
+ void onTransactionReady(in TaskFragmentTransaction transaction);
}
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 765cd81..cc90aaf 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -16,6 +16,13 @@
package android.window;
+import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENT_TO_TASK;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
+
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,6 +34,7 @@
import android.os.RemoteException;
import android.view.RemoteAnimationDefinition;
+import java.util.List;
import java.util.concurrent.Executor;
/**
@@ -186,6 +194,67 @@
public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
@NonNull IBinder activityToken) {}
+ /**
+ * Called when the transaction is ready so that the organizer can update the TaskFragments based
+ * on the changes in transaction.
+ * @hide
+ */
+ public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
+ final List<TaskFragmentTransaction.Change> changes = transaction.getChanges();
+ for (TaskFragmentTransaction.Change change : changes) {
+ // TODO(b/240519866): apply all changes in one WCT.
+ switch (change.getType()) {
+ case TYPE_TASK_FRAGMENT_APPEARED:
+ onTaskFragmentAppeared(change.getTaskFragmentInfo());
+ if (change.getTaskConfiguration() != null) {
+ // TODO(b/240519866): convert to pass TaskConfiguration for all TFs in the
+ // same Task
+ onTaskFragmentParentInfoChanged(
+ change.getTaskFragmentToken(),
+ change.getTaskConfiguration());
+ }
+ break;
+ case TYPE_TASK_FRAGMENT_INFO_CHANGED:
+ if (change.getTaskConfiguration() != null) {
+ // TODO(b/240519866): convert to pass TaskConfiguration for all TFs in the
+ // same Task
+ onTaskFragmentParentInfoChanged(
+ change.getTaskFragmentToken(),
+ change.getTaskConfiguration());
+ }
+ onTaskFragmentInfoChanged(change.getTaskFragmentInfo());
+ break;
+ case TYPE_TASK_FRAGMENT_VANISHED:
+ onTaskFragmentVanished(change.getTaskFragmentInfo());
+ break;
+ case TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED:
+ onTaskFragmentParentInfoChanged(
+ change.getTaskFragmentToken(),
+ change.getTaskConfiguration());
+ break;
+ case TYPE_TASK_FRAGMENT_ERROR:
+ final Bundle errorBundle = change.getErrorBundle();
+ onTaskFragmentError(
+ change.getErrorCallbackToken(),
+ errorBundle.getParcelable(
+ KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, TaskFragmentInfo.class),
+ errorBundle.getInt(KEY_ERROR_CALLBACK_OP_TYPE),
+ errorBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION,
+ java.lang.Throwable.class));
+ break;
+ case TYPE_ACTIVITY_REPARENT_TO_TASK:
+ onActivityReparentToTask(
+ change.getTaskId(),
+ change.getActivityIntent(),
+ change.getActivityToken());
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Unknown TaskFragmentEvent=" + change.getType());
+ }
+ }
+ }
+
@Override
public void applyTransaction(@NonNull WindowContainerTransaction t) {
t.setTaskFragmentOrganizer(mInterface);
@@ -203,51 +272,8 @@
private final ITaskFragmentOrganizer mInterface = new ITaskFragmentOrganizer.Stub() {
@Override
- public void onTaskFragmentAppeared(@NonNull TaskFragmentInfo taskFragmentInfo) {
- mExecutor.execute(
- () -> TaskFragmentOrganizer.this.onTaskFragmentAppeared(taskFragmentInfo));
- }
-
- @Override
- public void onTaskFragmentInfoChanged(@NonNull TaskFragmentInfo taskFragmentInfo) {
- mExecutor.execute(
- () -> TaskFragmentOrganizer.this.onTaskFragmentInfoChanged(taskFragmentInfo));
- }
-
- @Override
- public void onTaskFragmentVanished(@NonNull TaskFragmentInfo taskFragmentInfo) {
- mExecutor.execute(
- () -> TaskFragmentOrganizer.this.onTaskFragmentVanished(taskFragmentInfo));
- }
-
- @Override
- public void onTaskFragmentParentInfoChanged(
- @NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) {
- mExecutor.execute(
- () -> TaskFragmentOrganizer.this.onTaskFragmentParentInfoChanged(
- fragmentToken, parentConfig));
- }
-
- @Override
- public void onTaskFragmentError(
- @NonNull IBinder errorCallbackToken, @NonNull Bundle errorBundle) {
- mExecutor.execute(() -> {
- final TaskFragmentInfo info = errorBundle.getParcelable(
- KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO, TaskFragmentInfo.class);
- TaskFragmentOrganizer.this.onTaskFragmentError(
- errorCallbackToken, info,
- errorBundle.getInt(KEY_ERROR_CALLBACK_OP_TYPE),
- (Throwable) errorBundle.getSerializable(KEY_ERROR_CALLBACK_EXCEPTION,
- java.lang.Throwable.class));
- });
- }
-
- @Override
- public void onActivityReparentToTask(int taskId, @NonNull Intent activityIntent,
- @NonNull IBinder activityToken) {
- mExecutor.execute(
- () -> TaskFragmentOrganizer.this.onActivityReparentToTask(
- taskId, activityIntent, activityToken));
+ public void onTransactionReady(@NonNull TaskFragmentTransaction transaction) {
+ mExecutor.execute(() -> TaskFragmentOrganizer.this.onTransactionReady(transaction));
}
};
diff --git a/core/java/android/window/TaskFragmentTransaction.aidl b/core/java/android/window/TaskFragmentTransaction.aidl
new file mode 100644
index 0000000..aaa2db4
--- /dev/null
+++ b/core/java/android/window/TaskFragmentTransaction.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+parcelable TaskFragmentTransaction;
+parcelable TaskFragmentTransaction.Change;
diff --git a/core/java/android/window/TaskFragmentTransaction.java b/core/java/android/window/TaskFragmentTransaction.java
new file mode 100644
index 0000000..755864f
--- /dev/null
+++ b/core/java/android/window/TaskFragmentTransaction.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Used to communicate information about what are changing on embedded TaskFragments belonging to
+ * the same TaskFragmentOrganizer. A transaction can contain multiple changes.
+ * @see TaskFragmentTransaction.Change
+ * @hide
+ */
+public final class TaskFragmentTransaction implements Parcelable {
+
+ private final ArrayList<Change> mChanges = new ArrayList<>();
+
+ public TaskFragmentTransaction() {}
+
+ private TaskFragmentTransaction(Parcel in) {
+ in.readTypedList(mChanges, Change.CREATOR);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeTypedList(mChanges);
+ }
+
+ /** Adds a {@link Change} to this transaction. */
+ public void addChange(@Nullable Change change) {
+ if (change != null) {
+ mChanges.add(change);
+ }
+ }
+
+ /** Whether this transaction contains any {@link Change}. */
+ public boolean isEmpty() {
+ return mChanges.isEmpty();
+ }
+
+ @NonNull
+ public List<Change> getChanges() {
+ return mChanges;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("TaskFragmentTransaction{changes=[");
+ for (int i = 0; i < mChanges.size(); ++i) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ sb.append(mChanges.get(i));
+ }
+ sb.append("]}");
+ return sb.toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<TaskFragmentTransaction> CREATOR = new Creator<>() {
+ @Override
+ public TaskFragmentTransaction createFromParcel(Parcel in) {
+ return new TaskFragmentTransaction(in);
+ }
+
+ @Override
+ public TaskFragmentTransaction[] newArray(int size) {
+ return new TaskFragmentTransaction[size];
+ }
+ };
+
+ /** Change type: the TaskFragment is attached to the hierarchy. */
+ public static final int TYPE_TASK_FRAGMENT_APPEARED = 1;
+
+ /** Change type: the status of the TaskFragment is changed. */
+ public static final int TYPE_TASK_FRAGMENT_INFO_CHANGED = 2;
+
+ /** Change type: the TaskFragment is removed form the hierarchy. */
+ public static final int TYPE_TASK_FRAGMENT_VANISHED = 3;
+
+ /** Change type: the status of the parent leaf Task is changed. */
+ public static final int TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED = 4;
+
+ /** Change type: the TaskFragment related operation failed on the server side. */
+ public static final int TYPE_TASK_FRAGMENT_ERROR = 5;
+
+ /**
+ * Change type: an Activity is reparented to the Task. For example, when an Activity enters and
+ * then exits Picture-in-picture, it will be reparented back to its original Task. In this case,
+ * we need to notify the organizer so that it can check if the Activity matches any split rule.
+ */
+ public static final int TYPE_ACTIVITY_REPARENT_TO_TASK = 6;
+
+ @IntDef(prefix = { "TYPE_" }, value = {
+ TYPE_TASK_FRAGMENT_APPEARED,
+ TYPE_TASK_FRAGMENT_INFO_CHANGED,
+ TYPE_TASK_FRAGMENT_VANISHED,
+ TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED,
+ TYPE_TASK_FRAGMENT_ERROR,
+ TYPE_ACTIVITY_REPARENT_TO_TASK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface ChangeType {}
+
+ /** Represents the change an embedded TaskFragment undergoes. */
+ public static final class Change implements Parcelable {
+
+ /** @see ChangeType */
+ @ChangeType
+ private final int mType;
+
+ /** @see #setTaskFragmentToken(IBinder) */
+ @Nullable
+ private IBinder mTaskFragmentToken;
+
+ /** @see #setTaskFragmentInfo(TaskFragmentInfo) */
+ @Nullable
+ private TaskFragmentInfo mTaskFragmentInfo;
+
+ /** @see #setTaskId(int) */
+ private int mTaskId;
+
+ /** @see #setTaskConfiguration(Configuration) */
+ @Nullable
+ private Configuration mTaskConfiguration;
+
+ /** @see #setErrorCallbackToken(IBinder) */
+ @Nullable
+ private IBinder mErrorCallbackToken;
+
+ /** @see #setErrorBundle(Bundle) */
+ @Nullable
+ private Bundle mErrorBundle;
+
+ /** @see #setActivityIntent(Intent) */
+ @Nullable
+ private Intent mActivityIntent;
+
+ /** @see #setActivityToken(IBinder) */
+ @Nullable
+ private IBinder mActivityToken;
+
+ public Change(@ChangeType int type) {
+ mType = type;
+ }
+
+ private Change(Parcel in) {
+ mType = in.readInt();
+ mTaskFragmentToken = in.readStrongBinder();
+ mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR);
+ mTaskId = in.readInt();
+ mTaskConfiguration = in.readTypedObject(Configuration.CREATOR);
+ mErrorCallbackToken = in.readStrongBinder();
+ mErrorBundle = in.readBundle(TaskFragmentTransaction.class.getClassLoader());
+ mActivityIntent = in.readTypedObject(Intent.CREATOR);
+ mActivityToken = in.readStrongBinder();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeStrongBinder(mTaskFragmentToken);
+ dest.writeTypedObject(mTaskFragmentInfo, flags);
+ dest.writeInt(mTaskId);
+ dest.writeTypedObject(mTaskConfiguration, flags);
+ dest.writeStrongBinder(mErrorCallbackToken);
+ dest.writeBundle(mErrorBundle);
+ dest.writeTypedObject(mActivityIntent, flags);
+ dest.writeStrongBinder(mActivityToken);
+ }
+
+ /** The change is related to the TaskFragment created with this unique token. */
+ public Change setTaskFragmentToken(@NonNull IBinder taskFragmentToken) {
+ mTaskFragmentToken = requireNonNull(taskFragmentToken);
+ return this;
+ }
+
+ /** Info of the embedded TaskFragment. */
+ public Change setTaskFragmentInfo(@NonNull TaskFragmentInfo info) {
+ mTaskFragmentInfo = requireNonNull(info);
+ return this;
+ }
+
+ /** Task id the parent Task. */
+ public Change setTaskId(int taskId) {
+ mTaskId = taskId;
+ return this;
+ }
+
+ /** Configuration of the parent Task. */
+ public Change setTaskConfiguration(@NonNull Configuration configuration) {
+ mTaskConfiguration = requireNonNull(configuration);
+ return this;
+ }
+
+ /**
+ * If the {@link #TYPE_TASK_FRAGMENT_ERROR} is from a {@link WindowContainerTransaction}
+ * from the {@link TaskFragmentOrganizer}, it may come with an error callback token to
+ * report back.
+ */
+ public Change setErrorCallbackToken(@Nullable IBinder errorCallbackToken) {
+ mErrorCallbackToken = errorCallbackToken;
+ return this;
+ }
+
+ /**
+ * Bundle with necessary info about the failure operation of
+ * {@link #TYPE_TASK_FRAGMENT_ERROR}.
+ */
+ public Change setErrorBundle(@NonNull Bundle errorBundle) {
+ mErrorBundle = requireNonNull(errorBundle);
+ return this;
+ }
+
+ /**
+ * Intent of the activity that is reparented to the Task for
+ * {@link #TYPE_ACTIVITY_REPARENT_TO_TASK}.
+ */
+ public Change setActivityIntent(@NonNull Intent intent) {
+ mActivityIntent = requireNonNull(intent);
+ return this;
+ }
+
+ /**
+ * Token of the reparent activity for {@link #TYPE_ACTIVITY_REPARENT_TO_TASK}.
+ * If the activity belongs to the same process as the organizer, this will be the actual
+ * activity token; if the activity belongs to a different process, the server will generate
+ * a temporary token that the organizer can use to reparent the activity through
+ * {@link WindowContainerTransaction} if needed.
+ */
+ public Change setActivityToken(@NonNull IBinder activityToken) {
+ mActivityToken = requireNonNull(activityToken);
+ return this;
+ }
+
+ @ChangeType
+ public int getType() {
+ return mType;
+ }
+
+ @Nullable
+ public IBinder getTaskFragmentToken() {
+ return mTaskFragmentToken;
+ }
+
+ @Nullable
+ public TaskFragmentInfo getTaskFragmentInfo() {
+ return mTaskFragmentInfo;
+ }
+
+ public int getTaskId() {
+ return mTaskId;
+ }
+
+ @Nullable
+ public Configuration getTaskConfiguration() {
+ return mTaskConfiguration;
+ }
+
+ @Nullable
+ public IBinder getErrorCallbackToken() {
+ return mErrorCallbackToken;
+ }
+
+ @Nullable
+ public Bundle getErrorBundle() {
+ return mErrorBundle;
+ }
+
+ @Nullable
+ public Intent getActivityIntent() {
+ return mActivityIntent;
+ }
+
+ @Nullable
+ public IBinder getActivityToken() {
+ return mActivityToken;
+ }
+
+ @Override
+ public String toString() {
+ return "Change{ type=" + mType + " }";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<Change> CREATOR = new Creator<>() {
+ @Override
+ public Change createFromParcel(Parcel in) {
+ return new Change(in);
+ }
+
+ @Override
+ public Change[] newArray(int size) {
+ return new Change[size];
+ }
+ };
+ }
+}
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index c056e26..f843318 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -17,6 +17,7 @@
package com.android.internal.app;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -67,12 +68,53 @@
ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
}
- public boolean showSessionForActiveService(Bundle args, int sourceFlags,
- IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
+ /**
+ * Shows the session for the currently active service. Used to start a new session from system
+ * affordances.
+ *
+ * @param args the bundle to pass as arguments to the voice interaction session
+ * @param sourceFlags flags indicating the source of this show
+ * @param showCallback optional callback to be notified when the session was shown
+ * @param activityToken optional token of activity that needs to be on top
+ *
+ * @deprecated Use {@link #showSessionForActiveService(Bundle, int, String,
+ * IVoiceInteractionSessionShowCallback, IBinder)} instead
+ */
+ @Deprecated
+ public boolean showSessionForActiveService(@NonNull Bundle args, int sourceFlags,
+ @Nullable IVoiceInteractionSessionShowCallback showCallback,
+ @Nullable IBinder activityToken) {
+ return showSessionForActiveServiceInternal(args, sourceFlags, /* attributionTag */ null,
+ showCallback, activityToken);
+ }
+
+ /**
+ * Shows the session for the currently active service. Used to start a new session from system
+ * affordances.
+ *
+ * @param args the bundle to pass as arguments to the voice interaction session
+ * @param sourceFlags flags indicating the source of this show
+ * @param attributionTag the attribution tag of the calling context or {@code null} for default
+ * attribution
+ * @param showCallback optional callback to be notified when the session was shown
+ * @param activityToken optional token of activity that needs to be on top
+ */
+ public boolean showSessionForActiveService(@NonNull Bundle args, int sourceFlags,
+ @Nullable String attributionTag,
+ @Nullable IVoiceInteractionSessionShowCallback showCallback,
+ @Nullable IBinder activityToken) {
+ return showSessionForActiveServiceInternal(args, sourceFlags, attributionTag, showCallback,
+ activityToken);
+ }
+
+ private boolean showSessionForActiveServiceInternal(@NonNull Bundle args, int sourceFlags,
+ @Nullable String attributionTag,
+ @Nullable IVoiceInteractionSessionShowCallback showCallback,
+ @Nullable IBinder activityToken) {
try {
if (mVoiceInteractionManagerService != null) {
return mVoiceInteractionManagerService.showSessionForActiveService(args,
- sourceFlags, showCallback, activityToken);
+ sourceFlags, attributionTag, showCallback, activityToken);
}
} catch (RemoteException e) {
Log.w(TAG, "Failed to call showSessionForActiveService", e);
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index ff1afdd..f6c64732 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -254,7 +254,7 @@
SystemUiDeviceConfigFlags.IS_NEARBY_SHARE_FIRST_TARGET_IN_RANKED_APP,
DEFAULT_IS_NEARBY_SHARE_FIRST_TARGET_IN_RANKED_APP);
- private static final int DEFAULT_LIST_VIEW_UPDATE_DELAY_MS = 125;
+ private static final int DEFAULT_LIST_VIEW_UPDATE_DELAY_MS = 0;
private static final int URI_PERMISSION_INTENT_FLAGS = Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 4b01357..83bf801 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -39,15 +39,16 @@
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
interface IVoiceInteractionManagerService {
- void showSession(in Bundle sessionArgs, int flags);
+ void showSession(in Bundle sessionArgs, int flags, String attributionTag);
boolean deliverNewSession(IBinder token, IVoiceInteractionSession session,
IVoiceInteractor interactor);
- boolean showSessionFromSession(IBinder token, in Bundle sessionArgs, int flags);
+ boolean showSessionFromSession(IBinder token, in Bundle sessionArgs, int flags,
+ String attributionTag);
boolean hideSessionFromSession(IBinder token);
int startVoiceActivity(IBinder token, in Intent intent, String resolvedType,
- String callingFeatureId);
+ String attributionTag);
int startAssistantActivity(IBinder token, in Intent intent, String resolvedType,
- String callingFeatureId);
+ String attributionTag);
void setKeepAwake(IBinder token, boolean keepAwake);
void closeSystemDialogs(IBinder token);
void finish(IBinder token);
@@ -125,12 +126,14 @@
*
* @param args the bundle to pass as arguments to the voice interaction session
* @param sourceFlags flags indicating the source of this show
+ * @param attributionTag the attribution tag of the calling context or {@code null} for default
+ * attribution
* @param showCallback optional callback to be notified when the session was shown
* @param activityToken optional token of activity that needs to be on top
* @RequiresPermission Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE
*/
@EnforcePermission("ACCESS_VOICE_INTERACTION_SERVICE")
- boolean showSessionForActiveService(in Bundle args, int sourceFlags,
+ boolean showSessionForActiveService(in Bundle args, int sourceFlags, String attributionTag,
IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken);
/**
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index b32afb4..66fff5c 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -951,7 +951,7 @@
protected void onPostExecute(Drawable d) {
if (getOtherProfile() == mDisplayResolveInfo) {
mResolverListCommunicator.updateProfileViewButton();
- } else {
+ } else if (!mDisplayResolveInfo.hasDisplayIcon()) {
mDisplayResolveInfo.setDisplayIcon(d);
mHolder.bindIcon(mDisplayResolveInfo);
// Notify in case view is already bound to resolve the race conditions on
diff --git a/core/java/com/android/internal/inputmethod/IInputMethod.aidl b/core/java/com/android/internal/inputmethod/IInputMethod.aidl
index 8bab5c3..5db2e84 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethod.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethod.aidl
@@ -74,6 +74,8 @@
void hideSoftInput(in IBinder hideInputToken, int flags, in ResultReceiver resultReceiver);
+ void updateEditorToolType(int toolType);
+
void changeInputMethodSubtype(in InputMethodSubtype subtype);
void canStartStylusHandwriting(int requestId);
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 8f10a5e..e625b31 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -55,7 +55,6 @@
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_DIALOG_OPEN;
-import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_DISAPPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_NOTIFICATION_ADD;
@@ -155,7 +154,6 @@
// Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
- public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK = 1;
public static final int CUJ_NOTIFICATION_SHADE_SCROLL_FLING = 2;
public static final int CUJ_NOTIFICATION_SHADE_ROW_EXPAND = 3;
public static final int CUJ_NOTIFICATION_SHADE_ROW_SWIPE = 4;
@@ -226,7 +224,7 @@
public static final int[] CUJ_TO_STATSD_INTERACTION_TYPE = {
// This should be mapping to CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE.
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__NOTIFICATION_SHADE_SWIPE,
- UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK,
+ NO_STATSD_LOGGING, // This is deprecated.
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_SCROLL_FLING,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_EXPAND,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_ROW_SWIPE,
@@ -310,7 +308,6 @@
/** @hide */
@IntDef({
CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
- CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK,
CUJ_NOTIFICATION_SHADE_SCROLL_FLING,
CUJ_NOTIFICATION_SHADE_ROW_EXPAND,
CUJ_NOTIFICATION_SHADE_ROW_SWIPE,
@@ -738,8 +735,6 @@
switch (cujType) {
case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE:
return "SHADE_EXPAND_COLLAPSE";
- case CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE_LOCK:
- return "SHADE_EXPAND_COLLAPSE_LOCK";
case CUJ_NOTIFICATION_SHADE_SCROLL_FLING:
return "SHADE_SCROLL_FLING";
case CUJ_NOTIFICATION_SHADE_ROW_EXPAND:
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 8e54746..b9243ec 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -219,8 +219,9 @@
throw new IllegalArgumentException("Bundle does not contain a hardware bitmap");
}
- HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER, android.hardware.HardwareBuffer.class);
- ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE, android.graphics.ParcelableColorSpace.class);
+ HardwareBuffer buffer = bundle.getParcelable(KEY_BUFFER, HardwareBuffer.class);
+ ParcelableColorSpace colorSpace = bundle.getParcelable(KEY_COLOR_SPACE,
+ ParcelableColorSpace.class);
return Bitmap.wrapHardwareBuffer(Objects.requireNonNull(buffer),
colorSpace.getColorSpace());
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 5f5886e..9471fae 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -55,7 +55,7 @@
InputMethodSubtype getLastInputMethodSubtype(int userId);
boolean showSoftInput(in IInputMethodClient client, @nullable IBinder windowToken, int flags,
- in @nullable ResultReceiver resultReceiver, int reason);
+ int lastClickToolType, in @nullable ResultReceiver resultReceiver, int reason);
boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken, int flags,
in @nullable ResultReceiver resultReceiver, int reason);
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 09ff4e0..9ee9b82 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -828,6 +828,8 @@
mSystemGestureExclusionListener, mContext.getDisplayId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Failed to unregister window manager callbacks", e);
}
}
diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml
index d686dd2..34b6a54 100644
--- a/core/res/res/values-sw600dp/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -51,7 +51,5 @@
<!-- If true, show multiuser switcher by default unless the user specifically disables it. -->
<bool name="config_showUserSwitcherByDefault">true</bool>
-
- <integer name="config_chooser_max_targets_per_row">6</integer>
</resources>
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
index 8587a35..25bf93f 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
@@ -58,7 +58,7 @@
public class StartProgramListUpdatesFanoutTest {
private static final String TAG = "BroadcastRadioTests.hal2.StartProgramListUpdatesFanout";
- private static final VerificationWithTimeout CB_TIMEOUT = timeout(100);
+ private static final VerificationWithTimeout CB_TIMEOUT = timeout(500);
// Mocks
@Mock IBroadcastRadio mBroadcastRadioMock;
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationTest.java b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
index 669138c..0d5cd72 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
@@ -157,6 +157,22 @@
assertFalse(config.isNightModeActive());
}
+ @Test
+ public void testSequenceNumberWrapAround() {
+ Configuration config = new Configuration();
+ Configuration otherConfig = new Configuration();
+
+ // Verify the other configuration is not newer when this sequence number warp around.
+ config.seq = 1000;
+ otherConfig.seq = Integer.MAX_VALUE - 1000;
+ assertFalse(config.isOtherSeqNewer(otherConfig));
+
+ // Verify the other configuration is newer when the other sequence number warp around.
+ config.seq = Integer.MAX_VALUE - 1000;
+ otherConfig.seq = 1000;
+ assertTrue(config.isOtherSeqNewer(otherConfig));
+ }
+
private void dumpDebug(File f, Configuration config) throws Exception {
final AtomicFile af = new AtomicFile(f);
FileOutputStream fos = af.startWrite();
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index c1e72fe..32c3a26 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -58,10 +58,10 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import libcore.io.Streams;
-
import com.google.android.collect.Sets;
+import libcore.io.Streams;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -477,6 +477,14 @@
new File(mTarget, "test (1).jpg").createNewFile();
assertNameEquals("test (2).jpg",
FileUtils.buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+
+ assertNameEquals("test.mp3", FileUtils.buildUniqueFile(mTarget, "audio/mp3", "test.mp3"));
+ new File(mTarget, "test.mp3").createNewFile();
+ assertNameEquals("test (1).mp3",
+ FileUtils.buildUniqueFile(mTarget, "audio/mp3", "test.mp3"));
+ new File(mTarget, "test (1).mp3").createNewFile();
+ assertNameEquals("test (2).mp3",
+ FileUtils.buildUniqueFile(mTarget, "audio/mp3", "test.mp3"));
}
@Test
diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
index b867e44..9637de8 100644
--- a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
@@ -42,6 +42,7 @@
import android.text.style.MaskFilterSpan;
import android.text.style.UnderlineSpan;
import android.util.StringBuilderPrinter;
+import android.view.MotionEvent;
import android.view.autofill.AutofillId;
import androidx.test.filters.SmallTest;
@@ -490,7 +491,8 @@
assertThat(sb.toString()).isEqualTo(
"prefix: inputType=0x0 imeOptions=0x0 privateImeOptions=null\n"
+ "prefix: actionLabel=null actionId=0\n"
- + "prefix: initialSelStart=-1 initialSelEnd=-1 initialCapsMode=0x0\n"
+ + "prefix: initialSelStart=-1 initialSelEnd=-1 initialToolType=0"
+ + " initialCapsMode=0x0\n"
+ "prefix: hintText=null label=null\n"
+ "prefix: packageName=null autofillId=null fieldId=0 fieldName=null\n"
+ "prefix: extras=null\n"
@@ -509,6 +511,7 @@
info.initialCapsMode = TextUtils.CAP_MODE_CHARACTERS; // 0x1000
info.hintText = "testHintText";
info.label = "testLabel";
+ info.setInitialToolType(MotionEvent.TOOL_TYPE_STYLUS);
info.packageName = "android.view.inputmethod";
info.autofillId = new AutofillId(123);
info.fieldId = 456;
@@ -523,7 +526,8 @@
assertThat(sb.toString()).isEqualTo(
"prefix2: inputType=0x1 imeOptions=0x2 privateImeOptions=testOptions\n"
+ "prefix2: actionLabel=null actionId=0\n"
- + "prefix2: initialSelStart=0 initialSelEnd=1 initialCapsMode=0x1000\n"
+ + "prefix2: initialSelStart=0 initialSelEnd=1 initialToolType=2"
+ + " initialCapsMode=0x1000\n"
+ "prefix2: hintText=testHintText label=testLabel\n"
+ "prefix2: packageName=android.view.inputmethod autofillId=123"
+ " fieldId=456 fieldName=testField\n"
@@ -543,7 +547,8 @@
assertThat(sb.toString()).isEqualTo(
"prefix: inputType=0x0 imeOptions=0x0 privateImeOptions=null\n"
+ "prefix: actionLabel=null actionId=0\n"
- + "prefix: initialSelStart=-1 initialSelEnd=-1 initialCapsMode=0x0\n"
+ + "prefix: initialSelStart=-1 initialSelEnd=-1 initialToolType=0"
+ + " initialCapsMode=0x0\n"
+ "prefix: hintText=null label=null\n"
+ "prefix: packageName=null autofillId=null fieldId=0 fieldName=null\n"
+ "prefix: hintLocales=null\n"
diff --git a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
deleted file mode 100644
index e8793a9..0000000
--- a/core/tests/coretests/src/com/android/internal/util/ArrayUtilsTest.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.internal.util;
-
-import androidx.test.filters.SmallTest;
-
-import junit.framework.TestCase;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-
-public class ArrayUtilsTest extends TestCase {
-
- @SmallTest
- public void testUnstableRemoveIf() throws Exception {
- java.util.function.Predicate<Object> isNull = new java.util.function.Predicate<Object>() {
- @Override
- public boolean test(Object o) {
- return o == null;
- }
- };
-
- final Object a = new Object();
- final Object b = new Object();
- final Object c = new Object();
-
- ArrayList<Object> collection = null;
- assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
-
- collection = new ArrayList<>();
- assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
-
- collection = new ArrayList<>(Collections.singletonList(a));
- assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(1, collection.size());
- assertTrue(collection.contains(a));
-
- collection = new ArrayList<>(Collections.singletonList(null));
- assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(0, collection.size());
-
- collection = new ArrayList<>(Arrays.asList(a, b));
- assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(2, collection.size());
- assertTrue(collection.contains(a));
- assertTrue(collection.contains(b));
-
- collection = new ArrayList<>(Arrays.asList(a, null));
- assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(1, collection.size());
- assertTrue(collection.contains(a));
-
- collection = new ArrayList<>(Arrays.asList(null, a));
- assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(1, collection.size());
- assertTrue(collection.contains(a));
-
- collection = new ArrayList<>(Arrays.asList(null, null));
- assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(0, collection.size());
-
- collection = new ArrayList<>(Arrays.asList(a, b, c));
- assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(3, collection.size());
- assertTrue(collection.contains(a));
- assertTrue(collection.contains(b));
- assertTrue(collection.contains(c));
-
- collection = new ArrayList<>(Arrays.asList(a, b, null));
- assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(2, collection.size());
- assertTrue(collection.contains(a));
- assertTrue(collection.contains(b));
-
- collection = new ArrayList<>(Arrays.asList(a, null, b));
- assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(2, collection.size());
- assertTrue(collection.contains(a));
- assertTrue(collection.contains(b));
-
- collection = new ArrayList<>(Arrays.asList(null, a, b));
- assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(2, collection.size());
- assertTrue(collection.contains(a));
- assertTrue(collection.contains(b));
-
- collection = new ArrayList<>(Arrays.asList(a, null, null));
- assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(1, collection.size());
- assertTrue(collection.contains(a));
-
- collection = new ArrayList<>(Arrays.asList(null, null, a));
- assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(1, collection.size());
- assertTrue(collection.contains(a));
-
- collection = new ArrayList<>(Arrays.asList(null, a, null));
- assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(1, collection.size());
- assertTrue(collection.contains(a));
-
- collection = new ArrayList<>(Arrays.asList(null, null, null));
- assertEquals(3, ArrayUtils.unstableRemoveIf(collection, isNull));
- assertEquals(0, collection.size());
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_passesWhenRangeInsideArray() {
- ArrayUtils.throwsIfOutOfBounds(10, 2, 6);
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_passesWhenRangeIsWholeArray() {
- ArrayUtils.throwsIfOutOfBounds(10, 0, 10);
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_passesWhenEmptyRangeAtStart() {
- ArrayUtils.throwsIfOutOfBounds(10, 0, 0);
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_passesWhenEmptyRangeAtEnd() {
- ArrayUtils.throwsIfOutOfBounds(10, 10, 0);
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_passesWhenEmptyArray() {
- ArrayUtils.throwsIfOutOfBounds(0, 0, 0);
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_failsWhenRangeStartNegative() {
- try {
- ArrayUtils.throwsIfOutOfBounds(10, -1, 5);
- fail();
- } catch (ArrayIndexOutOfBoundsException expected) {
- // expected
- }
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_failsWhenCountNegative() {
- try {
- ArrayUtils.throwsIfOutOfBounds(10, 5, -1);
- fail();
- } catch (ArrayIndexOutOfBoundsException expected) {
- // expected
- }
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_failsWhenRangeStartTooHigh() {
- try {
- ArrayUtils.throwsIfOutOfBounds(10, 11, 0);
- fail();
- } catch (ArrayIndexOutOfBoundsException expected) {
- // expected
- }
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_failsWhenRangeEndTooHigh() {
- try {
- ArrayUtils.throwsIfOutOfBounds(10, 5, 6);
- fail();
- } catch (ArrayIndexOutOfBoundsException expected) {
- // expected
- }
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_failsWhenLengthNegative() {
- try {
- ArrayUtils.throwsIfOutOfBounds(-1, 0, 0);
- fail();
- } catch (ArrayIndexOutOfBoundsException expected) {
- // expected
- }
- }
-
- @SmallTest
- public void testThrowsIfOutOfBounds_failsWhenOverflowRangeEndTooHigh() {
- try {
- ArrayUtils.throwsIfOutOfBounds(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
- fail();
- } catch (ArrayIndexOutOfBoundsException expected) {
- // expected
- }
- }
-}
diff --git a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
index c66a743..72f3af6 100644
--- a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -18,6 +18,12 @@
import static org.junit.Assert.assertArrayEquals;
+import androidx.test.filters.SmallTest;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
import junit.framework.TestCase;
/**
@@ -257,4 +263,182 @@
assertArrayEquals(expectation, ArrayUtils.concat(array1, array2, array3));
}
+
+ @SmallTest
+ public void testUnstableRemoveIf() throws Exception {
+ java.util.function.Predicate<Object> isNull = new java.util.function.Predicate<Object>() {
+ @Override
+ public boolean test(Object o) {
+ return o == null;
+ }
+ };
+
+ final Object a = new Object();
+ final Object b = new Object();
+ final Object c = new Object();
+
+ ArrayList<Object> collection = null;
+ assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+
+ collection = new ArrayList<>();
+ assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+
+ collection = new ArrayList<>(Collections.singletonList(a));
+ assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Collections.singletonList(null));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(0, collection.size());
+
+ collection = new ArrayList<>(Arrays.asList(a, b));
+ assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(2, collection.size());
+ assertTrue(collection.contains(a));
+ assertTrue(collection.contains(b));
+
+ collection = new ArrayList<>(Arrays.asList(a, null));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Arrays.asList(null, a));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Arrays.asList(null, null));
+ assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(0, collection.size());
+
+ collection = new ArrayList<>(Arrays.asList(a, b, c));
+ assertEquals(0, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(3, collection.size());
+ assertTrue(collection.contains(a));
+ assertTrue(collection.contains(b));
+ assertTrue(collection.contains(c));
+
+ collection = new ArrayList<>(Arrays.asList(a, b, null));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(2, collection.size());
+ assertTrue(collection.contains(a));
+ assertTrue(collection.contains(b));
+
+ collection = new ArrayList<>(Arrays.asList(a, null, b));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(2, collection.size());
+ assertTrue(collection.contains(a));
+ assertTrue(collection.contains(b));
+
+ collection = new ArrayList<>(Arrays.asList(null, a, b));
+ assertEquals(1, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(2, collection.size());
+ assertTrue(collection.contains(a));
+ assertTrue(collection.contains(b));
+
+ collection = new ArrayList<>(Arrays.asList(a, null, null));
+ assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Arrays.asList(null, null, a));
+ assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Arrays.asList(null, a, null));
+ assertEquals(2, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(1, collection.size());
+ assertTrue(collection.contains(a));
+
+ collection = new ArrayList<>(Arrays.asList(null, null, null));
+ assertEquals(3, ArrayUtils.unstableRemoveIf(collection, isNull));
+ assertEquals(0, collection.size());
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_passesWhenRangeInsideArray() {
+ ArrayUtils.throwsIfOutOfBounds(10, 2, 6);
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_passesWhenRangeIsWholeArray() {
+ ArrayUtils.throwsIfOutOfBounds(10, 0, 10);
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_passesWhenEmptyRangeAtStart() {
+ ArrayUtils.throwsIfOutOfBounds(10, 0, 0);
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_passesWhenEmptyRangeAtEnd() {
+ ArrayUtils.throwsIfOutOfBounds(10, 10, 0);
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_passesWhenEmptyArray() {
+ ArrayUtils.throwsIfOutOfBounds(0, 0, 0);
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenRangeStartNegative() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(10, -1, 5);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenCountNegative() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(10, 5, -1);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenRangeStartTooHigh() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(10, 11, 0);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenRangeEndTooHigh() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(10, 5, 6);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenLengthNegative() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(-1, 0, 0);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
+
+ @SmallTest
+ public void testThrowsIfOutOfBounds_failsWhenOverflowRangeEndTooHigh() {
+ try {
+ ArrayUtils.throwsIfOutOfBounds(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
+ fail();
+ } catch (ArrayIndexOutOfBoundsException expected) {
+ // expected
+ }
+ }
}
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index ae1c9461..505d2a3 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1075,6 +1075,12 @@
"group": "WM_DEBUG_ORIENTATION",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1075136930": {
+ "message": "startLockTaskMode: Can't lock due to auth",
+ "level": "WARN",
+ "group": "WM_DEBUG_LOCKTASK",
+ "at": "com\/android\/server\/wm\/LockTaskController.java"
+ },
"-1069336896": {
"message": "onRootTaskOrderChanged(): rootTask=%s",
"level": "DEBUG",
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index f4f5e1e..8ae0d3d 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -1244,14 +1244,16 @@
nativePtrs[i++] = entry.getValue().native_instance;
writeString(namesBytes, entry.getKey());
}
- int typefacesBytesCount = nativeWriteTypefaces(null, nativePtrs);
// int (typefacesBytesCount), typefaces, namesBytes
+ final int typefaceBytesCountSize = Integer.BYTES;
+ int typefacesBytesCount = nativeWriteTypefaces(null, typefaceBytesCountSize, nativePtrs);
SharedMemory sharedMemory = SharedMemory.create(
- "fontMap", Integer.BYTES + typefacesBytesCount + namesBytes.size());
+ "fontMap", typefaceBytesCountSize + typefacesBytesCount + namesBytes.size());
ByteBuffer writableBuffer = sharedMemory.mapReadWrite().order(ByteOrder.BIG_ENDIAN);
try {
writableBuffer.putInt(typefacesBytesCount);
- int writtenBytesCount = nativeWriteTypefaces(writableBuffer.slice(), nativePtrs);
+ int writtenBytesCount =
+ nativeWriteTypefaces(writableBuffer, writableBuffer.position(), nativePtrs);
if (writtenBytesCount != typefacesBytesCount) {
throw new IOException(String.format("Unexpected bytes written: %d, expected: %d",
writtenBytesCount, typefacesBytesCount));
@@ -1276,7 +1278,9 @@
@NonNull ByteBuffer buffer, @NonNull Map<String, Typeface> out)
throws IOException {
int typefacesBytesCount = buffer.getInt();
- long[] nativePtrs = nativeReadTypefaces(buffer.slice());
+ // Note: Do not call buffer.slice(), as nativeReadTypefaces() expects
+ // that buffer.address() is page-aligned.
+ long[] nativePtrs = nativeReadTypefaces(buffer, buffer.position());
if (nativePtrs == null) {
throw new IOException("Could not read typefaces");
}
@@ -1598,9 +1602,10 @@
private static native void nativeRegisterGenericFamily(String str, long nativePtr);
private static native int nativeWriteTypefaces(
- @Nullable ByteBuffer buffer, @NonNull long[] nativePtrs);
+ @Nullable ByteBuffer buffer, int position, @NonNull long[] nativePtrs);
- private static native @Nullable long[] nativeReadTypefaces(@NonNull ByteBuffer buffer);
+ private static native
+ @Nullable long[] nativeReadTypefaces(@NonNull ByteBuffer buffer, int position);
private static native void nativeForceSetStaticFinalField(String fieldName, Typeface typeface);
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 6939a72..47de37d 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -359,10 +359,10 @@
/** @hide */
@TestApi
public Region getSafeZone() {
- mMaskMatrix.reset();
+ Path mask = getIconMask();
mMaskMatrix.setScale(SAFEZONE_SCALE, SAFEZONE_SCALE, getBounds().centerX(), getBounds().centerY());
Path p = new Path();
- mMask.transform(mMaskMatrix, p);
+ mask.transform(mMaskMatrix, p);
Region safezoneRegion = new Region(getBounds());
safezoneRegion.setPath(p, safezoneRegion);
return safezoneRegion;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 420d606..586e3a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -495,14 +495,15 @@
mPipBoundsState.getBounds(),
mPipBoundsState.getAspectRatio());
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
- mPipTaskOrganizer.scheduleAnimateResizePip(destinationBounds,
- mEnterAnimationDuration,
- null /* updateBoundsCallback */);
-
- mTouchHandler.onAspectRatioChanged();
- updateMovementBounds(null /* toBounds */, false /* fromRotation */,
- false /* fromImeAdjustment */, false /* fromShelfAdjustment */,
- null /* windowContainerTransaction */);
+ if (!destinationBounds.equals(mPipBoundsState.getBounds())) {
+ mPipTaskOrganizer.scheduleAnimateResizePip(destinationBounds,
+ mEnterAnimationDuration,
+ null /* updateBoundsCallback */);
+ mTouchHandler.onAspectRatioChanged();
+ updateMovementBounds(null /* toBounds */, false /* fromRotation */,
+ false /* fromImeAdjustment */, false /* fromShelfAdjustment */,
+ null /* windowContainerTransaction */);
+ }
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
index 3a6ce81..b212ea9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsController.java
@@ -122,9 +122,9 @@
cancelScheduledPlacement();
applyPlacementBounds(placement.getUnstashedBounds(), animationDuration);
} else if (immediate) {
+ boolean shouldStash = mUnstashRunnable != null || placement.getTriggerStash();
cancelScheduledPlacement();
- applyPlacementBounds(placement.getBounds(), animationDuration);
- scheduleUnstashIfNeeded(placement);
+ applyPlacement(placement, shouldStash, animationDuration);
} else {
applyPlacementBounds(mCurrentPlacementBounds, animationDuration);
schedulePinnedStackPlacement(placement, animationDuration);
@@ -176,22 +176,21 @@
"%s: applyPendingPlacement()", TAG);
}
if (mPendingPlacement != null) {
- if (mPendingStash) {
- mPendingStash = false;
- scheduleUnstashIfNeeded(mPendingPlacement);
- }
+ applyPlacement(mPendingPlacement, mPendingStash, mPendingPlacementAnimationDuration);
+ mPendingStash = false;
+ mPendingPlacement = null;
+ }
+ }
- if (mUnstashRunnable != null) {
- // currently stashed, use stashed pos
- applyPlacementBounds(mPendingPlacement.getBounds(),
- mPendingPlacementAnimationDuration);
- } else {
- applyPlacementBounds(mPendingPlacement.getUnstashedBounds(),
- mPendingPlacementAnimationDuration);
- }
+ private void applyPlacement(@NonNull final Placement placement, boolean shouldStash,
+ int animationDuration) {
+ if (placement.getStashType() != STASH_TYPE_NONE && shouldStash) {
+ scheduleUnstashIfNeeded(placement);
}
- mPendingPlacement = null;
+ Rect bounds =
+ mUnstashRunnable != null ? placement.getBounds() : placement.getUnstashedBounds();
+ applyPlacementBounds(bounds, animationDuration);
}
void onPipDismissed() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 1be17f9..de7e7bd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -21,7 +21,6 @@
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -86,7 +85,6 @@
import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.LegacyTransitions;
import com.android.wm.shell.transition.Transitions;
import java.io.PrintWriter;
@@ -411,62 +409,13 @@
}
if (!ENABLE_SHELL_TRANSITIONS) {
- startIntentLegacy(intent, fillInIntent, position, options);
+ mStageCoordinator.startIntentLegacy(intent, fillInIntent, position, options);
return;
}
mStageCoordinator.startIntent(intent, fillInIntent, position, options);
}
- private void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
- @SplitPosition int position, @Nullable Bundle options) {
- final WindowContainerTransaction evictWct = new WindowContainerTransaction();
- mStageCoordinator.prepareEvictChildTasks(position, evictWct);
-
- LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
- @Override
- public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
- IRemoteAnimationFinishedCallback finishedCallback,
- SurfaceControl.Transaction t) {
- if (apps == null || apps.length == 0) {
- // Switch the split position if launching as MULTIPLE_TASK failed.
- if ((fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
- setSideStagePosition(SplitLayout.reversePosition(
- mStageCoordinator.getSideStagePosition()));
- }
-
- // Do nothing when the animation was cancelled.
- t.apply();
- return;
- }
-
- for (int i = 0; i < apps.length; ++i) {
- if (apps[i].mode == MODE_OPENING) {
- t.show(apps[i].leash);
- }
- }
- t.apply();
-
- if (finishedCallback != null) {
- try {
- finishedCallback.onAnimationFinished();
- } catch (RemoteException e) {
- Slog.e(TAG, "Error finishing legacy transition: ", e);
- }
- }
-
- mSyncQueue.queue(evictWct);
- }
- };
-
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
-
- wct.sendPendingIntent(intent, fillInIntent, options);
- mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
- }
-
/** Returns {@code true} if it's launching the same component on both sides of the split. */
@VisibleForTesting
boolean isLaunchingAdjacently(@Nullable Intent startIntent,
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 2b3b61b..f2340d5 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
@@ -26,6 +26,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -118,6 +119,7 @@
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
+import com.android.wm.shell.transition.LegacyTransitions;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.util.SplitBounds;
@@ -195,7 +197,6 @@
private boolean mExitSplitScreenOnHide;
private boolean mIsDividerRemoteAnimating;
private boolean mIsExiting;
- private boolean mResizingSplits;
/** The target stage to dismiss to when unlock after folded. */
@StageType
@@ -210,7 +211,11 @@
@Override
public void onLeashReady(SurfaceControl leash) {
- mSyncQueue.runInSync(t -> applyDividerVisibility(t));
+ // This is for avoiding divider invisible due to delay of creating so only need
+ // to do when divider should visible case.
+ if (mDividerVisible) {
+ mSyncQueue.runInSync(t -> applyDividerVisibility(t));
+ }
}
};
@@ -433,6 +438,63 @@
});
}
+ /** Launches an activity into split by legacy transition. */
+ void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
+ @SplitPosition int position, @androidx.annotation.Nullable Bundle options) {
+ final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ prepareEvictChildTasks(position, evictWct);
+
+ LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
+ @Override
+ public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback,
+ SurfaceControl.Transaction t) {
+ if (apps == null || apps.length == 0) {
+ // Switch the split position if launching as MULTIPLE_TASK failed.
+ if ((fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
+ setSideStagePosition(SplitLayout.reversePosition(
+ getSideStagePosition()), null);
+ }
+
+ // Do nothing when the animation was cancelled.
+ t.apply();
+ return;
+ }
+
+ for (int i = 0; i < apps.length; ++i) {
+ if (apps[i].mode == MODE_OPENING) {
+ t.show(apps[i].leash);
+ }
+ }
+ t.apply();
+
+ if (finishedCallback != null) {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error finishing legacy transition: ", e);
+ }
+ }
+
+ mSyncQueue.queue(evictWct);
+ }
+ };
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
+
+ // If split still not active, apply windows bounds first to avoid surface reset to
+ // wrong pos by SurfaceAnimator from wms.
+ // TODO(b/223325631): check is it still necessary after improve enter transition done.
+ if (!mMainStage.isActive()) {
+ updateWindowBounds(mSplitLayout, wct);
+ }
+
+ wct.sendPendingIntent(intent, fillInIntent, options);
+ mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
+ }
+
/** Starts 2 tasks in one transition. */
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
@Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio,
@@ -849,6 +911,7 @@
.setWindowCrop(mSideStage.mRootLeash, null);
t.setPosition(mMainStage.mRootLeash, 0, 0)
.setPosition(mSideStage.mRootLeash, 0, 0);
+ t.hide(mMainStage.mDimLayer).hide(mSideStage.mDimLayer);
setDividerVisibility(false, t);
// In this case, exit still under progress, fade out the split decor after first WCT
@@ -858,7 +921,7 @@
WindowContainerTransaction finishedWCT = new WindowContainerTransaction();
mIsExiting = false;
childrenToTop.dismiss(finishedWCT, true /* toTop */);
- wct.reorder(mRootTaskInfo.token, false /* toTop */);
+ finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */);
mTaskOrganizer.applyTransaction(finishedWCT);
onTransitionAnimationComplete();
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index afc70a44..08eb2c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -154,8 +154,6 @@
private final int mCurrentUserId;
- private ScreenRotationAnimation mRotationAnimation;
-
private Drawable mEnterpriseThumbnailDrawable;
private BroadcastReceiver mEnterpriseResourceUpdatedReceiver = new BroadcastReceiver() {
@@ -340,12 +338,6 @@
final Runnable onAnimFinish = () -> {
if (!animations.isEmpty()) return;
-
- if (mRotationAnimation != null) {
- mRotationAnimation.kill();
- mRotationAnimation = null;
- }
-
mAnimations.remove(transition);
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
};
@@ -365,11 +357,8 @@
isSeamlessDisplayChange = isRotationSeamless(info, mDisplayController);
final int anim = getRotationAnimation(info);
if (!(isSeamlessDisplayChange || anim == ROTATION_ANIMATION_JUMPCUT)) {
- mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
- mTransactionPool, startTransaction, change, info.getRootLeash(),
- anim);
- mRotationAnimation.startAnimation(animations, onAnimFinish,
- mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
+ startRotationAnimation(startTransaction, change, info, anim, animations,
+ onAnimFinish);
continue;
}
} else {
@@ -413,6 +402,13 @@
startTransaction.setWindowCrop(change.getLeash(),
change.getEndAbsBounds().width(), change.getEndAbsBounds().height());
}
+ // Rotation change of independent non display window container.
+ if (change.getParent() == null
+ && change.getStartRotation() != change.getEndRotation()) {
+ startRotationAnimation(startTransaction, change, info,
+ ROTATION_ANIMATION_ROTATE, animations, onAnimFinish);
+ continue;
+ }
}
// Don't animate anything that isn't independent.
@@ -543,6 +539,31 @@
}
}
+ private void startRotationAnimation(SurfaceControl.Transaction startTransaction,
+ TransitionInfo.Change change, TransitionInfo info, int animHint,
+ ArrayList<Animator> animations, Runnable onAnimFinish) {
+ final ScreenRotationAnimation anim = new ScreenRotationAnimation(mContext, mSurfaceSession,
+ mTransactionPool, startTransaction, change, info.getRootLeash(), animHint);
+ // The rotation animation may consist of 3 animations: fade-out screenshot, fade-in real
+ // content, and background color. The item of "animGroup" will be removed if the sub
+ // animation is finished. Then if the list becomes empty, the rotation animation is done.
+ final ArrayList<Animator> animGroup = new ArrayList<>(3);
+ final ArrayList<Animator> animGroupStore = new ArrayList<>(3);
+ final Runnable finishCallback = () -> {
+ if (!animGroup.isEmpty()) return;
+ anim.kill();
+ animations.removeAll(animGroupStore);
+ onAnimFinish.run();
+ };
+ anim.startAnimation(animGroup, finishCallback, mTransitionAnimationScaleSetting,
+ mMainExecutor, mAnimExecutor);
+ for (int i = animGroup.size() - 1; i >= 0; i--) {
+ final Animator animator = animGroup.get(i);
+ animGroupStore.add(animator);
+ animations.add(animator);
+ }
+ }
+
private void edgeExtendWindow(TransitionInfo.Change change,
Animation a, SurfaceControl.Transaction startTransaction,
SurfaceControl.Transaction finishTransaction) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index 1005ef1..a843b2a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -84,7 +84,7 @@
private final Context mContext;
private final TransactionPool mTransactionPool;
private final float[] mTmpFloats = new float[9];
- /** The leash of display. */
+ /** The leash of the changing window container. */
private final SurfaceControl mSurfaceControl;
private final Rect mStartBounds = new Rect();
private final Rect mEndBounds = new Rect();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index e6006c4..cd29741 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -356,7 +356,7 @@
// Put all the OPEN/SHOW on top
if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
// Wallpaper is always at the bottom.
- layer = 0;
+ layer = -zSplitLine;
} else if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
if (isOpening) {
// put on top
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
index 6c9ea7b..4877442 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
@@ -21,6 +21,7 @@
import android.os.SystemClock
import android.view.InputDevice
import android.view.MotionEvent
+import android.view.ViewConfiguration
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.UiDevice
@@ -43,12 +44,15 @@
const val TIMEOUT_MS = 3_000L
const val DRAG_DURATION_MS = 1_000L
const val NOTIFICATION_SCROLLER = "notification_stack_scroller"
+ const val DIVIDER_BAR = "docked_divider_handle"
const val GESTURE_STEP_MS = 16L
private val notificationScrollerSelector: BySelector
get() = By.res(SYSTEM_UI_PACKAGE_NAME, NOTIFICATION_SCROLLER)
private val notificationContentSelector: BySelector
get() = By.text("Notification content")
+ private val dividerBarSelector: BySelector
+ get() = By.res(SYSTEM_UI_PACKAGE_NAME, DIVIDER_BAR)
fun getPrimary(instrumentation: Instrumentation): SplitScreenHelper =
SplitScreenHelper(
@@ -79,16 +83,16 @@
)
fun waitForSplitComplete(
- wmHelper: WindowManagerStateHelper,
- primaryApp: IComponentMatcher,
- secondaryApp: IComponentMatcher,
+ wmHelper: WindowManagerStateHelper,
+ primaryApp: IComponentMatcher,
+ secondaryApp: IComponentMatcher,
) {
wmHelper.StateSyncBuilder()
- .withAppTransitionIdle()
- .withWindowSurfaceAppeared(primaryApp)
- .withWindowSurfaceAppeared(secondaryApp)
- .withSplitDividerVisible()
- .waitForAndVerify()
+ .withAppTransitionIdle()
+ .withWindowSurfaceAppeared(primaryApp)
+ .withWindowSurfaceAppeared(secondaryApp)
+ .withSplitDividerVisible()
+ .waitForAndVerify()
}
fun dragFromNotificationToSplit(
@@ -215,5 +219,26 @@
allApps.unfreeze()
}
}
+
+ fun dragDividerToDismissSplit(
+ device: UiDevice,
+ wmHelper: WindowManagerStateHelper
+ ) {
+ val displayBounds = wmHelper.currentState.layerState
+ .displays.firstOrNull { !it.isVirtual }
+ ?.layerStackSpace
+ ?: error("Display not found")
+ val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
+ dividerBar.drag(Point(displayBounds.width * 4 / 5, displayBounds.height * 4 / 5))
+ }
+
+ fun doubleTapDividerToSwitch(device: UiDevice) {
+ val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
+ val interval = (ViewConfiguration.getDoubleTapTimeout() +
+ ViewConfiguration.getDoubleTapMinTime()) / 2
+ dividerBar.click()
+ SystemClock.sleep(interval.toLong())
+ dividerBar.click()
+ }
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
index 77dcbe0..b71a9d8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt
@@ -69,7 +69,7 @@
*/
@Presubmit
@Test
- fun focusDoesNotChange() {
+ fun focusChanges() {
testSpec.assertEventLog {
this.focusChanges("PipMenuView", "NexusLauncherActivity")
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
new file mode 100644
index 0000000..cd92db7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.WindowUtils
+import com.android.wm.shell.flicker.appWindowBecomesInvisible
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerBecomesInvisible
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesInvisible
+import com.android.wm.shell.flicker.splitScreenDividerBecomesInvisible
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test dismiss split screen by dragging the divider bar.
+ *
+ * To run this test: `atest WMShellFlickerTests:DismissSplitScreenByDivider`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class DismissSplitScreenByDivider (testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) {
+
+ // TODO(b/231399940): Remove this once we can use recent shortcut to enter split.
+ @Before
+ open fun before() {
+ Assume.assumeTrue(tapl.isTablet)
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ eachRun {
+ tapl.goHome()
+ primaryApp.launchViaIntent(wmHelper)
+ // TODO(b/231399940): Use recent shortcut to enter split.
+ tapl.launchedAppState.taskbar
+ .openAllApps()
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+ transitions {
+ SplitScreenHelper.dragDividerToDismissSplit(device, wmHelper)
+ wmHelper.StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withFullScreenApp(secondaryApp)
+ .waitForAndVerify()
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerBecomesInvisible() = testSpec.splitScreenDividerBecomesInvisible()
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerBecomesInvisible() = testSpec.layerBecomesInvisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsBecomesInvisible() = testSpec.splitAppLayerBoundsBecomesInvisible(
+ primaryApp, splitLeftTop = false)
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsIsFullscreenAtEnd() {
+ testSpec.assertLayers {
+ this.isVisible(secondaryApp)
+ .then()
+ .isInvisible(secondaryApp)
+ .then()
+ .isVisible(secondaryApp)
+ .invoke("secondaryAppBoundsIsFullscreenAtEnd") {
+ val displayBounds = WindowUtils.getDisplayBounds(testSpec.endRotation)
+ it.visibleRegion(secondaryApp).coversExactly(displayBounds)
+ }
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowBecomesInvisible() = testSpec.appWindowBecomesInvisible(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(secondaryApp)
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun entireScreenCovered() =
+ super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() =
+ super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() =
+ super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() =
+ super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() =
+ super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() =
+ super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() =
+ super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() =
+ super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes =
+ listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index 4f48ff0..127ac1e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -66,24 +66,24 @@
primaryApp.launchViaIntent(wmHelper)
// TODO(b/231399940): Use recent shortcut to enter split.
tapl.launchedAppState.taskbar
- .openAllApps()
- .getAppIcon(secondaryApp.appName)
- .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ .openAllApps()
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
}
transitions {
tapl.goHome()
wmHelper.StateSyncBuilder()
- .withAppTransitionIdle()
- .withHomeActivityVisible()
- .waitForAndVerify()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
}
}
@Presubmit
@Test
- fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesInvisible()
+ fun splitScreenDividerBecomesInvisible() = testSpec.splitScreenDividerBecomesInvisible()
@Presubmit
@Test
@@ -115,67 +115,67 @@
@Postsubmit
@Test
override fun entireScreenCovered() =
- super.entireScreenCovered()
+ super.entireScreenCovered()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun navBarLayerIsVisibleAtStartAndEnd() =
- super.navBarLayerIsVisibleAtStartAndEnd()
+ super.navBarLayerIsVisibleAtStartAndEnd()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun navBarLayerPositionAtStartAndEnd() =
- super.navBarLayerPositionAtStartAndEnd()
+ super.navBarLayerPositionAtStartAndEnd()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun navBarWindowIsAlwaysVisible() =
- super.navBarWindowIsAlwaysVisible()
+ super.navBarWindowIsAlwaysVisible()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun statusBarLayerIsVisibleAtStartAndEnd() =
- super.statusBarLayerIsVisibleAtStartAndEnd()
+ super.statusBarLayerIsVisibleAtStartAndEnd()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun statusBarLayerPositionAtStartAndEnd() =
- super.statusBarLayerPositionAtStartAndEnd()
+ super.statusBarLayerPositionAtStartAndEnd()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun statusBarWindowIsAlwaysVisible() =
- super.statusBarWindowIsAlwaysVisible()
+ super.statusBarWindowIsAlwaysVisible()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun taskBarLayerIsVisibleAtStartAndEnd() =
- super.taskBarLayerIsVisibleAtStartAndEnd()
+ super.taskBarLayerIsVisibleAtStartAndEnd()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun taskBarWindowIsAlwaysVisible() =
- super.taskBarWindowIsAlwaysVisible()
+ super.taskBarWindowIsAlwaysVisible()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
/** {@inheritDoc} */
@Postsubmit
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
new file mode 100644
index 0000000..38279a3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test double tap the divider bar to switch the two apps.
+ *
+ * To run this test: `atest WMShellFlickerTests:SwitchAppByDoubleTapDivider`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class SwitchAppByDoubleTapDivider (testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) {
+
+ // TODO(b/231399940): Remove this once we can use recent shortcut to enter split.
+ @Before
+ open fun before() {
+ Assume.assumeTrue(tapl.isTablet)
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ eachRun {
+ primaryApp.launchViaIntent(wmHelper)
+ // TODO(b/231399940): Use recent shortcut to enter split.
+ tapl.launchedAppState.taskbar
+ .openAllApps()
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+ transitions {
+ SplitScreenHelper.doubleTapDividerToSwitch(device)
+ wmHelper.StateSyncBuilder()
+ .withAppTransitionIdle()
+ .waitForAndVerify()
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerKeepVisible() {
+ testSpec.assertLayers {
+ this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp, splitLeftTop = true)
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+ secondaryApp, splitLeftTop = false)
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(secondaryApp)
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun entireScreenCovered() =
+ super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() =
+ super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() =
+ super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() =
+ super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() =
+ super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() =
+ super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() =
+ super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() =
+ super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes =
+ listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
new file mode 100644
index 0000000..c48f3f7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.splitscreen
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.Presubmit
+import android.view.WindowManagerPolicyConstants
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.wm.shell.flicker.appWindowBecomesVisible
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.layerBecomesVisible
+import com.android.wm.shell.flicker.layerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
+import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import org.junit.Assume
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test switch back to split pair after go home
+ *
+ * To run this test: `atest WMShellFlickerTests:SwitchBackToSplitFromHome`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) {
+
+ // TODO(b/231399940): Remove this once we can use recent shortcut to enter split.
+ @Before
+ open fun before() {
+ Assume.assumeTrue(tapl.isTablet)
+ }
+
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ eachRun {
+ primaryApp.launchViaIntent(wmHelper)
+ // TODO(b/231399940): Use recent shortcut to enter split.
+ tapl.launchedAppState.taskbar
+ .openAllApps()
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+
+ tapl.goHome()
+ wmHelper.StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ }
+ }
+ transitions {
+ tapl.workspace.quickSwitchToPreviousApp()
+ SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
+
+ @Presubmit
+ @Test
+ fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp)
+
+ @Presubmit
+ @Test
+ fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
+ primaryApp, splitLeftTop = false)
+
+ @Presubmit
+ @Test
+ fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
+ secondaryApp, splitLeftTop = true)
+
+ @Presubmit
+ @Test
+ fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp)
+
+ @Presubmit
+ @Test
+ fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun entireScreenCovered() =
+ super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() =
+ super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() =
+ super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() =
+ super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() =
+ super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() =
+ super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() =
+ super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() =
+ super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): List<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests(
+ repetitions = SplitScreenHelper.TEST_REPETITIONS,
+ // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy.
+ supportedNavigationModes =
+ listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY))
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
index 05e4722..cc51efd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipBoundsControllerTest.kt
@@ -179,6 +179,16 @@
}
@Test
+ fun testImmediatePlacement_DoNotStashIfAlreadyUnstashed() {
+ triggerImmediatePlacement(STASHED_PLACEMENT_RESTASH)
+ assertMovement(STASHED_BOUNDS)
+ assertMovementAt(time + STASH_DURATION, ANCHOR_BOUNDS)
+
+ triggerImmediatePlacement(STASHED_PLACEMENT)
+ assertNoMovementUpTo(time + FAR_FUTURE)
+ }
+
+ @Test
fun testInMoveMode_KeepAtAnchor() {
startMoveMode()
triggerImmediatePlacement(STASHED_MOVED_PLACEMENT_RESTASH)
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index 19efc5f..6a7119b 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -140,15 +140,13 @@
static jobject Typeface_getSupportedAxes(JNIEnv *env, jobject, jlong faceHandle) {
Typeface* face = toTypeface(faceHandle);
- const std::unordered_set<minikin::AxisTag>& tagSet = face->fFontCollection->getSupportedTags();
- const size_t length = tagSet.size();
+ const size_t length = face->fFontCollection->getSupportedAxesCount();
if (length == 0) {
return nullptr;
}
std::vector<jint> tagVec(length);
- int index = 0;
- for (const auto& tag : tagSet) {
- tagVec[index++] = tag;
+ for (size_t i = 0; i < length; i++) {
+ tagVec[i] = face->fFontCollection->getSupportedAxisAt(i);
}
std::sort(tagVec.begin(), tagVec.end());
const jintArray result = env->NewIntArray(length);
@@ -305,7 +303,8 @@
}
}
-static jint Typeface_writeTypefaces(JNIEnv *env, jobject, jobject buffer, jlongArray faceHandles) {
+static jint Typeface_writeTypefaces(JNIEnv* env, jobject, jobject buffer, jint position,
+ jlongArray faceHandles) {
MinikinFontSkiaFactory::init();
ScopedLongArrayRO faces(env, faceHandles);
std::vector<Typeface*> typefaces;
@@ -314,7 +313,12 @@
typefaces.push_back(toTypeface(faces[i]));
}
void* addr = buffer == nullptr ? nullptr : env->GetDirectBufferAddress(buffer);
- minikin::BufferWriter writer(addr);
+ if (addr != nullptr &&
+ reinterpret_cast<intptr_t>(addr) % minikin::BufferReader::kMaxAlignment != 0) {
+ ALOGE("addr (%p) must be aligned at kMaxAlignment, but it was not.", addr);
+ return 0;
+ }
+ minikin::BufferWriter writer(addr, position);
std::vector<std::shared_ptr<minikin::FontCollection>> fontCollections;
std::unordered_map<std::shared_ptr<minikin::FontCollection>, size_t> fcToIndex;
for (Typeface* typeface : typefaces) {
@@ -334,11 +338,18 @@
return static_cast<jint>(writer.size());
}
-static jlongArray Typeface_readTypefaces(JNIEnv *env, jobject, jobject buffer) {
+static jlongArray Typeface_readTypefaces(JNIEnv* env, jobject, jobject buffer, jint position) {
MinikinFontSkiaFactory::init();
void* addr = buffer == nullptr ? nullptr : env->GetDirectBufferAddress(buffer);
- if (addr == nullptr) return nullptr;
- minikin::BufferReader reader(addr);
+ if (addr == nullptr) {
+ ALOGE("Passed a null buffer.");
+ return nullptr;
+ }
+ if (reinterpret_cast<intptr_t>(addr) % minikin::BufferReader::kMaxAlignment != 0) {
+ ALOGE("addr (%p) must be aligned at kMaxAlignment, but it was not.", addr);
+ return nullptr;
+ }
+ minikin::BufferReader reader(addr, position);
std::vector<std::shared_ptr<minikin::FontCollection>> fontCollections =
minikin::FontCollection::readVector(&reader);
uint32_t typefaceCount = reader.read<uint32_t>();
@@ -357,7 +368,6 @@
return result;
}
-
static void Typeface_forceSetStaticFinalField(JNIEnv *env, jclass cls, jstring fieldName,
jobject typeface) {
ScopedUtfChars fieldNameChars(env, fieldName);
@@ -417,8 +427,8 @@
{"nativeGetSupportedAxes", "(J)[I", (void*)Typeface_getSupportedAxes},
{"nativeRegisterGenericFamily", "(Ljava/lang/String;J)V",
(void*)Typeface_registerGenericFamily},
- {"nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;[J)I", (void*)Typeface_writeTypefaces},
- {"nativeReadTypefaces", "(Ljava/nio/ByteBuffer;)[J", (void*)Typeface_readTypefaces},
+ {"nativeWriteTypefaces", "(Ljava/nio/ByteBuffer;I[J)I", (void*)Typeface_writeTypefaces},
+ {"nativeReadTypefaces", "(Ljava/nio/ByteBuffer;I)[J", (void*)Typeface_readTypefaces},
{"nativeForceSetStaticFinalField", "(Ljava/lang/String;Landroid/graphics/Typeface;)V",
(void*)Typeface_forceSetStaticFinalField},
{"nativeGetFamilySize", "(J)I", (void*)Typeface_getFamilySize},
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 2c421f8..f17129c 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -228,7 +228,7 @@
static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontPtr) {
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
minikin::BufferReader reader = font->font->typefaceMetadataReader();
- if (reader.data() != nullptr) {
+ if (reader.current() != nullptr) {
std::string path = std::string(reader.readString());
if (path.empty()) {
return nullptr;
@@ -270,7 +270,7 @@
static jint Font_getIndex(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
minikin::BufferReader reader = font->font->typefaceMetadataReader();
- if (reader.data() != nullptr) {
+ if (reader.current() != nullptr) {
reader.skipString(); // fontPath
return reader.read<int>();
} else {
@@ -283,7 +283,7 @@
static jint Font_getAxisCount(CRITICAL_JNI_PARAMS_COMMA jlong fontPtr) {
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
minikin::BufferReader reader = font->font->typefaceMetadataReader();
- if (reader.data() != nullptr) {
+ if (reader.current() != nullptr) {
reader.skipString(); // fontPath
reader.skip<int>(); // fontIndex
return reader.readArray<minikin::FontVariation>().second;
@@ -298,7 +298,7 @@
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontPtr);
minikin::BufferReader reader = font->font->typefaceMetadataReader();
minikin::FontVariation var;
- if (reader.data() != nullptr) {
+ if (reader.current() != nullptr) {
reader.skipString(); // fontPath
reader.skip<int>(); // fontIndex
var = reader.readArray<minikin::FontVariation>().first[index];
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 955bfcc..444366a 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -65,6 +65,8 @@
private static final String TAG = "AudioSystem";
+ private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
+
// private constructor to prevent instantiating AudioSystem
private AudioSystem() {
throw new UnsupportedOperationException("Trying to instantiate AudioSystem");
@@ -243,6 +245,8 @@
public static final int AUDIO_FORMAT_LDAC = 0x23000000;
/** @hide */
public static final int AUDIO_FORMAT_LC3 = 0x2B000000;
+ /** @hide */
+ public static final int AUDIO_FORMAT_OPUS = 0x08000000;
/** @hide */
@@ -254,7 +258,9 @@
AUDIO_FORMAT_APTX,
AUDIO_FORMAT_APTX_HD,
AUDIO_FORMAT_LDAC,
- AUDIO_FORMAT_LC3}
+ AUDIO_FORMAT_LC3,
+ AUDIO_FORMAT_OPUS
+ }
)
@Retention(RetentionPolicy.SOURCE)
public @interface AudioFormatNativeEnumForBtCodec {}
@@ -287,6 +293,7 @@
case AUDIO_FORMAT_APTX_HD: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD;
case AUDIO_FORMAT_LDAC: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC;
case AUDIO_FORMAT_LC3: return BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3;
+ case AUDIO_FORMAT_OPUS: return SOURCE_CODEC_TYPE_OPUS; // TODO update in U
default:
Log.e(TAG, "Unknown audio format 0x" + Integer.toHexString(audioFormat)
+ " for conversion to BT codec");
@@ -329,6 +336,8 @@
return AudioSystem.AUDIO_FORMAT_LDAC;
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3:
return AudioSystem.AUDIO_FORMAT_LC3;
+ case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+ return AudioSystem.AUDIO_FORMAT_OPUS;
default:
Log.e(TAG, "Unknown BT codec 0x" + Integer.toHexString(btCodec)
+ " for conversion to audio format");
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 472586b..fde7afd 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -18,7 +18,11 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.graphics.GraphicBuffer;
import android.graphics.ImageFormat;
import android.graphics.ImageFormat.Format;
@@ -29,6 +33,7 @@
import android.hardware.HardwareBuffer.Usage;
import android.hardware.SyncFence;
import android.hardware.camera2.MultiResolutionImageReader;
+import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
@@ -87,6 +92,38 @@
/**
* <p>
+ * Flag to gate correct exception thrown by {@code #detachImage}.
+ * </p>
+ * <p>
+ * {@code #detachImage} is documented as throwing {@link java.lang.IllegalStateException} in
+ * the event of an error; a native helper method to this threw
+ * {@link java.lang.RuntimeException} if the surface was abandoned while detaching the
+ * {@code Image}.
+ * <p>
+ * This previously undocumented exception behavior continues through Android T.
+ * </p>
+ * <p>
+ * After Android T, the native helper method only throws {@code IllegalStateExceptions} in
+ * accordance with the documentation.
+ * </p>
+ * <p>
+ * {@code #detachImage} will now throw only ISEs if it runs into errors while detaching
+ * the image. Behavior on apps targeting API levels <= T remains unchanged.
+ * </p>
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ private static final long DETACH_THROWS_ISE_ONLY = 236825255L;
+
+ /**
+ * Cached value of {@link #DETACH_THROWS_ISE_ONLY} flag to prevent repeated calls when
+ * detaching image.
+ */
+ private final boolean mDetachThrowsIseOnly =
+ CompatChanges.isChangeEnabled(DETACH_THROWS_ISE_ONLY);
+
+ /**
+ * <p>
* Create a new reader for images of the desired size and format.
* </p>
* <p>
@@ -825,10 +862,10 @@
* </p>
* <p>
* After this call, the ImageReader no longer owns this image, and the image
- * ownership can be transfered to another entity like {@link ImageWriter}
+ * ownership can be transferred to another entity like {@link ImageWriter}
* via {@link ImageWriter#queueInputImage}. It's up to the new owner to
* release the resources held by this image. For example, if the ownership
- * of this image is transfered to an {@link ImageWriter}, the image will be
+ * of this image is transferred to an {@link ImageWriter}, the image will be
* freed by the ImageWriter after the image data consumption is done.
* </p>
* <p>
@@ -849,16 +886,22 @@
* @throws IllegalStateException If the ImageReader or image have been
* closed, or the has been detached, or has not yet been
* acquired.
+ * @throws RuntimeException If there is an error detaching {@code Image} from {@code Surface}.
+ * {@code RuntimeException} is only thrown for applications targeting SDK <=
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+ * For applications targeting SDK >
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU},
+ * this method only throws {@code IllegalStateException}.
* @hide
*/
- public void detachImage(Image image) {
- if (image == null) {
+ public void detachImage(@Nullable Image image) {
+ if (image == null) {
throw new IllegalArgumentException("input image must not be null");
- }
- if (!isImageOwnedbyMe(image)) {
+ }
+ if (!isImageOwnedbyMe(image)) {
throw new IllegalArgumentException("Trying to detach an image that is not owned by"
+ " this ImageReader");
- }
+ }
SurfaceImage si = (SurfaceImage) image;
si.throwISEIfImageIsInvalid();
@@ -867,7 +910,7 @@
throw new IllegalStateException("Image was already detached from this ImageReader");
}
- nativeDetachImage(image);
+ nativeDetachImage(image, mDetachThrowsIseOnly);
si.clearSurfacePlanes();
si.mPlanes = null;
si.setDetached(true);
@@ -1408,7 +1451,7 @@
private synchronized native void nativeClose();
private synchronized native void nativeReleaseImage(Image i);
private synchronized native Surface nativeGetSurface();
- private synchronized native int nativeDetachImage(Image i);
+ private synchronized native int nativeDetachImage(Image i, boolean throwISEOnly);
private synchronized native void nativeDiscardFreeBuffers();
/**
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 9bf126b..31fb8d0 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -35,7 +35,7 @@
ISessionController getController();
void setFlags(int flags);
void setActive(boolean active);
- void setMediaButtonReceiver(in PendingIntent mbr, String sessionPackageName);
+ void setMediaButtonReceiver(in PendingIntent mbr);
void setMediaButtonBroadcastReceiver(in ComponentName broadcastReceiver);
void setLaunchPendingIntent(in PendingIntent pi);
void destroySession();
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 9e265d8..1bd12af 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -286,7 +286,7 @@
@Deprecated
public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
try {
- mBinder.setMediaButtonReceiver(mbr, mContext.getPackageName());
+ mBinder.setMediaButtonReceiver(mbr);
} catch (RemoteException e) {
Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
}
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 62c0d55..556f98c 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -643,7 +643,8 @@
return ACQUIRE_SUCCESS;
}
-static jint ImageReader_detachImage(JNIEnv* env, jobject thiz, jobject image) {
+static jint ImageReader_detachImage(JNIEnv* env, jobject thiz, jobject image,
+ jboolean throwISEOnly) {
ALOGV("%s:", __FUNCTION__);
JNIImageReaderContext* ctx = ImageReader_getContext(env, thiz);
if (ctx == NULL) {
@@ -666,8 +667,12 @@
res = bufferConsumer->detachBuffer(buffer->mSlot);
if (res != OK) {
ALOGE("Image detach failed: %s (%d)!!!", strerror(-res), res);
- jniThrowRuntimeException(env,
- "nativeDetachImage failed for image!!!");
+ if ((bool) throwISEOnly) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "nativeDetachImage failed for image!!!");
+ } else {
+ jniThrowRuntimeException(env, "nativeDetachImage failed for image!!!");
+ }
return res;
}
return OK;
@@ -965,7 +970,7 @@
{"nativeReleaseImage", "(Landroid/media/Image;)V", (void*)ImageReader_imageRelease },
{"nativeImageSetup", "(Landroid/media/Image;Z)I", (void*)ImageReader_imageSetup },
{"nativeGetSurface", "()Landroid/view/Surface;", (void*)ImageReader_getSurface },
- {"nativeDetachImage", "(Landroid/media/Image;)I", (void*)ImageReader_detachImage },
+ {"nativeDetachImage", "(Landroid/media/Image;Z)I", (void*)ImageReader_detachImage },
{"nativeCreateImagePlanes",
"(ILandroid/graphics/GraphicBuffer;IIIIII)[Landroid/media/ImageReader$ImagePlane;",
(void*)ImageReader_createImagePlanes },
diff --git a/mime/java-res/android.mime.types b/mime/java-res/android.mime.types
index 60f0e9e..cb74cfc 100644
--- a/mime/java-res/android.mime.types
+++ b/mime/java-res/android.mime.types
@@ -86,6 +86,7 @@
?audio/x-matroska mka
?audio/x-pn-realaudio ra
?audio/x-mpeg mp3
+?audio/mp3 mp3
?image/bmp bmp
?image/gif gif
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
index a596a9a..7c3f5a5 100644
--- a/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout-v31/settingslib_action_buttons.xml
@@ -28,8 +28,7 @@
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"
- android:hyphenationFrequency="normalFast"/>
+ android:layout_weight="1"/>
<View
android:id="@+id/divider1"
@@ -44,8 +43,7 @@
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"
- android:hyphenationFrequency="normalFast"/>
+ android:layout_weight="1"/>
<View
android:id="@+id/divider2"
@@ -60,8 +58,7 @@
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"
- android:hyphenationFrequency="normalFast"/>
+ android:layout_weight="1"/>
<View
android:id="@+id/divider3"
@@ -76,6 +73,5 @@
style="@style/SettingsLibActionButton"
android:layout_width="0dp"
android:layout_height="match_parent"
- android:layout_weight="1"
- android:hyphenationFrequency="normalFast"/>
+ android:layout_weight="1"/>
</LinearLayout>
diff --git a/packages/SettingsLib/ActionButtonsPreference/res/layout-v33/settingslib_action_buttons.xml b/packages/SettingsLib/ActionButtonsPreference/res/layout-v33/settingslib_action_buttons.xml
new file mode 100644
index 0000000..b4640ee
--- /dev/null
+++ b/packages/SettingsLib/ActionButtonsPreference/res/layout-v33/settingslib_action_buttons.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:paddingHorizontal="8dp"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/button1"
+ style="@style/SettingsLibActionButton"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:lineBreakWordStyle="phrase"
+ android:hyphenationFrequency="normalFast"/>
+
+ <View
+ android:id="@+id/divider1"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="4dp"
+ android:visibility="gone"
+ android:background="?android:colorBackground" />
+
+ <Button
+ android:id="@+id/button2"
+ style="@style/SettingsLibActionButton"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:lineBreakWordStyle="phrase"
+ android:hyphenationFrequency="normalFast"/>
+
+ <View
+ android:id="@+id/divider2"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="4dp"
+ android:visibility="gone"
+ android:background="?android:colorBackground" />
+
+ <Button
+ android:id="@+id/button3"
+ style="@style/SettingsLibActionButton"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:lineBreakWordStyle="phrase"
+ android:hyphenationFrequency="normalFast"/>
+
+ <View
+ android:id="@+id/divider3"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="4dp"
+ android:visibility="gone"
+ android:background="?android:colorBackground" />
+
+ <Button
+ android:id="@+id/button4"
+ style="@style/SettingsLibActionButton"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:lineBreakWordStyle="phrase"
+ android:hyphenationFrequency="normalFast"/>
+</LinearLayout>
diff --git a/packages/SettingsLib/AppPreference/res/layout-v33/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app.xml
new file mode 100644
index 0000000..8975857
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:minWidth="@dimen/secondary_app_icon_size"
+ android:orientation="horizontal"
+ android:paddingEnd="16dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="@dimen/secondary_app_icon_size"
+ android:layout_height="@dimen/secondary_app_icon_size"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textDirection="locale"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <TextView
+ android:id="@+id/appendix"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textColor="?android:attr/textColorSecondary"
+ android:visibility="gone"/>
+
+ <ProgressBar
+ android:id="@android:id/progress"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:max="100"
+ android:visibility="gone"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical|end"
+ android:minWidth="@dimen/two_target_min_width"
+ android:orientation="vertical"/>
+
+</LinearLayout>
diff --git a/packages/SettingsLib/AppPreference/res/layout-v33/preference_app_header.xml b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app_header.xml
new file mode 100644
index 0000000..3a219b9
--- /dev/null
+++ b/packages/SettingsLib/AppPreference/res/layout-v33/preference_app_header.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingBottom="16dp"
+ android:paddingTop="8dp"
+ android:clickable="false">
+
+ <TextView
+ android:id="@+id/apps_top_intro_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textDirection="locale"
+ android:clickable="false"
+ android:longClickable="false"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="@style/TextAppearance.TopIntroText" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
index 12db901..e65f7de 100644
--- a/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
+++ b/packages/SettingsLib/AppPreference/res/layout/preference_app.xml
@@ -55,7 +55,6 @@
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"/>
<TextView
@@ -63,7 +62,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textDirection="locale"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"/>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
index ec091bf..f4af9c7 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
@@ -19,6 +19,7 @@
import static android.text.Layout.HYPHENATION_FREQUENCY_NORMAL_FAST;
import android.app.ActionBar;
+import android.graphics.text.LineBreakConfig;
import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
@@ -88,6 +89,12 @@
mCollapsingToolbarLayout.setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
mCollapsingToolbarLayout.setHyphenationFrequency(HYPHENATION_FREQUENCY_NORMAL_FAST);
+ mCollapsingToolbarLayout.setStaticLayoutBuilderConfigurer(builder ->
+ builder.setLineBreakConfig(
+ new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(
+ LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+ .build()));
}
}
disableCollapsingToolbarLayoutScrollingBehavior();
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
index a8c7a3f..522de93 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java
@@ -22,6 +22,7 @@
import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
+import android.graphics.text.LineBreakConfig;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -110,6 +111,12 @@
mCollapsingToolbarLayout.setLineSpacingMultiplier(TOOLBAR_LINE_SPACING_MULTIPLIER);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
mCollapsingToolbarLayout.setHyphenationFrequency(HYPHENATION_FREQUENCY_NORMAL_FAST);
+ mCollapsingToolbarLayout.setStaticLayoutBuilderConfigurer(builder ->
+ builder.setLineBreakConfig(
+ new LineBreakConfig.Builder()
+ .setLineBreakWordStyle(
+ LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE)
+ .build()));
}
if (!TextUtils.isEmpty(mToolbarTitle)) {
mCollapsingToolbarLayout.setTitle(mToolbarTitle);
diff --git a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
index 2c1fdd4..42700b3 100644
--- a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
+++ b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
@@ -53,7 +53,6 @@
android:paddingTop="16dp"
android:paddingBottom="8dp"
android:textColor="?android:attr/textColorSecondary"
- android:hyphenationFrequency="normalFast"
android:ellipsize="marquee" />
<com.android.settingslib.widget.LinkTextView
diff --git a/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
new file mode 100644
index 0000000..a2f2510
--- /dev/null
+++ b/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeight"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/selectableItemBackground"
+ android:orientation="vertical"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="56dp"
+ android:gravity="start|top"
+ android:orientation="horizontal"
+ android:paddingEnd="12dp"
+ android:paddingTop="16dp"
+ android:paddingBottom="4dp">
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="16dp"
+ android:paddingBottom="8dp"
+ android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:ellipsize="marquee" />
+
+ <com.android.settingslib.widget.LinkTextView
+ android:id="@+id/settingslib_learn_more"
+ android:text="@string/settingslib_learn_more_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="8dp"
+ android:clickable="true"
+ android:visibility="gone"
+ style="@style/TextAppearance.Footer.Title.SettingsLib"/>
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
new file mode 100644
index 0000000..35d1323
--- /dev/null
+++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:background="?android:attr/colorBackground"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:id="@+id/frame"
+ android:minHeight="@dimen/settingslib_min_switch_bar_height"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_margin="@dimen/settingslib_switchbar_margin"
+ android:paddingStart="@dimen/settingslib_switchbar_padding_left"
+ android:paddingEnd="@dimen/settingslib_switchbar_padding_right">
+
+ <TextView
+ android:id="@+id/switch_text"
+ android:layout_height="wrap_content"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ android:layout_marginEnd="@dimen/settingslib_switch_title_margin"
+ android:layout_marginVertical="@dimen/settingslib_switch_title_margin"
+ android:layout_gravity="center_vertical"
+ android:ellipsize="end"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ style="@style/MainSwitchText.Settingslib" />
+
+ <ImageView
+ android:id="@+id/restricted_icon"
+ android:layout_width="@dimen/settingslib_restricted_icon_size"
+ android:layout_height="@dimen/settingslib_restricted_icon_size"
+ android:tint="?android:attr/colorAccent"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="@dimen/settingslib_restricted_icon_margin_end"
+ android:src="@drawable/settingslib_ic_info"
+ android:visibility="gone" />
+
+ <Switch
+ android:id="@android:id/switch_widget"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_gravity="center_vertical"
+ android:focusable="false"
+ android:clickable="false"
+ android:theme="@style/Switch.SettingsLib"/>
+ </LinearLayout>
+
+</LinearLayout>
+
+
diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml
new file mode 100644
index 0000000..bb8ac28
--- /dev/null
+++ b/packages/SettingsLib/RadioButtonPreference/res/layout-v33/preference_radio.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="20dp"
+ android:gravity="center"
+ android:minWidth="56dp"
+ android:orientation="vertical"/>
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:minWidth="32dp"
+ android:orientation="horizontal"
+ android:layout_marginEnd="16dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ settings:maxWidth="@dimen/secondary_app_icon_size"
+ settings:maxHeight="@dimen/secondary_app_icon_size"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+ <LinearLayout
+ android:id="@+id/summary_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewStart"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <TextView
+ android:id="@+id/appendix"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewEnd"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="1"
+ android:visibility="gone"
+ android:ellipsize="end"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/radio_extra_widget_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
+ <View
+ android:layout_width=".75dp"
+ android:layout_height="32dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:background="?android:attr/dividerVertical" />
+ <ImageView
+ android:id="@+id/radio_extra_widget"
+ android:layout_width="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:layout_height="fill_parent"
+ android:src="@drawable/ic_settings_accent"
+ android:contentDescription="@string/settings_label"
+ android:paddingStart="24dp"
+ android:paddingEnd="24dp"
+ android:layout_gravity="center"
+ android:background="?android:attr/selectableItemBackground" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
index 64d100a..906ff2c 100644
--- a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
+++ b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
@@ -66,7 +66,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"/>
<LinearLayout
@@ -81,7 +80,6 @@
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textAlignment="viewStart"
- android:hyphenationFrequency="normalFast"
android:textColor="?android:attr/textColorSecondary"/>
<TextView
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout-v33/preference_selector_with_widget.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout-v33/preference_selector_with_widget.xml
new file mode 100644
index 0000000..0b27464
--- /dev/null
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout-v33/preference_selector_with_widget.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="center_vertical"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:paddingHorizontal="20dp"
+ android:gravity="center"
+ android:minWidth="56dp"
+ android:orientation="vertical"/>
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:minWidth="32dp"
+ android:orientation="horizontal"
+ android:layout_marginEnd="16dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ settings:maxWidth="@dimen/secondary_app_icon_size"
+ settings:maxHeight="@dimen/secondary_app_icon_size"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:orientation="vertical"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"/>
+
+ <LinearLayout
+ android:id="@+id/summary_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone">
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewStart"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <TextView
+ android:id="@+id/appendix"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:textAlignment="viewEnd"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="1"
+ android:visibility="gone"
+ android:ellipsize="end"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/selector_extra_widget_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
+ <View
+ android:layout_width=".75dp"
+ android:layout_height="32dp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:background="?android:attr/dividerVertical" />
+ <ImageView
+ android:id="@+id/selector_extra_widget"
+ android:layout_width="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:layout_height="fill_parent"
+ android:src="@drawable/ic_settings_accent"
+ android:contentDescription="@string/settings_label"
+ android:paddingStart="24dp"
+ android:paddingEnd="24dp"
+ android:layout_gravity="center"
+ android:background="?android:attr/selectableItemBackground" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
index 2a550ef..8bb56ff 100644
--- a/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
+++ b/packages/SettingsLib/SelectorWithWidgetPreference/res/layout/preference_selector_with_widget.xml
@@ -66,7 +66,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"/>
<LinearLayout
@@ -81,7 +80,6 @@
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textAlignment="viewStart"
- android:hyphenationFrequency="normalFast"
android:textColor="?android:attr/textColorSecondary"/>
<TextView
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
index d4d466a..23aa993 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
@@ -43,7 +43,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee"/>
@@ -58,7 +57,6 @@
android:textAlignment="viewStart"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="10"
- android:hyphenationFrequency="normalFast"
style="@style/PreferenceSummaryTextStyle"/>
</RelativeLayout>
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
new file mode 100644
index 0000000..70ce374
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/selectableItemBackground"
+ android:clipToPadding="false"
+ android:baselineAligned="false">
+
+ <include layout="@layout/settingslib_icon_frame"/>
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee"/>
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignLeft="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:layout_gravity="start"
+ android:textAlignment="viewStart"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ style="@style/PreferenceSummaryTextStyle"/>
+
+ </RelativeLayout>
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="end|center_vertical"
+ android:paddingLeft="16dp"
+ android:paddingStart="16dp"
+ android:paddingRight="0dp"
+ android:paddingEnd="0dp"
+ android:orientation="vertical"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml
new file mode 100644
index 0000000..6046d91
--- /dev/null
+++ b/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:paddingBottom="16dp"
+ android:paddingTop="8dp"
+ android:background="?android:attr/selectableItemBackground"
+ android:clipToPadding="false">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clickable="false"
+ android:longClickable="false"
+ android:maxLines="10"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="@style/TextAppearance.TopIntroText"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
index b2a9037..4d6e1b7 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
@@ -33,6 +33,5 @@
android:clickable="false"
android:longClickable="false"
android:maxLines="10"
- android:hyphenationFrequency="normalFast"
android:textAppearance="@style/TextAppearance.TopIntroText"/>
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
index ac5807d..2c35772 100644
--- a/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout-v31/preference_two_target.xml
@@ -41,7 +41,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee"/>
@@ -53,7 +52,6 @@
android:layout_alignStart="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
- android:hyphenationFrequency="normalFast"
android:maxLines="10"/>
</RelativeLayout>
diff --git a/packages/SettingsLib/TwoTargetPreference/res/layout-v33/preference_two_target.xml b/packages/SettingsLib/TwoTargetPreference/res/layout-v33/preference_two_target.xml
new file mode 100644
index 0000000..0bca9ab
--- /dev/null
+++ b/packages/SettingsLib/TwoTargetPreference/res/layout-v33/preference_two_target.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!-- Based off preference_material_settings.xml except that ripple on only on the left side. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:clipToPadding="false">
+
+ <include layout="@layout/settingslib_icon_frame"/>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee"/>
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:maxLines="10"/>
+
+ </RelativeLayout>
+
+ <include layout="@layout/preference_two_target_divider" />
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:gravity="center"
+ android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout-v33/preference_access_point.xml b/packages/SettingsLib/res/layout-v33/preference_access_point.xml
new file mode 100644
index 0000000..81bfeffd
--- /dev/null
+++ b/packages/SettingsLib/res/layout-v33/preference_access_point.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!-- Based off preference_two_target.xml with Material ripple moved to parent for full ripple. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:background="?android:attr/selectableItemBackground"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="start|center_vertical"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:minWidth="48dp"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <androidx.preference.internal.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ settings:maxWidth="48dp"
+ settings:maxHeight="48dp" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee" />
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:maxLines="10" />
+
+ </RelativeLayout>
+
+ </LinearLayout>
+
+ <include layout="@layout/preference_two_target_divider" />
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:gravity="center"
+ android:orientation="vertical" />
+
+ <ImageButton
+ android:id="@+id/icon_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:minHeight="@dimen/min_tap_target_size"
+ android:layout_gravity="center"
+ android:background="?android:attr/selectableItemBackground"
+ android:visibility="gone">
+ </ImageButton>
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout-v33/preference_checkable_two_target.xml b/packages/SettingsLib/res/layout-v33/preference_checkable_two_target.xml
new file mode 100644
index 0000000..7ad018c
--- /dev/null
+++ b/packages/SettingsLib/res/layout-v33/preference_checkable_two_target.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<!-- Based off preference_material_settings.xml except that ripple on only on the left side. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:settings="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:background="@android:color/transparent"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:background="?android:attr/selectableItemBackground"
+ android:gravity="start|center_vertical"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/checkbox_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:minWidth="48dp"
+ android:minHeight="48dp"
+ android:orientation="horizontal"
+ android:clipToPadding="false"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <include layout="@layout/preference_widget_checkbox" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee" />
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:maxLines="10" />
+
+ </RelativeLayout>
+
+ </LinearLayout>
+
+ <include layout="@layout/preference_two_target_divider" />
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:minWidth="@dimen/two_target_min_width"
+ android:gravity="center"
+ android:orientation="vertical" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml b/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml
new file mode 100644
index 0000000..31e9696
--- /dev/null
+++ b/packages/SettingsLib/res/layout-v33/restricted_switch_preference.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/selectableItemBackground"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:id="@+id/icon_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="56dp"
+ android:gravity="start|center_vertical"
+ android:orientation="horizontal"
+ android:paddingEnd="12dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp">
+ <com.android.internal.widget.PreferenceImageView
+ android:id="@android:id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxWidth="48dp"
+ android:maxHeight="48dp" />
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:ellipsize="marquee" />
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:maxLines="10" />
+
+ <TextView
+ android:id="@+id/additional_summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/summary"
+ android:layout_alignStart="@android:id/summary"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10"
+ android:hyphenationFrequency="normalFast"
+ android:lineBreakWordStyle="phrase"
+ android:visibility="gone" />
+ </RelativeLayout>
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="end|center_vertical"
+ android:paddingStart="16dp"
+ android:orientation="vertical" />
+
+</LinearLayout>
diff --git a/packages/SettingsLib/res/layout/preference_access_point.xml b/packages/SettingsLib/res/layout/preference_access_point.xml
index 4ad9d80..802d604 100644
--- a/packages/SettingsLib/res/layout/preference_access_point.xml
+++ b/packages/SettingsLib/res/layout/preference_access_point.xml
@@ -65,7 +65,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee" />
@@ -77,7 +76,6 @@
android:layout_alignStart="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
- android:hyphenationFrequency="normalFast"
android:maxLines="10" />
</RelativeLayout>
diff --git a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
index cbe49cd..f512f9b 100644
--- a/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
+++ b/packages/SettingsLib/res/layout/preference_checkable_two_target.xml
@@ -62,7 +62,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee" />
@@ -74,7 +73,6 @@
android:layout_alignStart="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
- android:hyphenationFrequency="normalFast"
android:maxLines="10" />
</RelativeLayout>
diff --git a/packages/SettingsLib/res/layout/restricted_switch_preference.xml b/packages/SettingsLib/res/layout/restricted_switch_preference.xml
index edea144..169ae97 100644
--- a/packages/SettingsLib/res/layout/restricted_switch_preference.xml
+++ b/packages/SettingsLib/res/layout/restricted_switch_preference.xml
@@ -52,7 +52,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
- android:hyphenationFrequency="normalFast"
android:textAppearance="?android:attr/textAppearanceListItem"
android:ellipsize="marquee" />
@@ -63,7 +62,6 @@
android:layout_alignStart="@android:id/title"
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
- android:hyphenationFrequency="normalFast"
android:maxLines="10" />
<TextView android:id="@+id/additional_summary"
@@ -74,7 +72,6 @@
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="10"
- android:hyphenationFrequency="normalFast"
android:visibility="gone" />
</RelativeLayout>
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 29a1831..663e8e4 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -154,6 +154,8 @@
<item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx">aptX™</xliff:g> audio</item>
<item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx_hd">aptX™ HD</xliff:g> audio</item>
<item>LDAC</item>
+ <item>LC3</item>
+ <item>Opus</item>
</string-array>
<!-- Values for Bluetooth Audio Codec selection preference. -->
@@ -164,6 +166,8 @@
<item>2</item>
<item>3</item>
<item>4</item>
+ <item>5</item>
+ <item>6</item>
</string-array>
<!-- Summaries for Bluetooth Audio Codec selection preference. [CHAR LIMIT=50]-->
@@ -174,6 +178,8 @@
<item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx">aptX™</xliff:g> audio</item>
<item><xliff:g id="qualcomm">Qualcomm®</xliff:g> <xliff:g id="aptx_hd">aptX™ HD</xliff:g> audio</item>
<item>LDAC</item>
+ <item>LC3</item>
+ <item>Opus</item>
</string-array>
<!-- Titles for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=50] -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index df19c67..1940986 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -43,6 +43,8 @@
public class A2dpProfile implements LocalBluetoothProfile {
private static final String TAG = "A2dpProfile";
+ private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
+
private Context mContext;
private BluetoothA2dp mService;
@@ -328,6 +330,12 @@
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
index = 5;
break;
+ case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3:
+ index = 6;
+ break;
+ case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+ index = 7;
+ break;
}
if (index < 0) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
index 1a08366..b416738 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/MobileStatusTracker.java
@@ -44,6 +44,8 @@
private final Handler mReceiverHandler;
private final MobileTelephonyCallback mTelephonyCallback;
+ private boolean mListening = false;
+
/**
* MobileStatusTracker constructors
*
@@ -76,6 +78,7 @@
* Config the MobileStatusTracker to start or stop monitoring platform signals.
*/
public void setListening(boolean listening) {
+ mListening = listening;
if (listening) {
mPhone.registerTelephonyCallback(mReceiverHandler::post, mTelephonyCallback);
} else {
@@ -83,6 +86,10 @@
}
}
+ public boolean isListening() {
+ return mListening;
+ }
+
private void updateDataSim() {
int activeDataSubId = mDefaults.getActiveDataSubId();
if (SubscriptionManager.isValidSubscriptionId(activeDataSubId)) {
diff --git a/packages/SystemUI/res/drawable-hdpi/textfield_default_filled.9.png b/packages/SystemUI/res/drawable-hdpi/textfield_default_filled.9.png
new file mode 100644
index 0000000..3dd997f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/textfield_default_filled.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/textfield_default_filled.9.png b/packages/SystemUI/res/drawable-mdpi/textfield_default_filled.9.png
new file mode 100644
index 0000000..80aba01
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/textfield_default_filled.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/textfield_default_filled.9.png b/packages/SystemUI/res/drawable-xhdpi/textfield_default_filled.9.png
new file mode 100644
index 0000000..b3f89ed
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/textfield_default_filled.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/textfield_default_filled.9.png b/packages/SystemUI/res/drawable-xxhdpi/textfield_default_filled.9.png
new file mode 100644
index 0000000..efa2cb9
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/textfield_default_filled.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/edit_text_filled.xml b/packages/SystemUI/res/drawable/edit_text_filled.xml
new file mode 100644
index 0000000..cca34d4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/edit_text_filled.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetLeft="4dp"
+ android:insetRight="4dp"
+ android:insetTop="10dp"
+ android:insetBottom="10dp">
+ <selector>
+ <item android:state_enabled="false">
+ <nine-patch android:src="@drawable/textfield_default_filled"
+ android:tint="?android:attr/colorControlNormal" />
+ </item>
+ <item android:state_pressed="false" android:state_focused="false">
+ <nine-patch android:src="@drawable/textfield_default_filled"
+ android:tint="?android:attr/colorControlNormal" />
+ </item>
+ <item>
+ <nine-patch android:src="@drawable/textfield_default_filled"
+ android:tint="?android:attr/colorControlActivated" />
+ </item>
+ </selector>
+</inset>
diff --git a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
index da76c8d..bc8e540 100644
--- a/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout-land/auth_credential_password_view.xml
@@ -18,44 +18,70 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- android:gravity="center_horizontal"
+ android:orientation="horizontal"
android:elevation="@dimen/biometric_dialog_elevation">
- <TextView
- android:id="@+id/title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.AuthCredential.Title"/>
+ <RelativeLayout
+ android:id="@+id/auth_credential_header"
+ style="@style/AuthCredentialHeaderStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent">
- <TextView
- android:id="@+id/subtitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.AuthCredential.Subtitle"/>
+ <ImageView
+ android:id="@+id/icon"
+ style="@style/TextAppearance.AuthNonBioCredential.Icon"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:contentDescription="@null"/>
- <TextView
- android:id="@+id/description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.AuthCredential.Description"/>
+ <TextView
+ android:id="@+id/title"
+ style="@style/TextAppearance.AuthNonBioCredential.Title"
+ android:layout_below="@id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
- <ImeAwareEditText
- android:id="@+id/lockPassword"
- android:layout_width="208dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:minHeight="48dp"
- android:gravity="center"
- android:inputType="textPassword"
- android:maxLength="500"
- android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
- style="@style/TextAppearance.AuthCredential.PasswordEntry"/>
+ <TextView
+ android:id="@+id/subtitle"
+ style="@style/TextAppearance.AuthNonBioCredential.Subtitle"
+ android:layout_below="@id/title"
+ android:layout_alignParentLeft="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
- <TextView
- android:id="@+id/error"
- android:layout_width="match_parent"
+ <TextView
+ android:id="@+id/description"
+ style="@style/TextAppearance.AuthNonBioCredential.Description"
+ android:layout_below="@id/subtitle"
+ android:layout_alignParentLeft="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ </RelativeLayout>
+
+ <LinearLayout
+ android:id="@+id/auth_credential_input"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- style="@style/TextAppearance.AuthCredential.Error"/>
+ android:orientation="vertical">
+
+ <ImeAwareEditText
+ android:id="@+id/lockPassword"
+ style="@style/TextAppearance.AuthCredential.PasswordEntry"
+ android:layout_width="208dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+ android:inputType="textPassword"
+ android:minHeight="48dp" />
+
+ <TextView
+ android:id="@+id/error"
+ style="@style/TextAppearance.AuthNonBioCredential.Error"
+ android:layout_gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
</com.android.systemui.biometrics.AuthCredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
index 0ff1db2..75a80bc 100644
--- a/packages/SystemUI/res/layout/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -22,68 +22,63 @@
android:orientation="vertical">
<RelativeLayout
+ android:id="@+id/auth_credential_header"
+ style="@style/AuthCredentialHeaderStyle"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/icon"
+ style="@style/TextAppearance.AuthNonBioCredential.Icon"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:contentDescription="@null"/>
+
+ <TextView
+ android:id="@+id/title"
+ style="@style/TextAppearance.AuthNonBioCredential.Title"
+ android:layout_below="@id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/subtitle"
+ style="@style/TextAppearance.AuthNonBioCredential.Subtitle"
+ android:layout_below="@id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:id="@+id/description"
+ style="@style/TextAppearance.AuthNonBioCredential.Description"
+ android:layout_below="@id/subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </RelativeLayout>
+
+ <LinearLayout
+ android:id="@+id/auth_credential_input"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
android:orientation="vertical">
- <LinearLayout
- android:id="@+id/auth_credential_header"
- style="@style/AuthCredentialHeaderStyle"
- android:layout_width="match_parent"
+ <ImeAwareEditText
+ android:id="@+id/lockPassword"
+ style="@style/TextAppearance.AuthCredential.PasswordEntry"
+ android:layout_width="208dp"
android:layout_height="wrap_content"
- android:layout_alignParentTop="true">
+ android:layout_gravity="center_horizontal"
+ android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+ android:inputType="textPassword"
+ android:minHeight="48dp" />
- <ImageView
- android:id="@+id/icon"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:contentDescription="@null" />
-
- <TextView
- android:id="@+id/title"
- style="@style/TextAppearance.AuthNonBioCredential.Title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
-
- <TextView
- android:id="@+id/subtitle"
- style="@style/TextAppearance.AuthNonBioCredential.Subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
-
- <TextView
- android:id="@+id/description"
- style="@style/TextAppearance.AuthNonBioCredential.Description"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
-
- </LinearLayout>
-
- <LinearLayout
+ <TextView
+ android:id="@+id/error"
+ style="@style/TextAppearance.AuthNonBioCredential.Error"
+ android:layout_gravity="center_horizontal"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="center"
- android:orientation="vertical"
- android:layout_alignParentBottom="true">
+ android:layout_height="wrap_content" />
- <ImeAwareEditText
- android:id="@+id/lockPassword"
- style="@style/TextAppearance.AuthCredential.PasswordEntry"
- android:layout_width="208dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
- android:inputType="textPassword"
- android:minHeight="48dp" />
-
- <TextView
- android:id="@+id/error"
- style="@style/TextAppearance.AuthNonBioCredential.Error"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
-
- </LinearLayout>
-
- </RelativeLayout>
+ </LinearLayout>
</com.android.systemui.biometrics.AuthCredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-land/styles.xml b/packages/SystemUI/res/values-land/styles.xml
index 8919198..ac9a947 100644
--- a/packages/SystemUI/res/values-land/styles.xml
+++ b/packages/SystemUI/res/values-land/styles.xml
@@ -18,4 +18,13 @@
<style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer">
<item name="android:layout_width">360dp</item>
</style>
+
+ <style name="AuthCredentialHeaderStyle">
+ <item name="android:paddingStart">48dp</item>
+ <item name="android:paddingEnd">24dp</item>
+ <item name="android:paddingTop">48dp</item>
+ <item name="android:paddingBottom">10dp</item>
+ <item name="android:gravity">top|center_horizontal</item>
+ </style>
+
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ddbe6d6..60932a7 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -940,6 +940,9 @@
<!-- Y translation for credential contents when animating in -->
<dimen name="biometric_dialog_credential_translation_offset">60dp</dimen>
+ <!-- Biometric Auth Credential values -->
+ <dimen name="biometric_auth_icon_size">48dp</dimen>
+
<!-- Starting text size in sp of batteryLevel for wireless charging animation -->
<item name="wireless_charging_anim_battery_level_text_size_start" format="float" type="dimen">
0
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 2227978..f7acf06 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -198,6 +198,11 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
+ <style name="TextAppearance.AuthNonBioCredential.Icon">
+ <item name="android:layout_width">@dimen/biometric_auth_icon_size</item>
+ <item name="android:layout_height">@dimen/biometric_auth_icon_size</item>
+ </style>
+
<style name="TextAppearance.AuthNonBioCredential.Title">
<item name="android:fontFamily">google-sans</item>
<item name="android:layout_marginTop">20dp</item>
@@ -227,14 +232,16 @@
<style name="TextAppearance.AuthCredential.PasswordEntry" parent="@android:style/TextAppearance.DeviceDefault">
<item name="android:gravity">center</item>
+ <item name="android:paddingTop">28dp</item>
<item name="android:singleLine">true</item>
<item name="android:textColor">?android:attr/colorForeground</item>
<item name="android:textSize">24sp</item>
+ <item name="android:background">@drawable/edit_text_filled</item>
</style>
<style name="AuthCredentialHeaderStyle">
<item name="android:paddingStart">48dp</item>
- <item name="android:paddingEnd">24dp</item>
+ <item name="android:paddingEnd">48dp</item>
<item name="android:paddingTop">28dp</item>
<item name="android:paddingBottom">20dp</item>
<item name="android:orientation">vertical</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 916526d..ee30972 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -19,7 +19,6 @@
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED;
-import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
import static android.app.ActivityTaskManager.getService;
import android.annotation.NonNull;
@@ -27,7 +26,6 @@
import android.app.Activity;
import android.app.ActivityClient;
import android.app.ActivityManager;
-import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
@@ -337,7 +335,8 @@
* Shows a voice session identified by {@code token}
* @return true if the session was shown, false otherwise
*/
- public boolean showVoiceSession(IBinder token, Bundle args, int flags) {
+ public boolean showVoiceSession(@NonNull IBinder token, @NonNull Bundle args, int flags,
+ @Nullable String attributionTag) {
IVoiceInteractionManagerService service = IVoiceInteractionManagerService.Stub.asInterface(
ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
if (service == null) {
@@ -346,7 +345,7 @@
args.putLong(INVOCATION_TIME_MS_KEY, SystemClock.elapsedRealtime());
try {
- return service.showSessionFromSession(token, args, flags);
+ return service.showSessionFromSession(token, args, flags, attributionTag);
} catch (RemoteException e) {
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index 7c2673c..57ffdab 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -310,7 +310,8 @@
private void startVoiceInteractor(Bundle args) {
mAssistUtils.showSessionForActiveService(args,
- VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE, null, null);
+ VoiceInteractionSession.SHOW_SOURCE_ASSIST_GESTURE, mContext.getAttributionTag(),
+ null, null);
}
public void launchVoiceAssistFromKeyguard() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index 0892612..5ed8986 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -16,12 +16,20 @@
package com.android.systemui.biometrics;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.WindowInsets.Type.ime;
+
import android.annotation.NonNull;
import android.content.Context;
+import android.graphics.Insets;
import android.os.UserHandle;
import android.text.InputType;
import android.util.AttributeSet;
import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnApplyWindowInsetsListener;
+import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.ImeAwareEditText;
@@ -31,18 +39,24 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.VerifyCredentialResponse;
+import com.android.systemui.Dumpable;
import com.android.systemui.R;
+import java.io.PrintWriter;
+
/**
* Pin and Password UI
*/
public class AuthCredentialPasswordView extends AuthCredentialView
- implements TextView.OnEditorActionListener {
+ implements TextView.OnEditorActionListener, OnApplyWindowInsetsListener, Dumpable {
private static final String TAG = "BiometricPrompt/AuthCredentialPasswordView";
private final InputMethodManager mImm;
private ImeAwareEditText mPasswordField;
+ private ViewGroup mAuthCredentialHeader;
+ private ViewGroup mAuthCredentialInput;
+ private int mBottomInset = 0;
public AuthCredentialPasswordView(Context context,
AttributeSet attrs) {
@@ -53,6 +67,9 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+
+ mAuthCredentialHeader = findViewById(R.id.auth_credential_header);
+ mAuthCredentialInput = findViewById(R.id.auth_credential_input);
mPasswordField = findViewById(R.id.lockPassword);
mPasswordField.setOnEditorActionListener(this);
// TODO: De-dupe the logic with AuthContainerView
@@ -66,6 +83,8 @@
}
return true;
});
+
+ setOnApplyWindowInsetsListener(this);
}
@Override
@@ -127,4 +146,65 @@
mPasswordField.setText("");
}
}
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+
+ if (mAuthCredentialInput == null || mAuthCredentialHeader == null
+ || mSubtitleView == null || mPasswordField == null || mErrorView == null) {
+ return;
+ }
+
+ // b/157910732 In AuthContainerView#getLayoutParams() we used to prevent jank risk when
+ // resizing by IME show or hide, we used to setFitInsetsTypes `~WindowInsets.Type.ime()` to
+ // LP. As a result this view needs to listen onApplyWindowInsets() and handle onLayout.
+ int inputLeftBound;
+ int inputTopBound;
+ int headerRightBound = right;
+ if (getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE) {
+ inputTopBound = (bottom - (mPasswordField.getHeight() + mErrorView.getHeight())) / 2;
+ inputLeftBound = (right - left) / 2;
+ headerRightBound = inputLeftBound;
+ } else {
+ inputTopBound = mSubtitleView.getBottom() + (bottom - mSubtitleView.getBottom()) / 2;
+ inputLeftBound = (right - left - mAuthCredentialInput.getWidth()) / 2;
+ }
+
+ mAuthCredentialHeader.layout(left, top, headerRightBound, bottom);
+ mAuthCredentialInput.layout(inputLeftBound, inputTopBound, right, bottom);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ final int newHeight = MeasureSpec.getSize(heightMeasureSpec) - mBottomInset;
+
+ setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), newHeight);
+
+ measureChildren(widthMeasureSpec,
+ MeasureSpec.makeMeasureSpec(newHeight, MeasureSpec.AT_MOST));
+ }
+
+ @NonNull
+ @Override
+ public WindowInsets onApplyWindowInsets(@NonNull View v, WindowInsets insets) {
+
+ final Insets bottomInset = insets.getInsets(ime());
+ if (v instanceof AuthCredentialPasswordView && mBottomInset != bottomInset.bottom) {
+ mBottomInset = bottomInset.bottom;
+ requestLayout();
+ }
+ return insets;
+ }
+
+ @Override
+ public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println(TAG + "State:");
+ pw.println(" mBottomInset=" + mBottomInset);
+ pw.println(" mAuthCredentialHeader size=(" + mAuthCredentialHeader.getWidth() + ","
+ + mAuthCredentialHeader.getHeight());
+ pw.println(" mAuthCredentialInput size=(" + mAuthCredentialInput.getWidth() + ","
+ + mAuthCredentialInput.getHeight());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index 4fa835e..d4176ac 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -87,7 +87,7 @@
private boolean mShouldAnimateContents;
private TextView mTitleView;
- private TextView mSubtitleView;
+ protected TextView mSubtitleView;
private TextView mDescriptionView;
private ImageView mIconView;
protected TextView mErrorView;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
index fef7383..1c57480 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
@@ -75,12 +75,12 @@
}
private var radius: Float = 0f
set(value) {
- rippleShader.radius = value
+ rippleShader.setMaxSize(value * 2f, value * 2f)
field = value
}
private var origin: PointF = PointF()
set(value) {
- rippleShader.origin = value
+ rippleShader.setCenter(value.x, value.y)
field = value
}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
index 8292e52..da675de 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
@@ -19,7 +19,6 @@
import android.content.Context
import android.content.res.Configuration
import android.graphics.PixelFormat
-import android.graphics.PointF
import android.os.SystemProperties
import android.util.DisplayMetrics
import android.view.View
@@ -85,7 +84,7 @@
private var debounceLevel = 0
@VisibleForTesting
- var rippleView: RippleView = RippleView(context, attrs = null)
+ var rippleView: RippleView = RippleView(context, attrs = null).also { it.setupShader() }
init {
pluggedIn = batteryController.isPluggedIn
@@ -177,20 +176,25 @@
context.display.getRealMetrics(displayMetrics)
val width = displayMetrics.widthPixels
val height = displayMetrics.heightPixels
- rippleView.radius = Integer.max(width, height).toFloat()
- rippleView.origin = when (RotationUtils.getExactRotation(context)) {
+ val maxDiameter = Integer.max(width, height) * 2f
+ rippleView.setMaxSize(maxDiameter, maxDiameter)
+ when (RotationUtils.getExactRotation(context)) {
RotationUtils.ROTATION_LANDSCAPE -> {
- PointF(width * normalizedPortPosY, height * (1 - normalizedPortPosX))
+ rippleView.setCenter(
+ width * normalizedPortPosY, height * (1 - normalizedPortPosX))
}
RotationUtils.ROTATION_UPSIDE_DOWN -> {
- PointF(width * (1 - normalizedPortPosX), height * (1 - normalizedPortPosY))
+ rippleView.setCenter(
+ width * (1 - normalizedPortPosX), height * (1 - normalizedPortPosY))
}
RotationUtils.ROTATION_SEASCAPE -> {
- PointF(width * (1 - normalizedPortPosY), height * normalizedPortPosX)
+ rippleView.setCenter(
+ width * (1 - normalizedPortPosY), height * normalizedPortPosX)
}
else -> {
// ROTATION_NONE
- PointF(width * normalizedPortPosX, height * normalizedPortPosY)
+ rippleView.setCenter(
+ width * normalizedPortPosX, height * normalizedPortPosY)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
index f6368ee..65400c2 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java
@@ -21,7 +21,6 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Color;
-import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
@@ -34,6 +33,7 @@
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.ripple.RippleShader;
import com.android.systemui.ripple.RippleView;
import java.text.NumberFormat;
@@ -138,6 +138,8 @@
animatorSetScrim.start();
mRippleView = findViewById(R.id.wireless_charging_ripple);
+ // TODO: Make rounded box shape if the device is tablet.
+ mRippleView.setupShader(RippleShader.RippleShape.CIRCLE);
OnAttachStateChangeListener listener = new OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View view) {
@@ -230,11 +232,11 @@
if (mRippleView != null) {
int width = getMeasuredWidth();
int height = getMeasuredHeight();
- mRippleView.setColor(
- Utils.getColorAttr(mRippleView.getContext(),
- android.R.attr.colorAccent).getDefaultColor());
- mRippleView.setOrigin(new PointF(width / 2, height / 2));
- mRippleView.setRadius(Math.max(width, height) * 0.5f);
+ mRippleView.setCenter(width * 0.5f, height * 0.5f);
+ float maxSize = Math.max(width, height);
+ mRippleView.setMaxSize(maxSize, maxSize);
+ mRippleView.setColor(Utils.getColorAttr(mRippleView.getContext(),
+ android.R.attr.colorAccent).getDefaultColor());
}
super.onLayout(changed, left, top, right, bottom);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 3d40e67..c92cf54 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -99,6 +99,12 @@
public static final BooleanFlag LOCKSCREEN_CUSTOM_CLOCKS = new BooleanFlag(207, false);
+ /**
+ * Flag to enable the usage of the new bouncer data source. This is a refactor of and
+ * eventual replacement of KeyguardBouncer.java.
+ */
+ public static final BooleanFlag MODERN_BOUNCER = new BooleanFlag(208, true);
+
/***************************************/
// 300 - power menu
public static final BooleanFlag POWER_MENU_LITE =
@@ -145,9 +151,6 @@
/***************************************/
// 600- status bar
- public static final BooleanFlag COMBINED_STATUS_BAR_SIGNAL_ICONS =
- new BooleanFlag(601, false);
-
public static final ResourceBooleanFlag STATUS_BAR_USER_SWITCHER =
new ResourceBooleanFlag(602, R.bool.flag_user_switcher_chip);
@@ -223,7 +226,11 @@
new SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false);
public static final BooleanFlag NEW_BACK_AFFORDANCE =
- new BooleanFlag(1203, false /* default */, true /* teamfood */);
+ new BooleanFlag(1203, false /* default */, false /* teamfood */);
+
+ // 1300 - screenshots
+
+ public static final BooleanFlag SCREENSHOT_REQUEST_PROCESSOR = new BooleanFlag(1300, false);
// Pay no attention to the reflection behind the curtain.
// ========================== Curtain ==========================
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 495f697..0f1ae00 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -19,7 +19,6 @@
import android.annotation.SuppressLint
import android.app.StatusBarManager
import android.content.Context
-import android.graphics.PointF
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.media.MediaRoute2Info
@@ -202,10 +201,10 @@
val height = windowBounds.height()
val width = windowBounds.width()
- rippleView.radius = height / 5f
+ val maxDiameter = height / 2.5f
+ rippleView.setMaxSize(maxDiameter, maxDiameter)
// Center the ripple on the bottom of the screen in the middle.
- rippleView.origin = PointF(/* x= */ width / 2f, /* y= */ height.toFloat())
-
+ rippleView.setCenter(width * 0.5f, height.toFloat())
val color = Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent)
val colorWithAlpha = ColorUtils.setAlphaComponent(color, 70)
rippleView.setColor(colorWithAlpha)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
index fed546bf..6a505f0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
@@ -23,10 +23,10 @@
/**
* An expanding ripple effect for the media tap-to-transfer receiver chip.
*/
-class ReceiverChipRippleView(
- context: Context?, attrs: AttributeSet?
-) : RippleView(context, attrs) {
+class ReceiverChipRippleView(context: Context?, attrs: AttributeSet?) : RippleView(context, attrs) {
init {
+ // TODO: use RippleShape#ELLIPSE when calling setupShader.
+ setupShader()
setRippleFill(true)
duration = 3000L
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 6bc50a6..da9fefa 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -48,6 +48,7 @@
import androidx.annotation.NonNull;
+import com.android.keyguard.KeyguardViewController;
import com.android.systemui.Dumpable;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
@@ -89,6 +90,7 @@
private final AccessibilityManager mAccessibilityManager;
private final Lazy<AssistManager> mAssistManagerLazy;
private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
+ private final KeyguardViewController mKeyguardViewController;
private final UserTracker mUserTracker;
private final SystemActions mSystemActions;
private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
@@ -123,6 +125,7 @@
OverviewProxyService overviewProxyService,
Lazy<AssistManager> assistManagerLazy,
Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
+ KeyguardViewController keyguardViewController,
NavigationModeController navigationModeController,
UserTracker userTracker,
DumpManager dumpManager) {
@@ -131,6 +134,7 @@
mAccessibilityManager = accessibilityManager;
mAssistManagerLazy = assistManagerLazy;
mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
+ mKeyguardViewController = keyguardViewController;
mUserTracker = userTracker;
mSystemActions = systemActions;
accessibilityManager.addAccessibilityServicesStateChangeListener(this);
@@ -317,8 +321,12 @@
* {@link InputMethodService} and the keyguard states.
*/
public boolean isImeShown(int vis) {
- View shadeWindowView = mCentralSurfacesOptionalLazy.get().get().getNotificationShadeWindowView();
- boolean isKeyguardShowing = mCentralSurfacesOptionalLazy.get().get().isKeyguardShowing();
+ View shadeWindowView = null;
+ if (mCentralSurfacesOptionalLazy.get().isPresent()) {
+ shadeWindowView =
+ mCentralSurfacesOptionalLazy.get().get().getNotificationShadeWindowView();
+ }
+ boolean isKeyguardShowing = mKeyguardViewController.isShowing();
boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
&& shadeWindowView.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
return imeVisibleOnShade
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index b05e75e..b44b4de 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -510,7 +510,6 @@
private fun playCommitBackAnimation() {
// Check if we should vibrate again
if (previousState != GestureState.FLUNG) {
- backCallback.triggerBack()
velocityTracker!!.computeCurrentVelocity(1000)
val isSlow = abs(velocityTracker!!.xVelocity) < 500
val hasNotVibratedRecently =
@@ -519,6 +518,10 @@
vibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK)
}
}
+ // Dispatch the actual back trigger
+ if (DEBUG) Log.d(TAG, "playCommitBackAnimation() invoked triggerBack() on backCallback")
+ backCallback.triggerBack()
+
playAnimation(setGoneEndListener)
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 057ed24..fc6dcd3 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -60,6 +60,7 @@
import com.android.internal.util.LatencyTracker;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
@@ -82,6 +83,7 @@
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
+import com.android.systemui.util.Assert;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
@@ -191,6 +193,7 @@
private final int mDisplayId;
private final Executor mMainExecutor;
+ private final Executor mBackgroundExecutor;
private final Rect mPipExcludedBounds = new Rect();
private final Rect mNavBarOverlayExcludedBounds = new Rect();
@@ -251,6 +254,7 @@
private BackGestureTfClassifierProvider mBackGestureTfClassifierProvider;
private Map<String, Integer> mVocab;
private boolean mUseMLModel;
+ private boolean mMLModelIsLoading;
// minimum width below which we do not run the model
private int mMLEnableWidth;
private float mMLModelThreshold;
@@ -318,6 +322,7 @@
SysUiState sysUiState,
PluginManager pluginManager,
@Main Executor executor,
+ @Background Executor backgroundExecutor,
BroadcastDispatcher broadcastDispatcher,
ProtoTracer protoTracer,
NavigationModeController navigationModeController,
@@ -334,6 +339,7 @@
mContext = context;
mDisplayId = context.getDisplayId();
mMainExecutor = executor;
+ mBackgroundExecutor = backgroundExecutor;
mOverviewProxyService = overviewProxyService;
mSysUiState = sysUiState;
mPluginManager = pluginManager;
@@ -631,28 +637,63 @@
return;
}
- if (newState) {
- mBackGestureTfClassifierProvider = mBackGestureTfClassifierProviderProvider.get();
- mMLModelThreshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f);
- if (mBackGestureTfClassifierProvider.isActive()) {
- Trace.beginSection("EdgeBackGestureHandler#loadVocab");
- mVocab = mBackGestureTfClassifierProvider.loadVocab(mContext.getAssets());
- Trace.endSection();
- mUseMLModel = true;
+ mUseMLModel = newState;
+
+ if (mUseMLModel) {
+ Assert.isMainThread();
+ if (mMLModelIsLoading) {
+ Log.d(TAG, "Model tried to load while already loading.");
return;
}
- }
-
- mUseMLModel = false;
- if (mBackGestureTfClassifierProvider != null) {
+ mMLModelIsLoading = true;
+ mBackgroundExecutor.execute(() -> loadMLModel());
+ } else if (mBackGestureTfClassifierProvider != null) {
mBackGestureTfClassifierProvider.release();
mBackGestureTfClassifierProvider = null;
+ mVocab = null;
}
}
+ private void loadMLModel() {
+ BackGestureTfClassifierProvider provider = mBackGestureTfClassifierProviderProvider.get();
+ float threshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f);
+ Map<String, Integer> vocab = null;
+ if (provider != null && !provider.isActive()) {
+ provider.release();
+ provider = null;
+ Log.w(TAG, "Cannot load model because it isn't active");
+ }
+ if (provider != null) {
+ Trace.beginSection("EdgeBackGestureHandler#loadVocab");
+ vocab = provider.loadVocab(mContext.getAssets());
+ Trace.endSection();
+ }
+ BackGestureTfClassifierProvider finalProvider = provider;
+ Map<String, Integer> finalVocab = vocab;
+ mMainExecutor.execute(() -> onMLModelLoadFinished(finalProvider, finalVocab, threshold));
+ }
+
+ private void onMLModelLoadFinished(BackGestureTfClassifierProvider provider,
+ Map<String, Integer> vocab, float threshold) {
+ Assert.isMainThread();
+ mMLModelIsLoading = false;
+ if (!mUseMLModel) {
+ // This can happen if the user disables Gesture Nav while the model is loading.
+ if (provider != null) {
+ provider.release();
+ }
+ Log.d(TAG, "Model finished loading but isn't needed.");
+ return;
+ }
+ mBackGestureTfClassifierProvider = provider;
+ mVocab = vocab;
+ mMLModelThreshold = threshold;
+ }
+
private int getBackGesturePredictionsCategory(int x, int y, int app) {
- if (app == -1) {
+ BackGestureTfClassifierProvider provider = mBackGestureTfClassifierProvider;
+ if (provider == null || app == -1) {
return -1;
}
int distanceFromEdge;
@@ -673,7 +714,7 @@
new long[]{(long) y},
};
- mMLResults = mBackGestureTfClassifierProvider.predict(featuresVector);
+ mMLResults = provider.predict(featuresVector);
if (mMLResults == -1) {
return -1;
}
@@ -1031,6 +1072,7 @@
private final SysUiState mSysUiState;
private final PluginManager mPluginManager;
private final Executor mExecutor;
+ private final Executor mBackgroundExecutor;
private final BroadcastDispatcher mBroadcastDispatcher;
private final ProtoTracer mProtoTracer;
private final NavigationModeController mNavigationModeController;
@@ -1050,6 +1092,7 @@
SysUiState sysUiState,
PluginManager pluginManager,
@Main Executor executor,
+ @Background Executor backgroundExecutor,
BroadcastDispatcher broadcastDispatcher,
ProtoTracer protoTracer,
NavigationModeController navigationModeController,
@@ -1067,6 +1110,7 @@
mSysUiState = sysUiState;
mPluginManager = pluginManager;
mExecutor = executor;
+ mBackgroundExecutor = backgroundExecutor;
mBroadcastDispatcher = broadcastDispatcher;
mProtoTracer = protoTracer;
mNavigationModeController = navigationModeController;
@@ -1089,6 +1133,7 @@
mSysUiState,
mPluginManager,
mExecutor,
+ mBackgroundExecutor,
mBroadcastDispatcher,
mProtoTracer,
mNavigationModeController,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
index ec0d081..eeb1010 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java
@@ -134,18 +134,9 @@
mQSCarrierGroupController
.setOnSingleCarrierChangedListener(mView::setIsSingleCarrier);
- List<String> rssiIgnoredSlots;
-
- if (mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
- rssiIgnoredSlots = List.of(
- getResources().getString(com.android.internal.R.string.status_bar_no_calling),
- getResources().getString(com.android.internal.R.string.status_bar_call_strength)
- );
- } else {
- rssiIgnoredSlots = List.of(
- getResources().getString(com.android.internal.R.string.status_bar_mobile)
- );
- }
+ List<String> rssiIgnoredSlots = List.of(
+ getResources().getString(com.android.internal.R.string.status_bar_mobile)
+ );
mView.onAttach(mIconManager, mQSExpansionPathInterpolator, rssiIgnoredSlots,
mInsetsProvider, mFeatureFlags.isEnabled(Flags.COMBINED_QS_HEADERS));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
index 2dac639..e925b54 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
@@ -27,7 +27,6 @@
@JvmField val contentDescription: String? = null,
@JvmField val typeContentDescription: String? = null,
@JvmField val roaming: Boolean = false,
- @JvmField val providerModelBehavior: Boolean = false
) {
/**
* Changes the visibility of this state by returning a copy with the visibility changed.
@@ -41,4 +40,4 @@
if (this.visible == visible) return this
else return copy(visible = visible)
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index 592da65..703b95a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -45,7 +45,7 @@
private View mSpacer;
@Nullable
private CellSignalState mLastSignalState;
- private boolean mProviderModelInitialized = false;
+ private boolean mMobileSignalInitialized = false;
private boolean mIsSingleCarrier;
public QSCarrier(Context context) {
@@ -96,35 +96,25 @@
mMobileRoaming.setImageTintList(colorStateList);
mMobileSignal.setImageTintList(colorStateList);
- if (state.providerModelBehavior) {
- if (!mProviderModelInitialized) {
- mProviderModelInitialized = true;
- mMobileSignal.setImageDrawable(
- mContext.getDrawable(R.drawable.ic_qs_no_calling_sms));
- }
- mMobileSignal.setImageDrawable(mContext.getDrawable(state.mobileSignalIconId));
- mMobileSignal.setContentDescription(state.contentDescription);
- } else {
- if (!mProviderModelInitialized) {
- mProviderModelInitialized = true;
- mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
- }
- mMobileSignal.setImageLevel(state.mobileSignalIconId);
- StringBuilder contentDescription = new StringBuilder();
- if (state.contentDescription != null) {
- contentDescription.append(state.contentDescription).append(", ");
- }
- if (state.roaming) {
- contentDescription
- .append(mContext.getString(R.string.data_connection_roaming))
- .append(", ");
- }
- // TODO: show mobile data off/no internet text for 5 seconds before carrier text
- if (hasValidTypeContentDescription(state.typeContentDescription)) {
- contentDescription.append(state.typeContentDescription);
- }
- mMobileSignal.setContentDescription(contentDescription);
+ if (!mMobileSignalInitialized) {
+ mMobileSignalInitialized = true;
+ mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
}
+ mMobileSignal.setImageLevel(state.mobileSignalIconId);
+ StringBuilder contentDescription = new StringBuilder();
+ if (state.contentDescription != null) {
+ contentDescription.append(state.contentDescription).append(", ");
+ }
+ if (state.roaming) {
+ contentDescription
+ .append(mContext.getString(R.string.data_connection_roaming))
+ .append(", ");
+ }
+ // TODO: show mobile data off/no internet text for 5 seconds before carrier text
+ if (hasValidTypeContentDescription(state.typeContentDescription)) {
+ contentDescription.append(state.typeContentDescription);
+ }
+ mMobileSignal.setContentDescription(contentDescription);
}
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index 209d09d..6a8bf75 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -42,10 +42,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.connectivity.SignalCallback;
@@ -78,7 +75,6 @@
private QSCarrier[] mCarrierGroups = new QSCarrier[SIM_SLOTS];
private int[] mLastSignalLevel = new int[SIM_SLOTS];
private String[] mLastSignalLevelDescription = new String[SIM_SLOTS];
- private final boolean mProviderModel;
private final CarrierConfigTracker mCarrierConfigTracker;
private boolean mIsSingleCarrier;
@@ -90,9 +86,6 @@
private final SignalCallback mSignalCallback = new SignalCallback() {
@Override
public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
- if (mProviderModel) {
- return;
- }
int slotIndex = getSlotIndex(indicators.subId);
if (slotIndex >= SIM_SLOTS) {
Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
@@ -107,91 +100,12 @@
indicators.statusIcon.icon,
indicators.statusIcon.contentDescription,
indicators.typeContentDescription.toString(),
- indicators.roaming,
- mProviderModel
+ indicators.roaming
);
mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
}
@Override
- public void setCallIndicator(@NonNull IconState statusIcon, int subId) {
- if (!mProviderModel) {
- return;
- }
- int slotIndex = getSlotIndex(subId);
- if (slotIndex >= SIM_SLOTS) {
- Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
- return;
- }
- if (slotIndex == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
- Log.e(TAG, "Invalid SIM slot index for subscription: " + subId);
- return;
- }
-
- boolean displayCallStrengthIcon =
- mCarrierConfigTracker.getCallStrengthConfig(subId);
-
- if (statusIcon.icon == R.drawable.ic_qs_no_calling_sms) {
- if (statusIcon.visible) {
- mInfos[slotIndex] = new CellSignalState(
- true,
- statusIcon.icon,
- statusIcon.contentDescription,
- "",
- false,
- mProviderModel);
- } else {
- // Whenever the no Calling & SMS state is cleared, switched to the last
- // known call strength icon.
- if (displayCallStrengthIcon) {
- mInfos[slotIndex] = new CellSignalState(
- true,
- mLastSignalLevel[slotIndex],
- mLastSignalLevelDescription[slotIndex],
- "",
- false,
- mProviderModel);
- } else {
- mInfos[slotIndex] = new CellSignalState(
- true,
- R.drawable.ic_qs_sim_card,
- "",
- "",
- false,
- mProviderModel);
- }
- }
- mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
- } else {
- mLastSignalLevel[slotIndex] = statusIcon.icon;
- mLastSignalLevelDescription[slotIndex] = statusIcon.contentDescription;
- // Only Shows the call strength icon when the no Calling & SMS icon is not
- // shown.
- if (mInfos[slotIndex].mobileSignalIconId
- != R.drawable.ic_qs_no_calling_sms) {
- if (displayCallStrengthIcon) {
- mInfos[slotIndex] = new CellSignalState(
- true,
- statusIcon.icon,
- statusIcon.contentDescription,
- "",
- false,
- mProviderModel);
- } else {
- mInfos[slotIndex] = new CellSignalState(
- true,
- R.drawable.ic_qs_sim_card,
- "",
- "",
- false,
- mProviderModel);
- }
- mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
- }
- }
- }
-
- @Override
public void setNoSims(boolean hasNoSims, boolean simDetected) {
if (hasNoSims) {
for (int i = 0; i < SIM_SLOTS; i++) {
@@ -219,14 +133,8 @@
@Background Handler bgHandler, @Main Looper mainLooper,
NetworkController networkController,
CarrierTextManager.Builder carrierTextManagerBuilder, Context context,
- CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags,
- SlotIndexResolver slotIndexResolver) {
+ CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver) {
- if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
- mProviderModel = true;
- } else {
- mProviderModel = false;
- }
mActivityStarter = activityStarter;
mBgHandler = bgHandler;
mNetworkController = networkController;
@@ -262,8 +170,7 @@
R.drawable.ic_qs_no_calling_sms,
context.getText(AccessibilityContentDescriptions.NO_CALLING).toString(),
"",
- false,
- mProviderModel);
+ false);
mLastSignalLevel[i] = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
mLastSignalLevelDescription[i] =
context.getText(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0])
@@ -351,8 +258,7 @@
for (int i = 0; i < SIM_SLOTS; i++) {
if (mInfos[i].visible
&& mInfos[i].mobileSignalIconId == R.drawable.ic_qs_sim_card) {
- mInfos[i] = new CellSignalState(true, R.drawable.ic_blank, "", "", false,
- mProviderModel);
+ mInfos[i] = new CellSignalState(true, R.drawable.ic_blank, "", "", false);
}
}
}
@@ -470,15 +376,13 @@
private final CarrierTextManager.Builder mCarrierTextControllerBuilder;
private final Context mContext;
private final CarrierConfigTracker mCarrierConfigTracker;
- private final FeatureFlags mFeatureFlags;
private final SlotIndexResolver mSlotIndexResolver;
@Inject
public Builder(ActivityStarter activityStarter, @Background Handler handler,
@Main Looper looper, NetworkController networkController,
CarrierTextManager.Builder carrierTextControllerBuilder, Context context,
- CarrierConfigTracker carrierConfigTracker, FeatureFlags featureFlags,
- SlotIndexResolver slotIndexResolver) {
+ CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver) {
mActivityStarter = activityStarter;
mHandler = handler;
mLooper = looper;
@@ -486,7 +390,6 @@
mCarrierTextControllerBuilder = carrierTextControllerBuilder;
mContext = context;
mCarrierConfigTracker = carrierConfigTracker;
- mFeatureFlags = featureFlags;
mSlotIndexResolver = slotIndexResolver;
}
@@ -498,7 +401,7 @@
public QSCarrierGroupController build() {
return new QSCarrierGroupController(mView, mActivityStarter, mHandler, mLooper,
mNetworkController, mCarrierTextControllerBuilder, mContext,
- mCarrierConfigTracker, mFeatureFlags, mSlotIndexResolver);
+ mCarrierConfigTracker, mSlotIndexResolver);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index f629a036..d5efe36 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -806,7 +806,8 @@
return;
}
- mTelephonyManager.setDataEnabled(enabled);
+ mTelephonyManager.setDataEnabledForReason(
+ TelephonyManager.DATA_ENABLED_REASON_USER, enabled);
if (disableOtherSubscriptions) {
final List<SubscriptionInfo> subInfoList =
mSubscriptionManager.getActiveSubscriptionInfoList();
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
index 93a2efc..0a8e6e2 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt
@@ -20,92 +20,107 @@
import android.util.MathUtils
/**
- * Shader class that renders an expanding charging ripple effect. A charging ripple contains
- * three elements:
- * 1. an expanding filled circle that appears in the beginning and quickly fades away
+ * Shader class that renders an expanding ripple effect. The ripple contains three elements:
+ *
+ * 1. an expanding filled [RippleShape] that appears in the beginning and quickly fades away
* 2. an expanding ring that appears throughout the effect
* 3. an expanding ring-shaped area that reveals noise over #2.
*
+ * The ripple shader will be default to the circle shape if not specified.
+ *
* Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java.
*/
-class RippleShader internal constructor() : RuntimeShader(SHADER) {
+class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.CIRCLE) :
+ RuntimeShader(buildShader(rippleShape)) {
+
+ /** Shapes that the [RippleShader] supports. */
+ enum class RippleShape {
+ CIRCLE,
+ ROUNDED_BOX,
+ ELLIPSE
+ }
+
companion object {
- private const val SHADER_UNIFORMS = """uniform vec2 in_origin;
+ private const val SHADER_UNIFORMS = """uniform vec2 in_center;
+ uniform vec2 in_size;
uniform float in_progress;
- uniform float in_maxRadius;
+ uniform float in_cornerRadius;
+ uniform float in_thickness;
uniform float in_time;
uniform float in_distort_radial;
uniform float in_distort_xy;
- uniform float in_radius;
uniform float in_fadeSparkle;
- uniform float in_fadeCircle;
+ uniform float in_fadeFill;
uniform float in_fadeRing;
uniform float in_blur;
uniform float in_pixelDensity;
layout(color) uniform vec4 in_color;
uniform float in_sparkle_strength;"""
- private const val SHADER_LIB = """float triangleNoise(vec2 n) {
- n = fract(n * vec2(5.3987, 5.4421));
- n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));
- float xy = n.x * n.y;
- return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
+
+ private const val SHADER_CIRCLE_MAIN = """vec4 main(vec2 p) {
+ vec2 p_distorted = distort(p, in_time, in_distort_radial, in_distort_xy);
+ float radius = in_size.x * 0.5;
+ float sparkleRing = soften(circleRing(p_distorted-in_center, radius), in_blur);
+ float inside = soften(sdCircle(p_distorted-in_center, radius * 1.2), in_blur);
+ float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175)
+ * (1.-sparkleRing) * in_fadeSparkle;
+
+ float rippleInsideAlpha = (1.-inside) * in_fadeFill;
+ float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing;
+ float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * 0.45;
+ vec4 ripple = in_color * rippleAlpha;
+ return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
+ }
+ """
+
+ private const val SHADER_ROUNDED_BOX_MAIN = """vec4 main(vec2 p) {
+ float sparkleRing = soften(roundedBoxRing(p-in_center, in_size, in_cornerRadius,
+ in_thickness), in_blur);
+ float inside = soften(sdRoundedBox(p-in_center, in_size * 1.2, in_cornerRadius),
+ in_blur);
+ float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175)
+ * (1.-sparkleRing) * in_fadeSparkle;
+
+ float rippleInsideAlpha = (1.-inside) * in_fadeFill;
+ float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing;
+ float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * 0.45;
+ vec4 ripple = in_color * rippleAlpha;
+ return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
+ }
+ """
+
+ private const val SHADER_ELLIPSE_MAIN = """vec4 main(vec2 p) {
+ vec2 p_distorted = distort(p, in_time, in_distort_radial, in_distort_xy);
+
+ float sparkleRing = soften(ellipseRing(p_distorted-in_center, in_size), in_blur);
+ float inside = soften(sdEllipse(p_distorted-in_center, in_size * 1.2), in_blur);
+ float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175)
+ * (1.-sparkleRing) * in_fadeSparkle;
+
+ float rippleInsideAlpha = (1.-inside) * in_fadeFill;
+ float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing;
+ float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * 0.45;
+ vec4 ripple = in_color * rippleAlpha;
+ return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
+ }
+ """
+
+ private const val CIRCLE_SHADER = SHADER_UNIFORMS + RippleShaderUtilLibrary.SHADER_LIB +
+ SdfShaderLibrary.SHADER_SDF_OPERATION_LIB + SdfShaderLibrary.CIRCLE_SDF +
+ SHADER_CIRCLE_MAIN
+ private const val ROUNDED_BOX_SHADER = SHADER_UNIFORMS +
+ RippleShaderUtilLibrary.SHADER_LIB + SdfShaderLibrary.SHADER_SDF_OPERATION_LIB +
+ SdfShaderLibrary.ROUNDED_BOX_SDF + SHADER_ROUNDED_BOX_MAIN
+ private const val ELLIPSE_SHADER = SHADER_UNIFORMS + RippleShaderUtilLibrary.SHADER_LIB +
+ SdfShaderLibrary.SHADER_SDF_OPERATION_LIB + SdfShaderLibrary.ELLIPSE_SDF +
+ SHADER_ELLIPSE_MAIN
+
+ private fun buildShader(rippleShape: RippleShape): String =
+ when (rippleShape) {
+ RippleShape.CIRCLE -> CIRCLE_SHADER
+ RippleShape.ROUNDED_BOX -> ROUNDED_BOX_SHADER
+ RippleShape.ELLIPSE -> ELLIPSE_SHADER
}
- const float PI = 3.1415926535897932384626;
-
- float threshold(float v, float l, float h) {
- return step(l, v) * (1.0 - step(h, v));
- }
-
- float sparkles(vec2 uv, float t) {
- float n = triangleNoise(uv);
- float s = 0.0;
- for (float i = 0; i < 4; i += 1) {
- float l = i * 0.01;
- float h = l + 0.1;
- float o = smoothstep(n - l, h, n);
- o *= abs(sin(PI * o * (t + 0.55 * i)));
- s += o;
- }
- return s;
- }
-
- float softCircle(vec2 uv, vec2 xy, float radius, float blur) {
- float blurHalf = blur * 0.5;
- float d = distance(uv, xy);
- return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius);
- }
-
- float softRing(vec2 uv, vec2 xy, float radius, float blur) {
- float thickness_half = radius * 0.25;
- float circle_outer = softCircle(uv, xy, radius + thickness_half, blur);
- float circle_inner = softCircle(uv, xy, radius - thickness_half, blur);
- return circle_outer - circle_inner;
- }
-
- vec2 distort(vec2 p, vec2 origin, float time,
- float distort_amount_radial, float distort_amount_xy) {
- float2 distance = origin - p;
- float angle = atan(distance.y, distance.x);
- return p + vec2(sin(angle * 8 + time * 0.003 + 1.641),
- cos(angle * 5 + 2.14 + time * 0.00412)) * distort_amount_radial
- + vec2(sin(p.x * 0.01 + time * 0.00215 + 0.8123),
- cos(p.y * 0.01 + time * 0.005931)) * distort_amount_xy;
- }"""
- private const val SHADER_MAIN = """vec4 main(vec2 p) {
- vec2 p_distorted = distort(p, in_origin, in_time, in_distort_radial,
- in_distort_xy);
-
- // Draw shapes
- float sparkleRing = softRing(p_distorted, in_origin, in_radius, in_blur);
- float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175)
- * sparkleRing * in_fadeSparkle;
- float circle = softCircle(p_distorted, in_origin, in_radius * 1.2, in_blur);
- float rippleAlpha = max(circle * in_fadeCircle,
- softRing(p_distorted, in_origin, in_radius, in_blur) * in_fadeRing) * 0.45;
- vec4 ripple = in_color * rippleAlpha;
- return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
- }"""
- private const val SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN
private fun subProgress(start: Float, end: Float, progress: Float): Float {
val min = Math.min(start, end)
@@ -116,22 +131,18 @@
}
/**
- * Maximum radius of the ripple.
+ * Sets the center position of the ripple.
*/
- var radius: Float = 0.0f
- set(value) {
- field = value
- setFloatUniform("in_maxRadius", value)
- }
+ fun setCenter(x: Float, y: Float) {
+ setFloatUniform("in_center", x, y)
+ }
- /**
- * Origin coordinate of the ripple.
- */
- var origin: PointF = PointF()
- set(value) {
- field = value
- setFloatUniform("in_origin", value.x, value.y)
- }
+ /** Max width of the ripple. */
+ private var maxSize: PointF = PointF()
+ fun setMaxSize(width: Float, height: Float) {
+ maxSize.x = width
+ maxSize.y = height
+ }
/**
* Progress of the ripple. Float value between [0, 1].
@@ -140,20 +151,27 @@
set(value) {
field = value
setFloatUniform("in_progress", value)
- setFloatUniform("in_radius",
- (1 - (1 - value) * (1 - value) * (1 - value))* radius)
+ val curvedProg = 1 - (1 - value) * (1 - value) * (1 - value)
+
+ setFloatUniform("in_size", /* width= */ maxSize.x * curvedProg,
+ /* height= */ maxSize.y * curvedProg)
+ setFloatUniform("in_thickness", maxSize.y * curvedProg * 0.5f)
+ // radius should not exceed width and height values.
+ setFloatUniform("in_cornerRadius",
+ Math.min(maxSize.x, maxSize.y) * curvedProg)
+
setFloatUniform("in_blur", MathUtils.lerp(1.25f, 0.5f, value))
val fadeIn = subProgress(0f, 0.1f, value)
val fadeOutNoise = subProgress(0.4f, 1f, value)
var fadeOutRipple = 0f
- var fadeCircle = 0f
+ var fadeFill = 0f
if (!rippleFill) {
- fadeCircle = subProgress(0f, 0.2f, value)
+ fadeFill = subProgress(0f, 0.6f, value)
fadeOutRipple = subProgress(0.3f, 1f, value)
}
setFloatUniform("in_fadeSparkle", Math.min(fadeIn, 1 - fadeOutNoise))
- setFloatUniform("in_fadeCircle", 1 - fadeCircle)
+ setFloatUniform("in_fadeFill", 1 - fadeFill)
setFloatUniform("in_fadeRing", Math.min(fadeIn, 1 - fadeOutRipple))
}
@@ -169,7 +187,7 @@
/**
* A hex value representing the ripple color, in the format of ARGB
*/
- var color: Int = 0xffffff.toInt()
+ var color: Int = 0xffffff
set(value) {
field = value
setColorUniform("in_color", value)
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt
new file mode 100644
index 0000000..0cacbc2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.ripple
+
+/** A common utility functions that are used for computing [RippleShader]. */
+class RippleShaderUtilLibrary {
+ companion object {
+ const val SHADER_LIB = """
+ float triangleNoise(vec2 n) {
+ n = fract(n * vec2(5.3987, 5.4421));
+ n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));
+ float xy = n.x * n.y;
+ return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
+ }
+ const float PI = 3.1415926535897932384626;
+
+ float sparkles(vec2 uv, float t) {
+ float n = triangleNoise(uv);
+ float s = 0.0;
+ for (float i = 0; i < 4; i += 1) {
+ float l = i * 0.01;
+ float h = l + 0.1;
+ float o = smoothstep(n - l, h, n);
+ o *= abs(sin(PI * o * (t + 0.55 * i)));
+ s += o;
+ }
+ return s;
+ }
+
+ vec2 distort(vec2 p, float time, float distort_amount_radial,
+ float distort_amount_xy) {
+ float angle = atan(p.y, p.x);
+ return p + vec2(sin(angle * 8 + time * 0.003 + 1.641),
+ cos(angle * 5 + 2.14 + time * 0.00412)) * distort_amount_radial
+ + vec2(sin(p.x * 0.01 + time * 0.00215 + 0.8123),
+ cos(p.y * 0.01 + time * 0.005931)) * distort_amount_xy;
+ }"""
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt b/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
index fc52464..83d9f2d 100644
--- a/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
+++ b/packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt
@@ -23,39 +23,42 @@
import android.content.res.Configuration
import android.graphics.Canvas
import android.graphics.Paint
-import android.graphics.PointF
import android.util.AttributeSet
import android.view.View
+import com.android.systemui.ripple.RippleShader.RippleShape
private const val RIPPLE_SPARKLE_STRENGTH: Float = 0.3f
+private const val RIPPLE_DEFAULT_COLOR: Int = 0xffffffff.toInt()
/**
- * A generic expanding ripple effect. To trigger the ripple expansion, set [radius] and [origin],
- * then call [startRipple].
+ * A generic expanding ripple effect.
+ *
+ * Set up the shader with a desired [RippleShape] using [setupShader], [setMaxSize] and [setCenter],
+ * then call [startRipple] to trigger the ripple expansion.
*/
open class RippleView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
- private val rippleShader = RippleShader()
- private val defaultColor: Int = 0xffffffff.toInt()
+
+ private lateinit var rippleShader: RippleShader
+ private lateinit var rippleShape: RippleShape
private val ripplePaint = Paint()
var rippleInProgress: Boolean = false
- var radius: Float = 0.0f
- set(value) {
- rippleShader.radius = value
- field = value
- }
- var origin: PointF = PointF()
- set(value) {
- rippleShader.origin = value
- field = value
- }
var duration: Long = 1750
- init {
- rippleShader.color = defaultColor
- rippleShader.progress = 0f
- rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
- ripplePaint.shader = rippleShader
+ private var maxWidth: Float = 0.0f
+ private var maxHeight: Float = 0.0f
+ fun setMaxSize(maxWidth: Float, maxHeight: Float) {
+ this.maxWidth = maxWidth
+ this.maxHeight = maxHeight
+ rippleShader.setMaxSize(maxWidth, maxHeight)
+ }
+
+ private var centerX: Float = 0.0f
+ private var centerY: Float = 0.0f
+ fun setCenter(x: Float, y: Float) {
+ this.centerX = x
+ this.centerY = y
+ rippleShader.setCenter(x, y)
}
override fun onConfigurationChanged(newConfig: Configuration?) {
@@ -68,6 +71,18 @@
super.onAttachedToWindow()
}
+ /** Initializes the shader. Must be called before [startRipple]. */
+ fun setupShader(rippleShape: RippleShape = RippleShape.CIRCLE) {
+ this.rippleShape = rippleShape
+ rippleShader = RippleShader(rippleShape)
+
+ rippleShader.color = RIPPLE_DEFAULT_COLOR
+ rippleShader.progress = 0f
+ rippleShader.sparkleStrength = RIPPLE_SPARKLE_STRENGTH
+
+ ripplePaint.shader = rippleShader
+ }
+
@JvmOverloads
fun startRipple(onAnimationEnd: Runnable? = null) {
if (rippleInProgress) {
@@ -113,11 +128,24 @@
// if it's unsupported.
return
}
- // To reduce overdraw, we mask the effect to a circle whose radius is big enough to cover
- // the active effect area. Values here should be kept in sync with the
- // animation implementation in the ripple shader.
- val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
- (1 - rippleShader.progress)) * radius * 2
- canvas.drawCircle(origin.x, origin.y, maskRadius, ripplePaint)
+ // To reduce overdraw, we mask the effect to a circle or a rectangle that's bigger than the
+ // active effect area. Values here should be kept in sync with the animation implementation
+ // in the ripple shader.
+ if (rippleShape == RippleShape.CIRCLE) {
+ val maskRadius = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
+ (1 - rippleShader.progress)) * maxWidth
+ canvas.drawCircle(centerX, centerY, maskRadius, ripplePaint)
+ } else {
+ val maskWidth = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
+ (1 - rippleShader.progress)) * maxWidth * 2
+ val maskHeight = (1 - (1 - rippleShader.progress) * (1 - rippleShader.progress) *
+ (1 - rippleShader.progress)) * maxHeight * 2
+ canvas.drawRect(
+ /* left= */ centerX - maskWidth,
+ /* top= */ centerY - maskHeight,
+ /* right= */ centerX + maskWidth,
+ /* bottom= */ centerY + maskHeight,
+ ripplePaint)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt b/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt
new file mode 100644
index 0000000..7f26146
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.ripple
+
+/** Library class that contains 2D signed distance functions. */
+class SdfShaderLibrary {
+ companion object {
+ const val CIRCLE_SDF = """
+ float sdCircle(vec2 p, float r) {
+ return (length(p)-r) / r;
+ }
+
+ float circleRing(vec2 p, float radius) {
+ float thicknessHalf = radius * 0.25;
+
+ float outerCircle = sdCircle(p, radius + thicknessHalf);
+ float innerCircle = sdCircle(p, radius);
+
+ return subtract(outerCircle, innerCircle);
+ }
+ """
+
+ const val ROUNDED_BOX_SDF = """
+ float sdRoundedBox(vec2 p, vec2 size, float cornerRadius) {
+ size *= 0.5;
+ cornerRadius *= 0.5;
+ vec2 d = abs(p)-size+cornerRadius;
+
+ float outside = length(max(d, 0.0));
+ float inside = min(max(d.x, d.y), 0.0);
+
+ return (outside+inside-cornerRadius)/size.y;
+ }
+
+ float roundedBoxRing(vec2 p, vec2 size, float cornerRadius,
+ float borderThickness) {
+ float outerRoundBox = sdRoundedBox(p, size, cornerRadius);
+ float innerRoundBox = sdRoundedBox(p, size - vec2(borderThickness),
+ cornerRadius - borderThickness);
+ return subtract(outerRoundBox, innerRoundBox);
+ }
+ """
+
+ // Used non-trigonometry parametrization and Halley's method (iterative) for root finding.
+ // This is more expensive than the regular circle SDF, recommend to use the circle SDF if
+ // possible.
+ const val ELLIPSE_SDF = """float sdEllipse(vec2 p, vec2 wh) {
+ wh *= 0.5;
+
+ // symmetry
+ (wh.x > wh.y) ? wh = wh.yx, p = abs(p.yx) : p = abs(p);
+
+ vec2 u = wh*p, v = wh*wh;
+
+ float U1 = u.y/2.0; float U5 = 4.0*U1;
+ float U2 = v.y-v.x; float U6 = 6.0*U1;
+ float U3 = u.x-U2; float U7 = 3.0*U3;
+ float U4 = u.x+U2;
+
+ float t = 0.5;
+ for (int i = 0; i < 3; i ++) {
+ float F1 = t*(t*t*(U1*t+U3)+U4)-U1;
+ float F2 = t*t*(U5*t+U7)+U4;
+ float F3 = t*(U6*t+U7);
+
+ t += (F1*F2)/(F1*F3-F2*F2);
+ }
+
+ t = clamp(t, 0.0, 1.0);
+
+ float d = distance(p, wh*vec2(1.0-t*t,2.0*t)/(t*t+1.0));
+ d /= wh.y;
+
+ return (dot(p/wh,p/wh)>1.0) ? d : -d;
+ }
+
+ float ellipseRing(vec2 p, vec2 wh) {
+ vec2 thicknessHalf = wh * 0.25;
+
+ float outerEllipse = sdEllipse(p, wh + thicknessHalf);
+ float innerEllipse = sdEllipse(p, wh);
+
+ return subtract(outerEllipse, innerEllipse);
+ }
+ """
+
+ const val SHADER_SDF_OPERATION_LIB = """
+ float soften(float d, float blur) {
+ float blurHalf = blur * 0.5;
+ return smoothstep(-blurHalf, blurHalf, d);
+ }
+
+ float subtract(float outer, float inner) {
+ return max(outer, -inner);
+ }
+ """
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt
new file mode 100644
index 0000000..39f35a5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCapture.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.screenshot
+
+import android.graphics.Bitmap
+import android.graphics.Rect
+
+interface ImageCapture {
+
+ fun captureDisplay(displayId: Int, crop: Rect? = null): Bitmap?
+
+ fun captureTask(taskId: Int): Bitmap?
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
new file mode 100644
index 0000000..258c436
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.app.IActivityTaskManager
+import android.graphics.Bitmap
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.os.IBinder
+import android.util.Log
+import android.view.DisplayAddress
+import android.view.SurfaceControl
+import android.view.SurfaceControl.DisplayCaptureArgs
+import android.view.SurfaceControl.ScreenshotHardwareBuffer
+import androidx.annotation.VisibleForTesting
+import javax.inject.Inject
+
+private const val TAG = "ImageCaptureImpl"
+
+open class ImageCaptureImpl @Inject constructor(
+ private val displayManager: DisplayManager,
+ private val atmService: IActivityTaskManager
+) : ImageCapture {
+
+ override fun captureDisplay(displayId: Int, crop: Rect?): Bitmap? {
+ val width = crop?.width() ?: 0
+ val height = crop?.height() ?: 0
+ val sourceCrop = crop ?: Rect()
+ val displayToken = physicalDisplayToken(displayId) ?: return null
+ val buffer = captureDisplay(displayToken, width, height, sourceCrop)
+
+ return buffer?.asBitmap()
+ }
+
+ override fun captureTask(taskId: Int): Bitmap? {
+ val snapshot = atmService.takeTaskSnapshot(taskId)
+ return Bitmap.wrapHardwareBuffer(snapshot.hardwareBuffer, snapshot.colorSpace)
+ }
+
+ @VisibleForTesting
+ open fun physicalDisplayToken(displayId: Int): IBinder? {
+ val display = displayManager.getDisplay(displayId)
+ if (display == null) {
+ Log.e(TAG, "No display with id: $displayId")
+ return null
+ }
+ val address = display.address
+ if (address !is DisplayAddress.Physical) {
+ Log.e(TAG, "Display does not have a physical address: $display")
+ return null
+ }
+ return SurfaceControl.getPhysicalDisplayToken(address.physicalDisplayId)
+ }
+
+ @VisibleForTesting
+ open fun captureDisplay(displayToken: IBinder, width: Int, height: Int, crop: Rect): ScreenshotHardwareBuffer? {
+ val captureArgs = DisplayCaptureArgs.Builder(displayToken)
+ .setSize(width, height)
+ .setSourceCrop(crop)
+ .build()
+ return SurfaceControl.captureDisplay(captureArgs)
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
new file mode 100644
index 0000000..beb54c8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.net.Uri
+import android.util.Log
+import android.view.WindowManager.ScreenshotType
+import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
+import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
+import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION
+import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
+import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
+import java.util.function.Consumer
+import javax.inject.Inject
+
+/**
+ * Processes a screenshot request sent from {@link ScreenshotHelper}.
+ */
+@SysUISingleton
+internal class RequestProcessor @Inject constructor(
+ private val controller: ScreenshotController,
+) {
+ fun processRequest(
+ @ScreenshotType type: Int,
+ onSavedListener: Consumer<Uri>,
+ request: ScreenshotRequest,
+ callback: RequestCallback
+ ) {
+
+ if (type == TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+ val image = HardwareBitmapBundler.bundleToHardwareBitmap(request.bitmapBundle)
+
+ controller.handleImageAsScreenshot(
+ image, request.boundsInScreen, request.insets,
+ request.taskId, request.userId, request.topComponent, onSavedListener, callback
+ )
+ return
+ }
+
+ when (type) {
+ TAKE_SCREENSHOT_FULLSCREEN ->
+ controller.takeScreenshotFullscreen(null, onSavedListener, callback)
+ TAKE_SCREENSHOT_SELECTED_REGION ->
+ controller.takeScreenshotPartial(null, onSavedListener, callback)
+ else -> Log.w(TAG, "Invalid screenshot option: $type")
+ }
+ }
+
+ companion object {
+ const val TAG: String = "RequestProcessor"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 82de389..69ee8e8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -57,14 +57,12 @@
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
-import android.os.IBinder;
import android.os.RemoteException;
import android.provider.Settings;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
import android.view.Display;
-import android.view.DisplayAddress;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.KeyEvent;
@@ -72,7 +70,6 @@
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.ScrollCaptureResponse;
-import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
@@ -249,6 +246,7 @@
private final ScreenshotSmartActions mScreenshotSmartActions;
private final UiEventLogger mUiEventLogger;
private final ImageExporter mImageExporter;
+ private final ImageCapture mImageCapture;
private final Executor mMainExecutor;
private final ExecutorService mBgExecutor;
private final BroadcastSender mBroadcastSender;
@@ -295,6 +293,7 @@
ScrollCaptureClient scrollCaptureClient,
UiEventLogger uiEventLogger,
ImageExporter imageExporter,
+ ImageCapture imageCapture,
@Main Executor mainExecutor,
ScrollCaptureController scrollCaptureController,
LongScreenshotData longScreenshotHolder,
@@ -308,6 +307,7 @@
mScrollCaptureClient = scrollCaptureClient;
mUiEventLogger = uiEventLogger;
mImageExporter = imageExporter;
+ mImageCapture = imageCapture;
mMainExecutor = mainExecutor;
mScrollCaptureController = scrollCaptureController;
mLongScreenshotHolder = longScreenshotHolder;
@@ -531,7 +531,7 @@
// copy the input Rect, since SurfaceControl.screenshot can mutate it
Rect screenRect = new Rect(crop);
- Bitmap screenshot = captureScreenshot(crop);
+ Bitmap screenshot = mImageCapture.captureDisplay(DEFAULT_DISPLAY, crop);
if (screenshot == null) {
Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
@@ -549,32 +549,6 @@
ClipboardOverlayController.SELF_PERMISSION);
}
- private Bitmap captureScreenshot(Rect crop) {
- int width = crop.width();
- int height = crop.height();
- Bitmap screenshot = null;
- final Display display = getDefaultDisplay();
- final DisplayAddress address = display.getAddress();
- if (!(address instanceof DisplayAddress.Physical)) {
- Log.e(TAG, "Skipping Screenshot - Default display does not have a physical address: "
- + display);
- } else {
- final DisplayAddress.Physical physicalAddress = (DisplayAddress.Physical) address;
-
- final IBinder displayToken = SurfaceControl.getPhysicalDisplayToken(
- physicalAddress.getPhysicalDisplayId());
- final SurfaceControl.DisplayCaptureArgs captureArgs =
- new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
- .setSourceCrop(crop)
- .setSize(width, height)
- .build();
- final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
- SurfaceControl.captureDisplay(captureArgs);
- screenshot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
- }
- return screenshot;
- }
-
private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
Insets screenInsets, ComponentName topComponent, boolean showFlash) {
withWindowAttached(() ->
@@ -720,7 +694,7 @@
mScreenshotView.showScrollChip(response.getPackageName(), /* onClick */ () -> {
DisplayMetrics displayMetrics = new DisplayMetrics();
getDefaultDisplay().getRealMetrics(displayMetrics);
- Bitmap newScreenshot = captureScreenshot(
+ Bitmap newScreenshot = mImageCapture.captureDisplay(DEFAULT_DISPLAY,
new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));
mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot,
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 32d8203..f1f0223 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -21,6 +21,7 @@
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_PROCESS_COMPLETE;
import static com.android.internal.util.ScreenshotHelper.SCREENSHOT_MSG_URI;
+import static com.android.systemui.flags.Flags.SCREENSHOT_REQUEST_PROCESSOR;
import static com.android.systemui.screenshot.LogConfig.DEBUG_CALLBACK;
import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE;
@@ -57,6 +58,8 @@
import com.android.internal.util.ScreenshotHelper;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.FlagListenable.FlagEvent;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -75,6 +78,8 @@
private final Handler mHandler;
private final Context mContext;
private final @Background Executor mBgExecutor;
+ private final RequestProcessor mProcessor;
+ private final FeatureFlags mFeatureFlags;
private final BroadcastReceiver mCloseSystemDialogs = new BroadcastReceiver() {
@Override
@@ -104,7 +109,8 @@
public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager,
DevicePolicyManager devicePolicyManager, UiEventLogger uiEventLogger,
ScreenshotNotificationsController notificationsController, Context context,
- @Background Executor bgExecutor) {
+ @Background Executor bgExecutor, FeatureFlags featureFlags,
+ RequestProcessor processor) {
if (DEBUG_SERVICE) {
Log.d(TAG, "new " + this);
}
@@ -116,6 +122,9 @@
mNotificationsController = notificationsController;
mContext = context;
mBgExecutor = bgExecutor;
+ mFeatureFlags = featureFlags;
+ mFeatureFlags.addListener(SCREENSHOT_REQUEST_PROCESSOR, FlagEvent::requestNoRestart);
+ mProcessor = processor;
}
@Override
@@ -218,6 +227,12 @@
mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()), 0,
topComponent == null ? "" : topComponent.getPackageName());
+ if (mFeatureFlags.isEnabled(SCREENSHOT_REQUEST_PROCESSOR)) {
+ Log.d(TAG, "handleMessage: Using request processor");
+ mProcessor.processRequest(msg.what, uriConsumer, screenshotRequest, requestCallback);
+ return true;
+ }
+
switch (msg.what) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
if (DEBUG_SERVICE) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
index 91ef3c3..3e442587 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/dagger/ScreenshotModule.java
@@ -18,6 +18,8 @@
import android.app.Service;
+import com.android.systemui.screenshot.ImageCapture;
+import com.android.systemui.screenshot.ImageCaptureImpl;
import com.android.systemui.screenshot.TakeScreenshotService;
import dagger.Binds;
@@ -37,4 +39,6 @@
@ClassKey(TakeScreenshotService.class)
public abstract Service bindTakeScreenshotService(TakeScreenshotService service);
+ @Binds
+ public abstract ImageCapture bindImageCapture(ImageCaptureImpl capture);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 5793105..0f9ac36 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -264,14 +264,8 @@
Utils.getColorAttrDefaultColor(header.context, android.R.attr.textColorPrimary)
)
- carrierIconSlots = if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
- listOf(
- header.context.getString(com.android.internal.R.string.status_bar_no_calling),
- header.context.getString(com.android.internal.R.string.status_bar_call_strength)
- )
- } else {
+ carrierIconSlots =
listOf(header.context.getString(com.android.internal.R.string.status_bar_mobile))
- }
qsCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(qsCarrierGroup)
.build()
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 58e9bb8..7f0e76b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -44,7 +44,6 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
-import android.app.ActivityManager;
import android.app.Fragment;
import android.app.StatusBarManager;
import android.content.ContentResolver;
@@ -288,7 +287,6 @@
private final NotificationPanelView mView;
private final VibratorHelper mVibratorHelper;
private final MetricsLogger mMetricsLogger;
- private final ActivityManager mActivityManager;
private final ConfigurationController mConfigurationController;
private final Provider<FlingAnimationUtils.Builder> mFlingAnimationUtilsBuilder;
private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
@@ -341,14 +339,14 @@
private final RecordingController mRecordingController;
private final PanelEventsEmitter mPanelEventsEmitter;
private boolean mSplitShadeEnabled;
- // The bottom padding reserved for elements of the keyguard measuring notifications
+ /** The bottom padding reserved for elements of the keyguard measuring notifications. */
private float mKeyguardNotificationBottomPadding;
/**
* The top padding from where notification should start in lockscreen.
* Should be static also during animations and should match the Y of the first notification.
*/
private float mKeyguardNotificationTopPadding;
- // Current max allowed keyguard notifications determined by measuring the panel
+ /** Current max allowed keyguard notifications determined by measuring the panel. */
private int mMaxAllowedKeyguardNotifications;
private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController;
@@ -728,7 +726,6 @@
AccessibilityManager accessibilityManager, @DisplayId int displayId,
KeyguardUpdateMonitor keyguardUpdateMonitor,
MetricsLogger metricsLogger,
- ActivityManager activityManager,
ConfigurationController configurationController,
Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilder,
StatusBarTouchableRegionManager statusBarTouchableRegionManager,
@@ -798,7 +795,6 @@
panelExpansionStateManager,
ambientState,
interactionJankMonitor,
- keyguardUnlockAnimationController,
systemClock);
mView = view;
mVibratorHelper = vibratorHelper;
@@ -808,7 +804,6 @@
mQRCodeScannerController = qrCodeScannerController;
mControlsComponent = controlsComponent;
mMetricsLogger = metricsLogger;
- mActivityManager = activityManager;
mConfigurationController = configurationController;
mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
mMediaHierarchyManager = mediaHierarchyManager;
@@ -1504,10 +1499,7 @@
}
private void updateKeyguardStatusViewAlignment(boolean animate) {
- boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
- .getVisibleNotificationCount() != 0
- || mMediaDataManager.hasActiveMediaOrRecommendation();
- boolean shouldBeCentered = !mSplitShadeEnabled || !hasVisibleNotifications || mDozing;
+ boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
if (mStatusViewCentered != shouldBeCentered) {
mStatusViewCentered = shouldBeCentered;
ConstraintSet constraintSet = new ConstraintSet();
@@ -1531,6 +1523,15 @@
mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(mStatusViewCentered));
}
+ private boolean shouldKeyguardStatusViewBeCentered() {
+ boolean hasVisibleNotifications = mNotificationStackScrollLayoutController
+ .getVisibleNotificationCount() != 0
+ || mMediaDataManager.hasActiveMediaOrRecommendation();
+ boolean isOnAod = mDozing && mDozeParameters.getAlwaysOn();
+ return !mSplitShadeEnabled || !hasVisibleNotifications || isOnAod
+ || hasPulsingNotifications();
+ }
+
/**
* @return the padding of the stackscroller when unlocked
*/
@@ -3969,7 +3970,7 @@
* notification data being displayed. In the new notification pipeline, this is handled in
* {@link ShadeViewManager}.
*/
- public void updateNotificationViews(String reason) {
+ public void updateNotificationViews() {
mNotificationStackScrollLayoutController.updateFooter();
mNotificationIconAreaController.updateNotificationIcons(createVisibleEntriesList());
@@ -4426,7 +4427,7 @@
NotificationStackScrollLayout.OnEmptySpaceClickListener {
@Override
public void onEmptySpaceClicked(float x, float y) {
- onEmptySpaceClick(x);
+ onEmptySpaceClick();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index 15e1129..1d92105 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -89,7 +89,6 @@
Dumpable, ConfigurationListener {
private static final String TAG = "NotificationShadeWindowController";
- private static final boolean DEBUG = false;
private final Context mContext;
private final WindowManager mWindowManager;
@@ -190,7 +189,7 @@
return;
}
}
- mCallbacks.add(new WeakReference<StatusBarWindowCallback>(callback));
+ mCallbacks.add(new WeakReference<>(callback));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
index 121d69d..e52170e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java
@@ -18,6 +18,8 @@
import static android.view.WindowInsets.Type.systemBars;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
+
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
import android.annotation.LayoutRes;
@@ -52,14 +54,12 @@
import com.android.internal.view.FloatingActionMode;
import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.CentralSurfaces;
/**
* Combined keyguard and notification panel view. Also holding backdrop and scrims.
*/
public class NotificationShadeWindowView extends FrameLayout {
public static final String TAG = "NotificationShadeWindowView";
- public static final boolean DEBUG = CentralSurfaces.DEBUG;
private int mRightInset = 0;
private int mLeftInset = 0;
@@ -221,7 +221,7 @@
}
}
- class LayoutParams extends FrameLayout.LayoutParams {
+ private static class LayoutParams extends FrameLayout.LayoutParams {
public boolean ignoreRightInset;
@@ -243,7 +243,7 @@
public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback,
int type) {
if (type == ActionMode.TYPE_FLOATING) {
- return startActionMode(originalView, callback, type);
+ return startActionMode(originalView, callback);
}
return super.startActionModeForChild(originalView, callback, type);
}
@@ -258,14 +258,10 @@
final FloatingActionMode mode =
new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
mFloatingActionModeOriginatingView = originatingView;
- mFloatingToolbarPreDrawListener =
- new ViewTreeObserver.OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- mode.updateViewLocationInWindow();
- return true;
- }
- };
+ mFloatingToolbarPreDrawListener = () -> {
+ mode.updateViewLocationInWindow();
+ return true;
+ };
return mode;
}
@@ -292,10 +288,10 @@
}
private ActionMode startActionMode(
- View originatingView, ActionMode.Callback callback, int type) {
+ View originatingView, ActionMode.Callback callback) {
ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
ActionMode mode = createFloatingActionMode(originatingView, wrappedCallback);
- if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
+ if (wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
setHandledFloatingActionMode(mode);
} else {
mode = null;
@@ -382,7 +378,7 @@
/**
* Minimal window to satisfy FloatingToolbar.
*/
- private Window mFakeWindow = new Window(mContext) {
+ private final Window mFakeWindow = new Window(mContext) {
@Override
public void takeSurface(SurfaceHolder.Callback2 callback) {
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
index 587e0e6d..02316b7 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java
@@ -48,13 +48,12 @@
private View mStackScroller;
private View mKeyguardStatusBar;
- private ArrayList<View> mDrawingOrderedChildren = new ArrayList<>();
- private ArrayList<View> mLayoutDrawingOrder = new ArrayList<>();
+ private final ArrayList<View> mDrawingOrderedChildren = new ArrayList<>();
+ private final ArrayList<View> mLayoutDrawingOrder = new ArrayList<>();
private final Comparator<View> mIndexComparator = Comparator.comparingInt(this::indexOfChild);
private Consumer<WindowInsets> mInsetsChangedListener = insets -> {};
private Consumer<QS> mQSFragmentAttachedListener = qs -> {};
private QS mQs;
- private View mQSScrollView;
private View mQSContainer;
@Nullable
@@ -76,7 +75,6 @@
public void onFragmentViewCreated(String tag, Fragment fragment) {
mQs = (QS) fragment;
mQSFragmentAttachedListener.accept(mQs);
- mQSScrollView = mQs.getView().findViewById(R.id.expanded_qs_scroll_view);
mQSContainer = mQs.getView().findViewById(R.id.quick_settings_container);
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PanelView.java b/packages/SystemUI/src/com/android/systemui/shade/PanelView.java
index 1082967..efff0db 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/PanelView.java
@@ -49,10 +49,6 @@
super(context, attrs, defStyleAttr);
}
- public PanelView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
public void setOnTouchListener(PanelViewController.TouchHandler touchHandler) {
super.setOnTouchListener(touchHandler);
mTouchHandler = touchHandler;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
index 229acf4..4aad245 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
@@ -19,11 +19,11 @@
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
-import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE;
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
import static com.android.systemui.classifier.Classifier.GENERIC;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.UNLOCK;
+import static com.android.systemui.shade.PanelView.DEBUG;
import static java.lang.Float.isNaN;
@@ -53,7 +53,6 @@
import com.android.systemui.animation.Interpolators;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.doze.DozeLog;
-import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
@@ -77,7 +76,6 @@
import java.util.List;
public abstract class PanelViewController {
- public static final boolean DEBUG = PanelView.DEBUG;
public static final String TAG = PanelView.class.getSimpleName();
public static final float FLING_MAX_LENGTH_SECONDS = 0.6f;
public static final float FLING_SPEED_UP_FACTOR = 0.6f;
@@ -97,7 +95,7 @@
protected boolean mTouchSlopExceededBeforeDown;
private float mMinExpandHeight;
private boolean mPanelUpdateWhenAnimatorEnds;
- private boolean mVibrateOnOpening;
+ private final boolean mVibrateOnOpening;
protected boolean mIsLaunchAnimationRunning;
private int mFixedDuration = NO_FIXED_DURATION;
protected float mOverExpansion;
@@ -144,7 +142,6 @@
private int mTouchSlop;
private float mSlopMultiplier;
protected boolean mHintAnimationRunning;
- private boolean mOverExpandedBeforeFling;
private boolean mTouchAboveFalsingThreshold;
private int mUnlockFalsingThreshold;
private boolean mTouchStartedInEmptyArea;
@@ -155,9 +152,9 @@
private ValueAnimator mHeightAnimator;
private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
- private FlingAnimationUtils mFlingAnimationUtils;
- private FlingAnimationUtils mFlingAnimationUtilsClosing;
- private FlingAnimationUtils mFlingAnimationUtilsDismissing;
+ private final FlingAnimationUtils mFlingAnimationUtils;
+ private final FlingAnimationUtils mFlingAnimationUtilsClosing;
+ private final FlingAnimationUtils mFlingAnimationUtilsDismissing;
private final LatencyTracker mLatencyTracker;
private final FalsingManager mFalsingManager;
private final DozeLog mDozeLog;
@@ -174,13 +171,14 @@
private float mInitialTouchY;
private float mInitialTouchX;
private boolean mTouchDisabled;
+ private boolean mInitialTouchFromKeyguard;
/**
* Whether or not the PanelView can be expanded or collapsed with a drag.
*/
- private boolean mNotificationsDragEnabled;
+ private final boolean mNotificationsDragEnabled;
- private Interpolator mBounceInterpolator;
+ private final Interpolator mBounceInterpolator;
protected KeyguardBottomAreaView mKeyguardBottomArea;
/**
@@ -201,7 +199,6 @@
protected final AmbientState mAmbientState;
protected final LockscreenGestureLogger mLockscreenGestureLogger;
private final PanelExpansionStateManager mPanelExpansionStateManager;
- private final TouchHandler mTouchHandler;
private final InteractionJankMonitor mInteractionJankMonitor;
protected final SystemClock mSystemClock;
@@ -229,8 +226,6 @@
return mAmbientState;
}
- private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
-
public PanelViewController(
PanelView view,
FalsingManager falsingManager,
@@ -247,9 +242,7 @@
PanelExpansionStateManager panelExpansionStateManager,
AmbientState ambientState,
InteractionJankMonitor interactionJankMonitor,
- KeyguardUnlockAnimationController keyguardUnlockAnimationController,
SystemClock systemClock) {
- mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onKeyguardFadingAwayChanged() {
@@ -261,7 +254,7 @@
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mLockscreenGestureLogger = lockscreenGestureLogger;
mPanelExpansionStateManager = panelExpansionStateManager;
- mTouchHandler = createTouchHandler();
+ TouchHandler touchHandler = createTouchHandler();
mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
@@ -274,7 +267,7 @@
});
mView.addOnLayoutChangeListener(createLayoutChangeListener());
- mView.setOnTouchListener(mTouchHandler);
+ mView.setOnTouchListener(touchHandler);
mView.setOnConfigurationChangedListener(createOnConfigurationChangedListener());
mResources = mView.getResources();
@@ -398,11 +391,12 @@
public void startExpandMotion(float newX, float newY, boolean startTracking,
float expandedHeight) {
if (!mHandlingPointerUp && !mStatusBarStateController.isDozing()) {
- beginJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ beginJankMonitoring();
}
mInitialOffsetOnTouch = expandedHeight;
mInitialTouchY = newY;
mInitialTouchX = newX;
+ mInitialTouchFromKeyguard = mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
if (startTracking) {
mTouchSlopExceeded = true;
setExpandedHeight(mInitialOffsetOnTouch);
@@ -425,20 +419,14 @@
mStatusBarStateController.getState() == StatusBarState.KEYGUARD;
final boolean expand;
- if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
- // If the keyguard is fading away, don't expand it again. This can happen if you're
- // swiping to unlock, the app below the keyguard is in landscape, and the screen
- // rotates while your finger is still down after the swipe to unlock.
- if (mKeyguardStateController.isKeyguardFadingAway()) {
- expand = false;
- } else if (onKeyguard) {
+ if (mKeyguardStateController.isKeyguardFadingAway()
+ || (mInitialTouchFromKeyguard && !onKeyguard)) {
+ // Don't expand for any touches that started from the keyguard and ended after the
+ // keyguard is gone.
+ expand = false;
+ } else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
+ if (onKeyguard) {
expand = true;
- } else if (mKeyguardStateController.isKeyguardFadingAway()) {
- // If we're in the middle of dismissing the keyguard, don't expand due to the
- // cancelled gesture. Gesture cancellation during an unlock is expected in some
- // situations, such keeping your finger down while swiping to unlock to an app
- // that is locked in landscape (the rotation will cancel the touch event).
- expand = false;
} else if (mCentralSurfaces.isBouncerShowingOverDream()) {
expand = false;
} else {
@@ -475,7 +463,7 @@
} else if (!mCentralSurfaces.isBouncerShowing()
&& !mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()
&& !mKeyguardStateController.isKeyguardGoingAway()) {
- boolean expands = onEmptySpaceClick(mInitialTouchX);
+ boolean expands = onEmptySpaceClick();
onTrackingStopped(expands);
}
mVelocityTracker.clear();
@@ -670,7 +658,7 @@
@Override
public void onAnimationStart(Animator animation) {
if (!mStatusBarStateController.isDozing()) {
- beginJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ beginJankMonitoring();
}
}
@@ -702,10 +690,8 @@
mIsSpringBackAnimation = true;
ValueAnimator animator = ValueAnimator.ofFloat(mOverExpansion, 0);
animator.addUpdateListener(
- animation -> {
- setOverExpansionInternal((float) animation.getAnimatedValue(),
- false /* isFromGesture */);
- });
+ animation -> setOverExpansionInternal((float) animation.getAnimatedValue(),
+ false /* isFromGesture */));
animator.setDuration(SHADE_OPEN_SPRING_BACK_DURATION);
animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
animator.addListener(new AnimatorListenerAdapter() {
@@ -731,10 +717,10 @@
setAnimator(null);
mKeyguardStateController.notifyPanelFlingEnd();
if (!cancelled) {
- endJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ endJankMonitoring();
notifyExpandingFinished();
} else {
- cancelJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ cancelJankMonitoring();
}
updatePanelExpansionAndVisibility();
}
@@ -773,13 +759,6 @@
setExpandedHeight(currentMaxPanelHeight);
}
- private float getStackHeightFraction(float height) {
- final float gestureFraction = height / getMaxPanelHeight();
- final float stackHeightFraction = Interpolators.ACCELERATE_DECELERATE
- .getInterpolation(gestureFraction);
- return stackHeightFraction;
- }
-
public void setExpandedHeightInternal(float h) {
if (isNaN(h)) {
Log.wtf(TAG, "ExpandedHeight set to NaN");
@@ -911,13 +890,8 @@
return !isFullyCollapsed() && !mTracking && !mClosing;
}
- private final Runnable mFlingCollapseRunnable = new Runnable() {
- @Override
- public void run() {
- fling(0, false /* expand */, mNextCollapseSpeedUpFactor,
- false /* expandBecauseOfFalsing */);
- }
- };
+ private final Runnable mFlingCollapseRunnable = () -> fling(0, false /* expand */,
+ mNextCollapseSpeedUpFactor, false /* expandBecauseOfFalsing */);
public void expand(final boolean animate) {
if (!isFullyCollapsed() && !isCollapsing()) {
@@ -950,7 +924,7 @@
mView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
if (mAnimateAfterExpanding) {
notifyExpandingStarted();
- beginJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ beginJankMonitoring();
fling(0, true /* expand */);
} else {
setExpandedFraction(1f);
@@ -1150,7 +1124,7 @@
*
* @return whether the panel will be expanded after the action performed by this method
*/
- protected boolean onEmptySpaceClick(float x) {
+ protected boolean onEmptySpaceClick() {
if (mHintAnimationRunning) {
return true;
}
@@ -1432,9 +1406,9 @@
// mHeightAnimator is null, there is no remaining frame, ends instrumenting.
if (mHeightAnimator == null) {
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- endJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ endJankMonitoring();
} else {
- cancelJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
+ cancelJankMonitoring();
}
}
break;
@@ -1465,28 +1439,32 @@
}
}
- private void beginJankMonitoring(int cuj) {
+ private void beginJankMonitoring() {
if (mInteractionJankMonitor == null) {
return;
}
InteractionJankMonitor.Configuration.Builder builder =
- InteractionJankMonitor.Configuration.Builder.withView(cuj, mView)
+ InteractionJankMonitor.Configuration.Builder.withView(
+ InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
+ mView)
.setTag(isFullyCollapsed() ? "Expand" : "Collapse");
mInteractionJankMonitor.begin(builder);
}
- private void endJankMonitoring(int cuj) {
+ private void endJankMonitoring() {
if (mInteractionJankMonitor == null) {
return;
}
- InteractionJankMonitor.getInstance().end(cuj);
+ InteractionJankMonitor.getInstance().end(
+ InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
}
- private void cancelJankMonitoring(int cuj) {
+ private void cancelJankMonitoring() {
if (mInteractionJankMonitor == null) {
return;
}
- InteractionJankMonitor.getInstance().cancel(cuj);
+ InteractionJankMonitor.getInstance().cancel(
+ InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE);
}
protected float getExpansionFraction() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index 3013ad0..a57d849b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -61,21 +61,19 @@
private int mVisibleState = -1;
private DualToneHandler mDualToneHandler;
private boolean mForceHidden;
- private boolean mProviderModel;
/**
* Designated constructor
*/
public static StatusBarMobileView fromContext(
Context context,
- String slot,
- boolean providerModel
+ String slot
) {
LayoutInflater inflater = LayoutInflater.from(context);
StatusBarMobileView v = (StatusBarMobileView)
inflater.inflate(R.layout.status_bar_mobile_signal_group, null);
v.setSlot(slot);
- v.init(providerModel);
+ v.init();
v.setVisibleState(STATE_ICON);
return v;
}
@@ -108,17 +106,12 @@
outRect.bottom += translationY;
}
- private void init(boolean providerModel) {
- mProviderModel = providerModel;
+ private void init() {
mDualToneHandler = new DualToneHandler(getContext());
mMobileGroup = findViewById(R.id.mobile_group);
mMobile = findViewById(R.id.mobile_signal);
mMobileType = findViewById(R.id.mobile_type);
- if (mProviderModel) {
- mMobileRoaming = findViewById(R.id.mobile_roaming_large);
- } else {
- mMobileRoaming = findViewById(R.id.mobile_roaming);
- }
+ mMobileRoaming = findViewById(R.id.mobile_roaming);
mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space);
mIn = findViewById(R.id.mobile_in);
mOut = findViewById(R.id.mobile_out);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
index 6914ae6..1638780 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
@@ -21,6 +21,7 @@
import android.telephony.SubscriptionInfo;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.connectivity.NetworkController.EmergencyListener;
@@ -36,6 +37,7 @@
* Implements network listeners and forwards the calls along onto other listeners but on
* the current or specified Looper.
*/
+@SysUISingleton
public class CallbackHandler extends Handler implements EmergencyListener, SignalCallback {
private static final String TAG = "CallbackHandler";
private static final int MSG_EMERGENCE_CHANGED = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index f2014e9..ec221b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -26,25 +26,17 @@
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings.Global;
-import android.telephony.AccessNetworkConstants;
import android.telephony.CellSignalStrength;
import android.telephony.CellSignalStrengthCdma;
-import android.telephony.ServiceState;
import android.telephony.SignalStrength;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
-import android.telephony.ims.ImsException;
-import android.telephony.ims.ImsMmTelManager;
-import android.telephony.ims.ImsReasonInfo;
-import android.telephony.ims.ImsRegistrationAttributes;
-import android.telephony.ims.RegistrationManager.RegistrationCallback;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.settingslib.AccessibilityContentDescriptions;
import com.android.settingslib.SignalIcon.MobileIconGroup;
import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.MobileMappings.Config;
@@ -54,8 +46,6 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.SignalStrengthUtil;
import com.android.systemui.R;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.util.CarrierConfigTracker;
import java.io.PrintWriter;
@@ -70,33 +60,22 @@
public class MobileSignalController extends SignalController<MobileState, MobileIconGroup> {
private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
private static final int STATUS_HISTORY_SIZE = 64;
- private static final int IMS_TYPE_WWAN = 1;
- private static final int IMS_TYPE_WLAN = 2;
- private static final int IMS_TYPE_WLAN_CROSS_SIM = 3;
private final TelephonyManager mPhone;
private final CarrierConfigTracker mCarrierConfigTracker;
- private final ImsMmTelManager mImsMmTelManager;
private final SubscriptionDefaults mDefaults;
private final String mNetworkNameDefault;
private final String mNetworkNameSeparator;
private final ContentObserver mObserver;
- private final boolean mProviderModelBehavior;
- private final Handler mReceiverHandler;
- private int mImsType = IMS_TYPE_WWAN;
// Save entire info for logging, we only use the id.
final SubscriptionInfo mSubscriptionInfo;
private Map<String, MobileIconGroup> mNetworkToIconLookup;
- private int mLastLevel;
private MobileIconGroup mDefaultIcons;
private Config mConfig;
@VisibleForTesting
boolean mInflateSignalStrengths = false;
- private int mLastWwanLevel;
- private int mLastWlanLevel;
- private int mLastWlanCrossSimLevel;
@VisibleForTesting
- MobileStatusTracker mMobileStatusTracker;
+ final MobileStatusTracker mMobileStatusTracker;
// Save the previous STATUS_HISTORY_SIZE states for logging.
private final String[] mMobileStatusHistory = new String[STATUS_HISTORY_SIZE];
@@ -133,52 +112,6 @@
}
};
- private final RegistrationCallback mRegistrationCallback = new RegistrationCallback() {
- @Override
- public void onRegistered(ImsRegistrationAttributes attributes) {
- Log.d(mTag, "onRegistered: " + "attributes=" + attributes);
- int imsTransportType = attributes.getTransportType();
- int registrationAttributes = attributes.getAttributeFlags();
- if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WWAN) {
- mImsType = IMS_TYPE_WWAN;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
- getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- } else if (imsTransportType == AccessNetworkConstants.TRANSPORT_TYPE_WLAN) {
- if (registrationAttributes == 0) {
- mImsType = IMS_TYPE_WLAN;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
- getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- } else if (registrationAttributes
- == ImsRegistrationAttributes.ATTR_EPDG_OVER_CELL_INTERNET) {
- mImsType = IMS_TYPE_WLAN_CROSS_SIM;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
- getCallStrengthDescription(
- mLastWlanCrossSimLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
- }
- }
-
- @Override
- public void onUnregistered(ImsReasonInfo info) {
- Log.d(mTag, "onDeregistered: " + "info=" + info);
- mImsType = IMS_TYPE_WWAN;
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
- getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
- };
-
// TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
// need listener lists anymore.
public MobileSignalController(
@@ -192,7 +125,7 @@
SubscriptionDefaults defaults,
Looper receiverLooper,
CarrierConfigTracker carrierConfigTracker,
- FeatureFlags featureFlags
+ MobileStatusTrackerFactory mobileStatusTrackerFactory
) {
super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
@@ -206,7 +139,6 @@
R.string.status_bar_network_name_separator).toString();
mNetworkNameDefault = getTextIfExists(
com.android.internal.R.string.lockscreen_carrier_default).toString();
- mReceiverHandler = new Handler(receiverLooper);
mNetworkToIconLookup = mapIconSets(mConfig);
mDefaultIcons = getDefaultIcons(mConfig);
@@ -223,10 +155,7 @@
updateTelephony();
}
};
- mImsMmTelManager = ImsMmTelManager.createForSubscriptionId(info.getSubscriptionId());
- mMobileStatusTracker = new MobileStatusTracker(mPhone, receiverLooper,
- info, mDefaults, mMobileCallback);
- mProviderModelBehavior = featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS);
+ mMobileStatusTracker = mobileStatusTrackerFactory.createTracker(mMobileCallback);
}
void setConfiguration(Config config) {
@@ -271,41 +200,14 @@
mContext.getContentResolver().registerContentObserver(Global.getUriFor(
Global.MOBILE_DATA + mSubscriptionInfo.getSubscriptionId()),
true, mObserver);
- if (mProviderModelBehavior) {
- mReceiverHandler.post(mTryRegisterIms);
- }
}
- // There is no listener to monitor whether the IMS service is ready, so we have to retry the
- // IMS registration.
- private final Runnable mTryRegisterIms = new Runnable() {
- private static final int MAX_RETRY = 12;
- private int mRetryCount;
-
- @Override
- public void run() {
- try {
- mRetryCount++;
- mImsMmTelManager.registerImsRegistrationCallback(
- mReceiverHandler::post, mRegistrationCallback);
- Log.d(mTag, "registerImsRegistrationCallback succeeded");
- } catch (RuntimeException | ImsException e) {
- if (mRetryCount < MAX_RETRY) {
- Log.e(mTag, mRetryCount + " registerImsRegistrationCallback failed", e);
- // Wait for 5 seconds to retry
- mReceiverHandler.postDelayed(mTryRegisterIms, 5000);
- }
- }
- }
- };
-
/**
* Stop listening for phone state changes.
*/
public void unregisterListener() {
mMobileStatusTracker.setListening(false);
mContext.getContentResolver().unregisterContentObserver(mObserver);
- mImsMmTelManager.unregisterImsRegistrationCallback(mRegistrationCallback);
}
private void updateInflateSignalStrength() {
@@ -394,7 +296,7 @@
CharSequence qsDescription = null;
if (mCurrentState.dataSim) {
- // If using provider model behavior, only show QS icons if the state is also default
+ // only show QS icons if the state is also default
if (!mCurrentState.isDefault) {
return new QsInfo(qsTypeIcon, qsIcon, qsDescription);
}
@@ -416,32 +318,15 @@
private SbInfo getSbInfo(String contentDescription, int dataTypeIcon) {
final boolean dataDisabled = mCurrentState.isDataDisabledOrNotDefault();
- boolean showTriangle = false;
- int typeIcon = 0;
- IconState statusIcon = null;
+ IconState statusIcon = new IconState(
+ mCurrentState.enabled && !mCurrentState.airplaneMode,
+ getCurrentIconId(), contentDescription);
- if (mProviderModelBehavior) {
- boolean showDataIconStatusBar = (mCurrentState.dataConnected || dataDisabled)
- && (mCurrentState.dataSim && mCurrentState.isDefault);
- typeIcon =
- (showDataIconStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
- showDataIconStatusBar |= mCurrentState.roaming;
- statusIcon = new IconState(
- showDataIconStatusBar && !mCurrentState.airplaneMode,
- getCurrentIconId(), contentDescription);
-
- showTriangle = showDataIconStatusBar && !mCurrentState.airplaneMode;
- } else {
- statusIcon = new IconState(
- mCurrentState.enabled && !mCurrentState.airplaneMode,
- getCurrentIconId(), contentDescription);
-
- boolean showDataIconInStatusBar =
- (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled;
- typeIcon =
- (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
- showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
- }
+ boolean showDataIconInStatusBar =
+ (mCurrentState.dataConnected && mCurrentState.isDefault) || dataDisabled;
+ int typeIcon =
+ (showDataIconInStatusBar || mConfig.alwaysShowDataRatIcon) ? dataTypeIcon : 0;
+ boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
return new SbInfo(showTriangle, typeIcon, statusIcon);
}
@@ -560,144 +445,7 @@
}
private void updateMobileStatus(MobileStatus mobileStatus) {
- int lastVoiceState = mCurrentState.getVoiceServiceState();
mCurrentState.setFromMobileStatus(mobileStatus);
-
- notifyMobileLevelChangeIfNecessary(mobileStatus.signalStrength);
- if (mProviderModelBehavior) {
- maybeNotifyCallStateChanged(lastVoiceState);
- }
- }
-
- /** Call state changed is only applicable when provider model behavior is true */
- private void maybeNotifyCallStateChanged(int lastVoiceState) {
- int currentVoiceState = mCurrentState.getVoiceServiceState();
- if (lastVoiceState == currentVoiceState) {
- return;
- }
- // Only update the no calling Status in the below scenarios
- // 1. The first valid voice state has been received
- // 2. The voice state has been changed and either the last or current state is
- // ServiceState.STATE_IN_SERVICE
- if (lastVoiceState == -1
- || (lastVoiceState == ServiceState.STATE_IN_SERVICE
- || currentVoiceState == ServiceState.STATE_IN_SERVICE)) {
- boolean isNoCalling = mCurrentState.isNoCalling();
- isNoCalling &= !hideNoCalling();
- IconState statusIcon = new IconState(isNoCalling,
- R.drawable.ic_qs_no_calling_sms,
- getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
- }
-
- void updateNoCallingState() {
- int currentVoiceState = mCurrentState.getVoiceServiceState();
- boolean isNoCalling = currentVoiceState != ServiceState.STATE_IN_SERVICE;
- isNoCalling &= !hideNoCalling();
- IconState statusIcon = new IconState(isNoCalling,
- R.drawable.ic_qs_no_calling_sms,
- getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
-
- private boolean hideNoCalling() {
- return mNetworkController.hasDefaultNetwork()
- && mCarrierConfigTracker.getNoCallingConfig(mSubscriptionInfo.getSubscriptionId());
- }
-
- private int getCallStrengthIcon(int level, boolean isWifi) {
- return isWifi ? TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[level]
- : TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[level];
- }
-
- private String getCallStrengthDescription(int level, boolean isWifi) {
- return isWifi
- ? getTextIfExists(AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[level])
- .toString()
- : getTextIfExists(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[level])
- .toString();
- }
-
- void refreshCallIndicator(SignalCallback callback) {
- boolean isNoCalling = mCurrentState.isNoCalling();
- isNoCalling &= !hideNoCalling();
- IconState statusIcon = new IconState(isNoCalling,
- R.drawable.ic_qs_no_calling_sms,
- getTextIfExists(AccessibilityContentDescriptions.NO_CALLING).toString());
- callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
-
- switch (mImsType) {
- case IMS_TYPE_WWAN:
- statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWwanLevel, /* isWifi= */false),
- getCallStrengthDescription(mLastWwanLevel, /* isWifi= */false));
- break;
- case IMS_TYPE_WLAN:
- statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWlanLevel, /* isWifi= */true),
- getCallStrengthDescription(mLastWlanLevel, /* isWifi= */true));
- break;
- case IMS_TYPE_WLAN_CROSS_SIM:
- statusIcon = new IconState(
- true,
- getCallStrengthIcon(mLastWlanCrossSimLevel, /* isWifi= */false),
- getCallStrengthDescription(mLastWlanCrossSimLevel, /* isWifi= */false));
- }
- callback.setCallIndicator(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
-
- void notifyWifiLevelChange(int level) {
- if (!mProviderModelBehavior) {
- return;
- }
- mLastWlanLevel = level;
- if (mImsType != IMS_TYPE_WLAN) {
- return;
- }
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(level, /* isWifi= */true),
- getCallStrengthDescription(level, /* isWifi= */true));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
-
- void notifyDefaultMobileLevelChange(int level) {
- if (!mProviderModelBehavior) {
- return;
- }
- mLastWlanCrossSimLevel = level;
- if (mImsType != IMS_TYPE_WLAN_CROSS_SIM) {
- return;
- }
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(level, /* isWifi= */false),
- getCallStrengthDescription(level, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
-
- void notifyMobileLevelChangeIfNecessary(SignalStrength signalStrength) {
- if (!mProviderModelBehavior) {
- return;
- }
- int newLevel = getSignalLevel(signalStrength);
- if (newLevel != mLastLevel) {
- mLastLevel = newLevel;
- mLastWwanLevel = newLevel;
- if (mImsType == IMS_TYPE_WWAN) {
- IconState statusIcon = new IconState(
- true,
- getCallStrengthIcon(newLevel, /* isWifi= */false),
- getCallStrengthDescription(newLevel, /* isWifi= */false));
- notifyCallStateChange(statusIcon, mSubscriptionInfo.getSubscriptionId());
- }
- if (mCurrentState.dataSim) {
- mNetworkController.notifyDefaultMobileLevelChange(newLevel);
- }
- }
}
int getSignalLevel(SignalStrength signalStrength) {
@@ -801,19 +549,14 @@
mMobileStatusHistoryIndex = (mMobileStatusHistoryIndex + 1) % STATUS_HISTORY_SIZE;
}
- @VisibleForTesting
- void setImsType(int imsType) {
- mImsType = imsType;
- }
-
@Override
public void dump(PrintWriter pw) {
super.dump(pw);
pw.println(" mSubscription=" + mSubscriptionInfo + ",");
- pw.println(" mProviderModelBehavior=" + mProviderModelBehavior + ",");
pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
pw.println(" isDataDisabled=" + isDataDisabled() + ",");
pw.println(" mNetworkToIconLookup=" + mNetworkToIconLookup + ",");
+ pw.println(" mMobileStatusTracker.isListening=" + mMobileStatusTracker.isListening());
pw.println(" MobileStatusHistory");
int size = 0;
for (int i = 0; i < STATUS_HISTORY_SIZE; i++) {
@@ -843,6 +586,11 @@
icon = iconState;
description = desc;
}
+
+ @Override
+ public String toString() {
+ return "QsInfo: ratTypeIcon=" + ratTypeIcon + " icon=" + icon;
+ }
}
/** Box for status bar icon info */
@@ -856,5 +604,11 @@
ratTypeIcon = typeIcon;
icon = iconState;
}
+
+ @Override
+ public String toString() {
+ return "SbInfo: showTriangle=" + showTriangle + " ratTypeIcon=" + ratTypeIcon
+ + " icon=" + icon;
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
new file mode 100644
index 0000000..7938179
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.connectivity
+
+import android.content.Context
+import android.os.Looper
+import android.telephony.SubscriptionInfo
+import android.telephony.TelephonyManager
+import com.android.settingslib.mobile.MobileMappings
+import com.android.settingslib.mobile.MobileStatusTracker
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.util.CarrierConfigTracker
+import javax.inject.Inject
+
+/**
+ * Factory to make MobileSignalController injectable
+ */
+@SysUISingleton
+internal class MobileSignalControllerFactory @Inject constructor(
+ val context: Context,
+ val callbackHandler: CallbackHandler,
+ val carrierConfigTracker: CarrierConfigTracker,
+) {
+ fun createMobileSignalController(
+ config: MobileMappings.Config,
+ hasMobileData: Boolean,
+ phone: TelephonyManager,
+ networkController: NetworkControllerImpl,
+ subscriptionInfo: SubscriptionInfo,
+ subscriptionDefaults: MobileStatusTracker.SubscriptionDefaults,
+ receiverLooper: Looper,
+ ): MobileSignalController {
+ val mobileTrackerFactory = MobileStatusTrackerFactory(
+ phone,
+ receiverLooper,
+ subscriptionInfo,
+ subscriptionDefaults)
+
+ return MobileSignalController(
+ context,
+ config,
+ hasMobileData,
+ phone,
+ callbackHandler,
+ networkController,
+ subscriptionInfo,
+ subscriptionDefaults,
+ receiverLooper,
+ carrierConfigTracker,
+ mobileTrackerFactory,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt
new file mode 100644
index 0000000..a4c1a198
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.connectivity
+
+import android.os.Looper
+import android.telephony.SubscriptionInfo
+import android.telephony.TelephonyManager
+import com.android.settingslib.mobile.MobileStatusTracker
+
+/**
+ * Factory for [MobileStatusTracker], which lives in SettingsLib
+ */
+class MobileStatusTrackerFactory (
+ val phone: TelephonyManager,
+ val receiverLooper: Looper,
+ val info: SubscriptionInfo,
+ val defaults: MobileStatusTracker.SubscriptionDefaults,
+) {
+ fun createTracker(
+ callback: MobileStatusTracker.Callback
+ ): MobileStatusTracker {
+ return MobileStatusTracker(
+ phone,
+ receiverLooper,
+ info,
+ defaults,
+ callback)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 268531d..ea7ec4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -71,8 +71,6 @@
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.LogLevel;
import com.android.systemui.log.dagger.StatusBarNetworkControllerLog;
@@ -133,12 +131,11 @@
private final BroadcastDispatcher mBroadcastDispatcher;
private final DemoModeController mDemoModeController;
private final Object mLock = new Object();
- private final boolean mProviderModelBehavior;
private Config mConfig;
private final CarrierConfigTracker mCarrierConfigTracker;
- private final FeatureFlags mFeatureFlags;
private final DumpManager mDumpManager;
private final LogBuffer mLogBuffer;
+ private final MobileSignalControllerFactory mMobileFactory;
private TelephonyCallback.ActiveDataSubscriptionIdListener mPhoneStateListener;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -234,9 +231,9 @@
DemoModeController demoModeController,
CarrierConfigTracker carrierConfigTracker,
WifiStatusTrackerFactory trackerFactory,
+ MobileSignalControllerFactory mobileFactory,
@Main Handler handler,
InternetDialogFactory internetDialogFactory,
- FeatureFlags featureFlags,
DumpManager dumpManager,
@StatusBarNetworkControllerLog LogBuffer logBuffer) {
this(context, connectivityManager,
@@ -256,8 +253,8 @@
demoModeController,
carrierConfigTracker,
trackerFactory,
+ mobileFactory,
handler,
- featureFlags,
dumpManager,
logBuffer);
mReceiverHandler.post(mRegisterListeners);
@@ -282,8 +279,8 @@
DemoModeController demoModeController,
CarrierConfigTracker carrierConfigTracker,
WifiStatusTrackerFactory trackerFactory,
+ MobileSignalControllerFactory mobileFactory,
@Main Handler handler,
- FeatureFlags featureFlags,
DumpManager dumpManager,
LogBuffer logBuffer
) {
@@ -297,6 +294,7 @@
mCallbackHandler = callbackHandler;
mDataSaverController = new DataSaverControllerImpl(context);
mBroadcastDispatcher = broadcastDispatcher;
+ mMobileFactory = mobileFactory;
mSubscriptionManager = subManager;
mSubDefaults = defaultsHandler;
@@ -304,7 +302,6 @@
mHasMobileDataFeature = telephonyManager.isDataCapable();
mDemoModeController = demoModeController;
mCarrierConfigTracker = carrierConfigTracker;
- mFeatureFlags = featureFlags;
mDumpManager = dumpManager;
mLogBuffer = logBuffer;
@@ -456,7 +453,6 @@
};
mDemoModeController.addCallback(this);
- mProviderModelBehavior = mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS);
mDumpManager.registerDumpable(TAG, this);
}
@@ -497,16 +493,16 @@
// broadcasts
IntentFilter filter = new IntentFilter();
- filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
- filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
- filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
- filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
- filter.addAction(Intent.ACTION_SERVICE_STATE);
- filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
+ filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
+ filter.addAction(Intent.ACTION_SERVICE_STATE);
+ filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
filter.addAction(Settings.Panel.ACTION_INTERNET_CONNECTIVITY);
+ filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+ filter.addAction(TelephonyManager.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
+ filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
mBroadcastDispatcher.registerReceiverWithHandler(this, filter, mReceiverHandler);
mListening = true;
@@ -659,20 +655,6 @@
return controller != null ? controller.getNetworkNameForCarrierWiFi() : "";
}
- void notifyWifiLevelChange(int level) {
- for (int i = 0; i < mMobileSignalControllers.size(); i++) {
- MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
- mobileSignalController.notifyWifiLevelChange(level);
- }
- }
-
- void notifyDefaultMobileLevelChange(int level) {
- for (int i = 0; i < mMobileSignalControllers.size(); i++) {
- MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
- mobileSignalController.notifyDefaultMobileLevelChange(level);
- }
- }
-
private void notifyControllersMobileDataChanged() {
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
@@ -745,9 +727,6 @@
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
mobileSignalController.notifyListeners(cb);
- if (mProviderModelBehavior) {
- mobileSignalController.refreshCallIndicator(cb);
- }
}
mCallbackHandler.setListening(cb, true);
}
@@ -862,9 +841,6 @@
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
MobileSignalController controller = mMobileSignalControllers.valueAt(i);
controller.setConfiguration(mConfig);
- if (mProviderModelBehavior) {
- controller.refreshCallIndicator(mCallbackHandler);
- }
}
refreshLocale();
}
@@ -981,11 +957,15 @@
mMobileSignalControllers.put(subId, cachedControllers.get(subId));
cachedControllers.remove(subId);
} else {
- MobileSignalController controller = new MobileSignalController(mContext, mConfig,
- mHasMobileDataFeature, mPhone.createForSubscriptionId(subId),
- mCallbackHandler, this, subscriptions.get(i),
- mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker,
- mFeatureFlags);
+ MobileSignalController controller = mMobileFactory.createMobileSignalController(
+ mConfig,
+ mHasMobileDataFeature,
+ mPhone.createForSubscriptionId(subId),
+ this,
+ subscriptions.get(i),
+ mSubDefaults,
+ mReceiverHandler.getLooper()
+ );
controller.setUserSetupComplete(mUserSetup);
mMobileSignalControllers.put(subId, controller);
if (subscriptions.get(i).getSimSlotIndex() == 0) {
@@ -1139,24 +1119,11 @@
|| mValidatedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
pushConnectivityToSignals();
- if (mProviderModelBehavior) {
- mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
- && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
- && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
- mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
- mNoNetworksAvailable);
- for (int i = 0; i < mMobileSignalControllers.size(); i++) {
- MobileSignalController mobileSignalController = mMobileSignalControllers.valueAt(i);
- mobileSignalController.updateNoCallingState();
- }
- notifyAllListeners();
- } else {
- mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
- && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
- && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
- mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
- mNoNetworksAvailable);
- }
+ mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
+ && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
+ && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
+ mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
+ mNoNetworksAvailable);
}
/**
@@ -1346,7 +1313,7 @@
mMobileSignalControllers.clear();
int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax();
for (int i = start /* get out of normal index range */; i < start + num; i++) {
- subs.add(addSignalController(i, i));
+ subs.add(addDemoModeSignalController(i, i));
}
mCallbackHandler.setSubs(subs);
for (int i = 0; i < mMobileSignalControllers.size(); i++) {
@@ -1372,7 +1339,7 @@
List<SubscriptionInfo> subs = new ArrayList<>();
while (mMobileSignalControllers.size() <= slot) {
int nextSlot = mMobileSignalControllers.size();
- subs.add(addSignalController(nextSlot, nextSlot));
+ subs.add(addDemoModeSignalController(nextSlot, nextSlot));
}
if (!subs.isEmpty()) {
mCallbackHandler.setSubs(subs);
@@ -1462,14 +1429,20 @@
mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
}
- private SubscriptionInfo addSignalController(int id, int simSlotIndex) {
+ private SubscriptionInfo addDemoModeSignalController(int id, int simSlotIndex) {
SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0,
null, null, null, "", false, null, null);
- MobileSignalController controller = new MobileSignalController(mContext,
- mConfig, mHasMobileDataFeature,
- mPhone.createForSubscriptionId(info.getSubscriptionId()), mCallbackHandler, this,
- info, mSubDefaults, mReceiverHandler.getLooper(), mCarrierConfigTracker,
- mFeatureFlags);
+
+ MobileSignalController controller = mMobileFactory.createMobileSignalController(
+ mConfig,
+ mHasMobileDataFeature,
+ mPhone.createForSubscriptionId(info.getSubscriptionId()),
+ this,
+ info,
+ mSubDefaults,
+ mReceiverHandler.getLooper()
+ );
+
mMobileSignalControllers.put(id, controller);
controller.getState().userSetup = true;
return info;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index 87cdb17..12f2c22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -222,7 +222,6 @@
mCurrentState.connected = mWifiTracker.connected;
mCurrentState.ssid = mWifiTracker.ssid;
mCurrentState.rssi = mWifiTracker.rssi;
- boolean levelChanged = mCurrentState.level != mWifiTracker.level;
mCurrentState.level = mWifiTracker.level;
mCurrentState.statusLabel = mWifiTracker.statusLabel;
mCurrentState.isCarrierMerged = mWifiTracker.isCarrierMerged;
@@ -230,10 +229,6 @@
mCurrentState.iconGroup =
mCurrentState.isCarrierMerged ? mCarrierMergedWifiIconGroup
: mUnmergedWifiIconGroup;
-
- if (levelChanged) {
- mNetworkController.notifyWifiLevelChange(mCurrentState.level);
- }
}
boolean isCarrierMergedWifi(int subId) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 126a986..dbf4810 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -18,8 +18,10 @@
import android.animation.ObjectAnimator
import android.util.FloatProperty
+import com.android.systemui.Dumpable
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.collection.NotificationEntry
@@ -32,17 +34,20 @@
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionListener
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
+import java.io.PrintWriter
import javax.inject.Inject
import kotlin.math.min
@SysUISingleton
class NotificationWakeUpCoordinator @Inject constructor(
+ dumpManager: DumpManager,
private val mHeadsUpManager: HeadsUpManager,
private val statusBarStateController: StatusBarStateController,
private val bypassController: KeyguardBypassController,
private val dozeParameters: DozeParameters,
private val screenOffAnimationController: ScreenOffAnimationController
-) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener {
+) : OnHeadsUpChangedListener, StatusBarStateController.StateListener, PanelExpansionListener,
+ Dumpable {
private val mNotificationVisibility = object : FloatProperty<NotificationWakeUpCoordinator>(
"notificationVisibility") {
@@ -60,6 +65,7 @@
private var mLinearDozeAmount: Float = 0.0f
private var mDozeAmount: Float = 0.0f
+ private var mDozeAmountSource: String = "init"
private var mNotificationVisibleAmount = 0.0f
private var mNotificationsVisible = false
private var mNotificationsVisibleForExpansion = false
@@ -142,6 +148,7 @@
}
init {
+ dumpManager.registerDumpable(this)
mHeadsUpManager.addListener(this)
statusBarStateController.addCallback(this)
addListener(object : WakeUpListener {
@@ -248,13 +255,14 @@
// Let's notify the scroller that an animation started
notifyAnimationStart(mLinearDozeAmount == 1.0f)
}
- setDozeAmount(linear, eased)
+ setDozeAmount(linear, eased, source = "StatusBar")
}
- fun setDozeAmount(linear: Float, eased: Float) {
+ fun setDozeAmount(linear: Float, eased: Float, source: String) {
val changed = linear != mLinearDozeAmount
mLinearDozeAmount = linear
mDozeAmount = eased
+ mDozeAmountSource = source
mStackScrollerController.setDozeAmount(mDozeAmount)
updateHideAmount()
if (changed && linear == 0.0f) {
@@ -271,7 +279,7 @@
// undefined state, so it's an indication that we should do state cleanup. We override
// the doze amount to 0f (not dozing) so that the notifications are no longer hidden.
// See: UnlockedScreenOffAnimationController.onFinishedWakingUp()
- setDozeAmount(0f, 0f)
+ setDozeAmount(0f, 0f, source = "Override: Shade->Shade (lock cancelled by unlock)")
}
if (overrideDozeAmountIfAnimatingScreenOff(mLinearDozeAmount)) {
@@ -311,12 +319,11 @@
*/
private fun overrideDozeAmountIfBypass(): Boolean {
if (bypassController.bypassEnabled) {
- var amount = 1.0f
- if (statusBarStateController.state == StatusBarState.SHADE ||
- statusBarStateController.state == StatusBarState.SHADE_LOCKED) {
- amount = 0.0f
+ if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+ setDozeAmount(1f, 1f, source = "Override: bypass (keyguard)")
+ } else {
+ setDozeAmount(0f, 0f, source = "Override: bypass (shade)")
}
- setDozeAmount(amount, amount)
return true
}
return false
@@ -332,7 +339,7 @@
*/
private fun overrideDozeAmountIfAnimatingScreenOff(linearDozeAmount: Float): Boolean {
if (screenOffAnimationController.overrideNotificationsFullyDozingOnKeyguard()) {
- setDozeAmount(1f, 1f)
+ setDozeAmount(1f, 1f, source = "Override: animating screen off")
return true
}
@@ -426,4 +433,24 @@
*/
@JvmDefault fun onPulseExpansionChanged(expandingChanged: Boolean) {}
}
-}
\ No newline at end of file
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("mLinearDozeAmount: $mLinearDozeAmount")
+ pw.println("mDozeAmount: $mDozeAmount")
+ pw.println("mDozeAmountSource: $mDozeAmountSource")
+ pw.println("mNotificationVisibleAmount: $mNotificationVisibleAmount")
+ pw.println("mNotificationsVisible: $mNotificationsVisible")
+ pw.println("mNotificationsVisibleForExpansion: $mNotificationsVisibleForExpansion")
+ pw.println("mVisibilityAmount: $mVisibilityAmount")
+ pw.println("mLinearVisibilityAmount: $mLinearVisibilityAmount")
+ pw.println("pulseExpanding: $pulseExpanding")
+ pw.println("state: ${StatusBarState.toString(state)}")
+ pw.println("fullyAwake: $fullyAwake")
+ pw.println("wakingUp: $wakingUp")
+ pw.println("willWakeUp: $willWakeUp")
+ pw.println("collapsedEnoughToHide: $collapsedEnoughToHide")
+ pw.println("pulsing: $pulsing")
+ pw.println("notificationsFullyHidden: $notificationsFullyHidden")
+ pw.println("canShowPulsingHuns: $canShowPulsingHuns")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index 6dbbf0d..fc8e7d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -30,7 +30,6 @@
import com.android.systemui.R;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -255,9 +254,7 @@
public void addMobileView(MobileIconState state) {
Log.d(TAG, "addMobileView: ");
- StatusBarMobileView view = StatusBarMobileView.fromContext(
- mContext, state.slot,
- mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS));
+ StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, state.slot);
view.applyMobileState(state);
view.setStaticDrawableColor(mColor);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index a2140c6ab..7b8c5fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -423,6 +423,21 @@
+ getActualPaddingEnd();
}
+ @VisibleForTesting
+ boolean shouldForceOverflow(int i, int speedBumpIndex, float iconAppearAmount,
+ int maxVisibleIcons) {
+ return speedBumpIndex != -1 && i >= speedBumpIndex
+ && iconAppearAmount > 0.0f || i >= maxVisibleIcons;
+ }
+
+ @VisibleForTesting
+ boolean isOverflowing(boolean isLastChild, float translationX, float layoutEnd,
+ float iconSize) {
+ // Layout end, as used here, does not include padding end.
+ final float overflowX = isLastChild ? layoutEnd : layoutEnd - iconSize;
+ return translationX >= overflowX;
+ }
+
/**
* Calculate the horizontal translations for each notification based on how much the icons
* are inserted into the notification container.
@@ -448,26 +463,26 @@
if (mFirstVisibleIconState == null) {
mFirstVisibleIconState = iconState;
}
- boolean forceOverflow = mSpeedBumpIndex != -1 && i >= mSpeedBumpIndex
- && iconState.iconAppearAmount > 0.0f || i >= maxVisibleIcons;
- boolean isLastChild = i == childCount - 1;
- float drawingScale = mOnLockScreen && view instanceof StatusBarIconView
- ? ((StatusBarIconView) view).getIconScaleIncreased()
- : 1f;
iconState.visibleState = iconState.hidden
? StatusBarIconView.STATE_HIDDEN
: StatusBarIconView.STATE_ICON;
- final float overflowDotX = layoutEnd - mIconSize;
- boolean isOverflowing = translationX > overflowDotX;
+ final boolean forceOverflow = shouldForceOverflow(i, mSpeedBumpIndex,
+ iconState.iconAppearAmount, maxVisibleIcons);
+ final boolean isOverflowing = forceOverflow || isOverflowing(
+ /* isLastChild= */ i == childCount - 1, translationX, layoutEnd, mIconSize);
- if (firstOverflowIndex == -1 && (forceOverflow || isOverflowing)) {
- firstOverflowIndex = isLastChild && !forceOverflow ? i - 1 : i;
+ // First icon to overflow.
+ if (firstOverflowIndex == -1 && isOverflowing) {
+ firstOverflowIndex = i;
mVisualOverflowStart = layoutEnd - mIconSize;
if (forceOverflow || mIsStaticLayout) {
mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart);
}
}
+ final float drawingScale = mOnLockScreen && view instanceof StatusBarIconView
+ ? ((StatusBarIconView) view).getIconScaleIncreased()
+ : 1f;
translationX += iconState.iconAppearAmount * view.getWidth() * drawingScale;
}
mNumDots = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 31d9266..30b640b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -38,7 +38,6 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.demomode.DemoModeCommandReceiver;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -361,9 +360,7 @@
}
private StatusBarMobileView onCreateStatusBarMobileView(String slot) {
- StatusBarMobileView view = StatusBarMobileView.fromContext(
- mContext, slot,
- mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS));
+ StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, slot);
return view;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 4d9c811..dac532b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -19,6 +19,7 @@
import static android.view.WindowInsets.Type.navigationBars;
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_DISMISS_BOUNCER;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_COLLAPSING;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
@@ -387,6 +388,7 @@
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
} else if (mShowing && !hideBouncerOverDream) {
if (!isWakeAndUnlocking()
+ && !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER)
&& !mCentralSurfaces.isInLaunchTransition()
&& !isUnlockCollapsing()) {
mBouncer.setExpansion(fraction);
@@ -398,9 +400,8 @@
}
} else if (!mShowing && mBouncer.inTransit()) {
// Keyguard is not visible anymore, but expansion animation was still running.
- // We need to keep propagating the expansion state to the bouncer, otherwise it will be
- // stuck in transit.
- mBouncer.setExpansion(fraction);
+ // We need to hide the bouncer, otherwise it will be stuck in transit.
+ mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
} else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) {
// Panel expanded while pulsing but didn't translate the bouncer (because we are
// unlocked.) Let's simply wake-up to dismiss the lock screen.
@@ -739,8 +740,10 @@
}
mNotificationShadeWindowController.setKeyguardOccluded(mOccluded);
- // setDozing(false) will call reset once we stop dozing.
- if (!mDozing) {
+ // setDozing(false) will call reset once we stop dozing. Also, if we're going away, there's
+ // no need to reset the keyguard views as we'll be gone shortly. Resetting now could cause
+ // unexpected visible behavior if the keyguard is still visible as we're animating unlocked.
+ if (!mDozing && !mKeyguardStateController.isKeyguardGoingAway()) {
// If Keyguard is reshown, don't hide the bouncer as it might just have been requested
// by a FLAG_DISMISS_KEYGUARD_ACTIVITY.
reset(isOccluding /* hideBouncerWhenShowing*/);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index ee242a4..492734e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -26,8 +26,6 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
import com.android.systemui.statusbar.connectivity.NetworkController;
@@ -66,7 +64,6 @@
private final Handler mHandler = Handler.getMain();
private final CarrierConfigTracker mCarrierConfigTracker;
private final TunerService mTunerService;
- private final FeatureFlags mFeatureFlags;
private boolean mHideAirplane;
private boolean mHideMobile;
@@ -90,8 +87,7 @@
CarrierConfigTracker carrierConfigTracker,
NetworkController networkController,
SecurityController securityController,
- TunerService tunerService,
- FeatureFlags featureFlags
+ TunerService tunerService
) {
mContext = context;
@@ -100,7 +96,6 @@
mNetworkController = networkController;
mSecurityController = securityController;
mTunerService = tunerService;
- mFeatureFlags = featureFlags;
mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane);
mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile);
@@ -378,40 +373,6 @@
}
@Override
- public void setConnectivityStatus(boolean noDefaultNetwork, boolean noValidatedNetwork,
- boolean noNetworksAvailable) {
- if (!mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
- return;
- }
- if (DEBUG) {
- Log.d(TAG, "setConnectivityStatus: "
- + "noDefaultNetwork = " + noDefaultNetwork + ","
- + "noValidatedNetwork = " + noValidatedNetwork + ","
- + "noNetworksAvailable = " + noNetworksAvailable);
- }
- WifiIconState newState = mWifiIconState.copy();
- newState.noDefaultNetwork = noDefaultNetwork;
- newState.noValidatedNetwork = noValidatedNetwork;
- newState.noNetworksAvailable = noNetworksAvailable;
- newState.slot = mSlotWifi;
- newState.airplaneSpacerVisible = mIsAirplaneMode;
- if (noDefaultNetwork && noNetworksAvailable && !mIsAirplaneMode) {
- newState.visible = true;
- newState.resId = R.drawable.ic_qs_no_internet_unavailable;
- } else if (noDefaultNetwork && !noNetworksAvailable
- && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) {
- newState.visible = true;
- newState.resId = R.drawable.ic_qs_no_internet_available;
- } else {
- newState.visible = false;
- newState.resId = 0;
- }
- updateWifiIconWithState(newState);
- mWifiIconState = newState;
- }
-
-
- @Override
public void setEthernetIndicators(IconState state) {
boolean visible = state.visible && !mHideEthernet;
int resId = state.icon;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index f406825..f61b488 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -473,6 +473,10 @@
int state = mAnimationScheduler.getAnimationState();
if (state == IDLE || state == SHOWING_PERSISTENT_DOT) {
animateShow(mEndSideContent, animate);
+ } else {
+ // We are in the middle of a system status event animation, which will animate the
+ // alpha (but not the visibility). Allow the view to become visible again
+ mEndSideContent.setVisibility(View.VISIBLE);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt
new file mode 100644
index 0000000..6c02b0d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollector.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline
+
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
+import kotlinx.coroutines.flow.StateFlow
+
+/**
+ * Interface exposing a flow for raw connectivity information. Clients should collect on
+ * [rawConnectivityInfoFlow] to get updates on connectivity information.
+ *
+ * Note: [rawConnectivityInfoFlow] should be a *hot* flow, so that we only create one instance of it
+ * and all clients get references to the same flow.
+ *
+ * This will be used for the new status bar pipeline to compile information we need to display some
+ * of the icons in the RHS of the status bar.
+ */
+interface ConnectivityInfoCollector {
+ val rawConnectivityInfoFlow: StateFlow<RawConnectivityInfo>
+}
+
+/**
+ * An object containing all of the raw connectivity information.
+ *
+ * Importantly, all the information in this object should not be processed at all (i.e., the data
+ * that we receive from callbacks should be piped straight into this object and not be filtered,
+ * manipulated, or processed in any way). Instead, any listeners on
+ * [ConnectivityInfoCollector.rawConnectivityInfoFlow] can do the processing.
+ *
+ * This allows us to keep all the processing in one place which is beneficial for logging and
+ * debugging purposes.
+ */
+data class RawConnectivityInfo(
+ val networkCapabilityInfo: Map<Int, NetworkCapabilityInfo> = emptyMap(),
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt
new file mode 100644
index 0000000..8d69422
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoCollectorImpl.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilitiesRepo
+import kotlinx.coroutines.CoroutineScope
+import javax.inject.Inject
+import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * The real implementation of [ConnectivityInfoCollector] that will collect information from all the
+ * relevant connectivity callbacks and compile it into [rawConnectivityInfoFlow].
+ */
+@SysUISingleton
+class ConnectivityInfoCollectorImpl @Inject constructor(
+ networkCapabilitiesRepo: NetworkCapabilitiesRepo,
+ @Application scope: CoroutineScope,
+) : ConnectivityInfoCollector {
+ override val rawConnectivityInfoFlow: StateFlow<RawConnectivityInfo> =
+ // TODO(b/238425913): Collect all the separate flows for individual raw information into
+ // this final flow.
+ networkCapabilitiesRepo.dataStream
+ .map {
+ RawConnectivityInfo(networkCapabilityInfo = it)
+ }
+ .stateIn(scope, started = Lazily, initialValue = RawConnectivityInfo())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
index a841914..1aae250 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
@@ -19,7 +19,15 @@
import android.content.Context
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
/**
* A processor that transforms raw connectivity information that we get from callbacks and turns it
@@ -27,20 +35,43 @@
*
* This will be used for the new status bar pipeline to calculate the list of icons that should be
* displayed in the RHS of the status bar.
+ *
+ * Anyone can listen to [processedInfoFlow] to get updates to the processed data.
*/
@SysUISingleton
class ConnectivityInfoProcessor @Inject constructor(
+ connectivityInfoCollector: ConnectivityInfoCollector,
context: Context,
- private val statusBarPipelineFlags: StatusBarPipelineFlags,
+ @Application private val scope: CoroutineScope,
+ statusBarPipelineFlags: StatusBarPipelineFlags,
) : CoreStartable(context) {
+ // Note: This flow will not start running until a client calls `collect` on it, which means that
+ // [connectivityInfoCollector]'s flow will also not start anything until that `collect` call
+ // happens.
+ val processedInfoFlow: Flow<ProcessedConnectivityInfo> =
+ if (!statusBarPipelineFlags.isNewPipelineEnabled())
+ emptyFlow()
+ else connectivityInfoCollector.rawConnectivityInfoFlow
+ .map { it.process() }
+ .stateIn(
+ scope,
+ started = Lazily,
+ initialValue = ProcessedConnectivityInfo()
+ )
+
override fun start() {
- if (statusBarPipelineFlags.isNewPipelineEnabled()) {
- init()
- }
}
- /** Initializes this processor and everything it depends on. */
- private fun init() {
- // TODO(b/238425913): Register all the connectivity callbacks here.
+ private fun RawConnectivityInfo.process(): ProcessedConnectivityInfo {
+ // TODO(b/238425913): Actually process the raw info into meaningful data.
+ return ProcessedConnectivityInfo(this.networkCapabilityInfo)
}
}
+
+/**
+ * An object containing connectivity info that has been processed into data that can be directly
+ * used by the status bar (and potentially other SysUI areas) to display icons.
+ */
+data class ProcessedConnectivityInfo(
+ val networkCapabilityInfo: Map<Int, NetworkCapabilityInfo> = emptyMap(),
+)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 771bb0c..c4e2b73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.pipeline.dagger
import com.android.systemui.CoreStartable
+import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollector
+import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollectorImpl
import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor
import dagger.Binds
import dagger.Module
@@ -30,4 +32,9 @@
@IntoMap
@ClassKey(ConnectivityInfoProcessor::class)
abstract fun bindConnectivityInfoProcessor(cip: ConnectivityInfoProcessor): CoreStartable
+
+ @Binds
+ abstract fun provideConnectivityInfoCollector(
+ impl: ConnectivityInfoCollectorImpl
+ ): ConnectivityInfoCollector
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 85ecfd3..035404c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -16,6 +16,7 @@
package com.android.keyguard;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
@@ -31,7 +32,6 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -41,6 +41,7 @@
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.IStrongAuthTracker;
import android.app.trust.TrustManager;
@@ -52,6 +53,7 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
@@ -68,6 +70,7 @@
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IRemoteCallback;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.telephony.ServiceState;
@@ -182,6 +185,18 @@
private ActiveUnlockConfig mActiveUnlockConfig;
@Mock
private KeyguardUpdateMonitorLogger mKeyguardUpdateMonitorLogger;
+ @Mock
+ private IActivityManager mActivityService;
+
+ private final int mCurrentUserId = 100;
+ private final UserInfo mCurrentUserInfo = new UserInfo(mCurrentUserId, "Test user", 0);
+
+ @Captor
+ private ArgumentCaptor<IBiometricEnabledOnKeyguardCallback>
+ mBiometricEnabledCallbackArgCaptor;
+ @Captor
+ private ArgumentCaptor<FaceManager.AuthenticationCallback> mAuthenticationCallbackCaptor;
+
// Direct executor
private Executor mBackgroundExecutor = Runnable::run;
private Executor mMainExecutor = Runnable::run;
@@ -190,18 +205,16 @@
private TestableContext mSpiedContext;
private MockitoSession mMockitoSession;
private StatusBarStateController.StateListener mStatusBarStateListener;
+ private IBiometricEnabledOnKeyguardCallback mBiometricEnabledOnKeyguardCallback;
@Before
- public void setup() {
+ public void setup() throws RemoteException {
MockitoAnnotations.initMocks(this);
mSpiedContext = spy(mContext);
when(mPackageManager.hasSystemFeature(anyString())).thenReturn(true);
when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
- doAnswer(invocation -> {
- IBiometricEnabledOnKeyguardCallback callback = invocation.getArgument(0);
- callback.onChanged(true /* enabled */, KeyguardUpdateMonitor.getCurrentUser());
- return null;
- }).when(mBiometricManager).registerEnabledOnKeyguardCallback(any());
+ when(mActivityService.getCurrentUser()).thenReturn(mCurrentUserInfo);
+ when(mActivityService.getCurrentUserId()).thenReturn(mCurrentUserId);
when(mFaceManager.isHardwareDetected()).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates()).thenReturn(true);
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
@@ -262,13 +275,20 @@
.startMocking();
ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
.when(SubscriptionManager::getDefaultSubscriptionId);
+ KeyguardUpdateMonitor.setCurrentUser(mCurrentUserId);
ExtendedMockito.doReturn(KeyguardUpdateMonitor.getCurrentUser())
.when(ActivityManager::getCurrentUser);
+ ExtendedMockito.doReturn(mActivityService).when(ActivityManager::getService);
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
+ verify(mBiometricManager)
+ .registerEnabledOnKeyguardCallback(mBiometricEnabledCallbackArgCaptor.capture());
+ mBiometricEnabledOnKeyguardCallback = mBiometricEnabledCallbackArgCaptor.getValue();
+ biometricsEnabledForCurrentUser();
+
verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue();
mKeyguardUpdateMonitor.registerCallback(mTestCallback);
@@ -718,7 +738,6 @@
verify(mLockPatternUtils).requireStrongAuth(anyInt(), anyInt());
}
-
@Test
public void testFaceAndFingerprintLockout() {
mKeyguardUpdateMonitor.dispatchStartedWakingUp();
@@ -924,6 +943,7 @@
@Test
public void testSecondaryLockscreenRequirement() {
+ KeyguardUpdateMonitor.setCurrentUser(UserHandle.myUserId());
int user = KeyguardUpdateMonitor.getCurrentUser();
String packageName = "fake.test.package";
String cls = "FakeService";
@@ -1097,8 +1117,7 @@
@Test
public void testShouldNotUpdateBiometricListeningStateOnStatusBarStateChange() {
// GIVEN state for face auth should run aside from StatusBarState
- when(mDevicePolicyManager.getKeyguardDisabledFeatures(null,
- KeyguardUpdateMonitor.getCurrentUser())).thenReturn(0);
+ biometricsNotDisabledThroughDevicePolicyManager();
mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
setKeyguardBouncerVisibility(false /* isVisible */);
mKeyguardUpdateMonitor.dispatchStartedWakingUp();
@@ -1153,6 +1172,306 @@
verify(mTestCallback).showTrustGrantedMessage("Unlocked by wearable");
}
+ @Test
+ public void testShouldListenForFace_whenFaceManagerNotAvailable_returnsFalse() {
+ mFaceManager = null;
+ mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenFpIsLockedOut_returnsFalse() throws RemoteException {
+ // Face auth should run when the following is true.
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ currentUserIsPrimary();
+ strongAuthNotRequired();
+ biometricsEnabledForCurrentUser();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ // Fingerprint is locked out.
+ fingerprintErrorLockedOut();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenFaceIsAlreadyAuthenticated_returnsFalse()
+ throws RemoteException {
+ // Face auth should run when the following is true.
+ bouncerFullyVisibleAndNotGoingToSleep();
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ strongAuthNotRequired();
+ biometricsEnabledForCurrentUser();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ userNotCurrentlySwitching();
+
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+ triggerSuccessfulFaceAuth();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenUserIsNotPrimary_returnsFalse() throws RemoteException {
+ // This disables face auth
+ when(mUserManager.isPrimaryUser()).thenReturn(false);
+ mKeyguardUpdateMonitor =
+ new TestableKeyguardUpdateMonitor(mSpiedContext);
+
+ // Face auth should run when the following is true.
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ strongAuthNotRequired();
+ biometricsEnabledForCurrentUser();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenStrongAuthDoesNotAllowScanning_returnsFalse()
+ throws RemoteException {
+ // Face auth should run when the following is true.
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ currentUserIsPrimary();
+ biometricsEnabledForCurrentUser();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ userNotCurrentlySwitching();
+
+ // This disables face auth
+ when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+ .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
+ mTestableLooper.processAllMessages();
+
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenBiometricsDisabledForUser_returnsFalse()
+ throws RemoteException {
+ // Face auth should run when the following is true.
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+ // This disables face auth
+ biometricsDisabledForCurrentUser();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenUserCurrentlySwitching_returnsFalse()
+ throws RemoteException {
+ // Face auth should run when the following is true.
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+ userCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenSecureCameraLaunched_returnsFalse()
+ throws RemoteException {
+ // Face auth should run when the following is true.
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+
+ secureCameraLaunched();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenOccludingAppRequestsFaceAuth_returnsTrue()
+ throws RemoteException {
+ // Face auth should run when the following is true.
+ keyguardNotGoingAway();
+ bouncerFullyVisibleAndNotGoingToSleep();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ secureCameraLaunched();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+ occludingAppRequestsFaceAuth();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenBouncerShowingAndDeviceIsAwake_returnsTrue()
+ throws RemoteException {
+ // Face auth should run when the following is true.
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+ bouncerFullyVisibleAndNotGoingToSleep();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+ }
+
+ @Test
+ public void testShouldListenForFace_whenAuthInterruptIsActive_returnsTrue()
+ throws RemoteException {
+ // Face auth should run when the following is true.
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
+
+ triggerAuthInterrupt();
+ mTestableLooper.processAllMessages();
+
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
+ }
+
+ private void triggerAuthInterrupt() {
+ mKeyguardUpdateMonitor.onAuthInterruptDetected(true);
+ }
+
+ private void occludingAppRequestsFaceAuth() {
+ mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
+ }
+
+ private void secureCameraLaunched() {
+ mKeyguardUpdateMonitor.onCameraLaunched();
+ }
+
+ private void userCurrentlySwitching() {
+ mKeyguardUpdateMonitor.setSwitchingUser(true);
+ }
+
+ private void fingerprintErrorLockedOut() {
+ mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
+ .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out");
+ }
+
+ private void triggerSuccessfulFaceAuth() {
+ mKeyguardUpdateMonitor.requestFaceAuth(true);
+ verify(mFaceManager).authenticate(any(),
+ any(),
+ mAuthenticationCallbackCaptor.capture(),
+ any(),
+ anyInt(),
+ anyBoolean());
+ mAuthenticationCallbackCaptor.getValue()
+ .onAuthenticationSucceeded(
+ new FaceManager.AuthenticationResult(null, null, mCurrentUserId, false));
+ }
+
+ private void currentUserIsPrimary() {
+ when(mUserManager.isPrimaryUser()).thenReturn(true);
+ }
+
+ private void biometricsNotDisabledThroughDevicePolicyManager() {
+ when(mDevicePolicyManager.getKeyguardDisabledFeatures(null,
+ KeyguardUpdateMonitor.getCurrentUser())).thenReturn(0);
+ }
+
+ private void biometricsEnabledForCurrentUser() throws RemoteException {
+ mBiometricEnabledOnKeyguardCallback.onChanged(true, KeyguardUpdateMonitor.getCurrentUser());
+ }
+
+ private void biometricsDisabledForCurrentUser() throws RemoteException {
+ mBiometricEnabledOnKeyguardCallback.onChanged(
+ false,
+ KeyguardUpdateMonitor.getCurrentUser()
+ );
+ }
+
+ private void strongAuthNotRequired() {
+ when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
+ .thenReturn(0);
+ }
+
+ private void currentUserDoesNotHaveTrust() {
+ mKeyguardUpdateMonitor.onTrustChanged(
+ false,
+ KeyguardUpdateMonitor.getCurrentUser(),
+ -1,
+ new ArrayList<>()
+ );
+ }
+
+ private void userNotCurrentlySwitching() {
+ mKeyguardUpdateMonitor.setSwitchingUser(false);
+ }
+
+ private void keyguardNotGoingAway() {
+ mKeyguardUpdateMonitor.setKeyguardGoingAway(false);
+ }
+
+ private void bouncerFullyVisibleAndNotGoingToSleep() {
+ mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true, true);
+ mKeyguardUpdateMonitor.dispatchFinishedGoingToSleep(/* value doesn't matter */1);
+ }
+
private void setKeyguardBouncerVisibility(boolean isVisible) {
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible, isVisible);
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index 6978490..3ac28c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -35,12 +35,12 @@
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.eq
-import org.mockito.Mockito.reset
import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -63,6 +63,7 @@
controller = WiredChargingRippleController(
commandRegistry, batteryController, configurationController,
featureFlags, context, windowManager, systemClock, uiEventLogger)
+ rippleView.setupShader()
controller.rippleView = rippleView // Replace the real ripple view with a mock instance
controller.registerCallbacks()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index edcf479..8073103 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -39,6 +39,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.keyguard.KeyguardViewController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
@@ -112,7 +113,7 @@
mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
mAccessibilityButtonModeObserver, mAccessibilityButtonTargetObserver,
mSystemActions, mOverviewProxyService, mAssistManagerLazy,
- () -> Optional.of(mock(CentralSurfaces.class)),
+ () -> Optional.of(mock(CentralSurfaces.class)), mock(KeyguardViewController.class),
mNavigationModeController, mUserTracker, mDumpManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index fe5f433..51f0953 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -72,6 +72,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
+import com.android.keyguard.KeyguardViewController;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
@@ -193,6 +194,8 @@
@Mock
private CentralSurfaces mCentralSurfaces;
@Mock
+ private KeyguardViewController mKeyguardViewController;
+ @Mock
private UserContextProvider mUserContextProvider;
@Mock
private Resources mResources;
@@ -237,8 +240,8 @@
mock(AccessibilityButtonTargetsObserver.class),
mSystemActions, mOverviewProxyService,
() -> mock(AssistManager.class), () -> Optional.of(mCentralSurfaces),
- mock(NavigationModeController.class), mock(UserTracker.class),
- mock(DumpManager.class)));
+ mKeyguardViewController, mock(NavigationModeController.class),
+ mock(UserTracker.class), mock(DumpManager.class)));
mNavigationBar = createNavBar(mContext);
mExternalDisplayNavigationBar = createNavBar(mSysuiTestableContextExternal);
});
@@ -377,7 +380,7 @@
// Verify navbar didn't alter and showing back icon when the keyguard is showing without
// requesting IME insets visible.
- doReturn(true).when(mCentralSurfaces).isKeyguardShowing();
+ doReturn(true).when(mKeyguardViewController).isShowing();
mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true);
assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 07c8af9..be14cc5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -26,7 +26,6 @@
import com.android.systemui.colorextraction.SysuiColorExtractor
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.qs.carrier.QSCarrierGroup
import com.android.systemui.qs.carrier.QSCarrierGroupController
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
@@ -46,10 +45,10 @@
import org.mockito.Answers
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -162,7 +161,6 @@
@Test
fun testRSSISlot_notCombined() {
- `when`(featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(false)
controller.init()
val captor = argumentCaptor<List<String>>()
@@ -174,20 +172,6 @@
}
@Test
- fun testRSSISlot_combined() {
- `when`(featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(true)
- controller.init()
-
- val captor = argumentCaptor<List<String>>()
- verify(view).onAttach(any(), any(), capture(captor), any(), anyBoolean())
-
- assertThat(captor.value).containsExactly(
- mContext.getString(com.android.internal.R.string.status_bar_no_calling),
- mContext.getString(com.android.internal.R.string.status_bar_call_strength)
- )
- }
-
- @Test
fun testSingleCarrierCallback() {
controller.init()
reset(view)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 09ce37b..1e7722a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -44,7 +44,6 @@
import androidx.test.filters.SmallTest;
import com.android.keyguard.CarrierTextManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
@@ -88,7 +87,6 @@
@Mock
private QSCarrier mQSCarrier3;
private TestableLooper mTestableLooper;
- @Mock private FeatureFlags mFeatureFlags;
@Mock
private QSCarrierGroupController.OnSingleCarrierChangedListener mOnSingleCarrierChangedListener;
@@ -130,7 +128,7 @@
mQSCarrierGroupController = new QSCarrierGroupController.Builder(
mActivityStarter, handler, TestableLooper.get(this).getLooper(),
mNetworkController, mCarrierTextControllerBuilder, mContext, mCarrierConfigTracker,
- mFeatureFlags, mSlotIndexResolver)
+ mSlotIndexResolver)
.setQSCarrierGroup(mQSCarrierGroup)
.build();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
index 5212255..99a17a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierTest.java
@@ -22,13 +22,11 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.util.FeatureFlagUtils;
import android.view.LayoutInflater;
import android.view.View;
import androidx.test.filters.SmallTest;
-import com.android.settingslib.graph.SignalDrawable;
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -59,14 +57,14 @@
@Test
public void testUpdateState_first() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c, false));
}
@Test
public void testUpdateState_same() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c, false));
assertFalse(mQSCarrier.updateState(c, false));
@@ -74,7 +72,7 @@
@Test
public void testUpdateState_changed() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c, false));
@@ -85,14 +83,14 @@
@Test
public void testUpdateState_singleCarrier_first() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
assertTrue(mQSCarrier.updateState(c, true));
}
@Test
public void testUpdateState_singleCarrier_noShowIcon() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
mQSCarrier.updateState(c, true);
@@ -101,7 +99,7 @@
@Test
public void testUpdateState_multiCarrier_showIcon() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
mQSCarrier.updateState(c, false);
@@ -110,7 +108,7 @@
@Test
public void testUpdateState_changeSingleMultiSingle() {
- CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false, false);
+ CellSignalState c = new CellSignalState(true, mSignalIconId, "", "", false);
mQSCarrier.updateState(c, true);
assertEquals(View.GONE, mQSCarrier.getRSSIView().getVisibility());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt
new file mode 100644
index 0000000..2d2f4cc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.ripple
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class RippleViewTest : SysuiTestCase() {
+ @Mock
+ private lateinit var rippleView: RippleView
+
+ @Before
+ fun setup() {
+ rippleView = RippleView(context, null)
+ }
+
+ @Test
+ fun testSetupShader_compilesCircle() {
+ rippleView.setupShader(RippleShader.RippleShape.CIRCLE)
+ }
+
+ @Test
+ fun testSetupShader_compilesRoundedBox() {
+ rippleView.setupShader(RippleShader.RippleShape.ROUNDED_BOX)
+ }
+
+ @Test
+ fun testSetupShader_compilesEllipse() {
+ rippleView.setupShader(RippleShader.RippleShape.ELLIPSE)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
new file mode 100644
index 0000000..ce3f20d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.app.IActivityTaskManager
+import android.graphics.Rect
+import android.hardware.display.DisplayManager
+import android.os.Binder
+import android.os.IBinder
+import android.testing.AndroidTestingRunner
+import android.view.Display
+import android.view.SurfaceControl.ScreenshotHardwareBuffer
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Test the logic within ImageCaptureImpl
+ */
+@RunWith(AndroidTestingRunner::class)
+class ImageCaptureImplTest : SysuiTestCase() {
+ private val displayManager = mock<DisplayManager>()
+ private val atmService = mock<IActivityTaskManager>()
+ private val capture = TestableImageCaptureImpl(displayManager, atmService)
+
+ @Test
+ fun captureDisplayWithCrop() {
+ capture.captureDisplay(Display.DEFAULT_DISPLAY, Rect(1, 2, 3, 4))
+ assertThat(capture.token).isNotNull()
+ assertThat(capture.width!!).isEqualTo(2)
+ assertThat(capture.height!!).isEqualTo(2)
+ assertThat(capture.crop!!).isEqualTo(Rect(1, 2, 3, 4))
+ }
+
+ @Test
+ fun captureDisplayWithNullCrop() {
+ capture.captureDisplay(Display.DEFAULT_DISPLAY, null)
+ assertThat(capture.token).isNotNull()
+ assertThat(capture.width!!).isEqualTo(0)
+ assertThat(capture.height!!).isEqualTo(0)
+ assertThat(capture.crop!!).isEqualTo(Rect())
+ }
+
+ class TestableImageCaptureImpl(
+ displayManager: DisplayManager,
+ atmService: IActivityTaskManager
+ ) :
+ ImageCaptureImpl(displayManager, atmService) {
+
+ var token: IBinder? = null
+ var width: Int? = null
+ var height: Int? = null
+ var crop: Rect? = null
+
+ override fun physicalDisplayToken(displayId: Int): IBinder {
+ return Binder()
+ }
+
+ override fun captureDisplay(displayToken: IBinder, width: Int, height: Int, crop: Rect):
+ ScreenshotHardwareBuffer {
+ this.token = displayToken
+ this.width = width
+ this.height = height
+ this.crop = crop
+ return ScreenshotHardwareBuffer(null, null, false, false)
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
new file mode 100644
index 0000000..002f23a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.screenshot
+
+import android.content.ComponentName
+import android.graphics.Bitmap
+import android.graphics.ColorSpace
+import android.graphics.Insets
+import android.graphics.Rect
+import android.hardware.HardwareBuffer
+import android.net.Uri
+import android.view.WindowManager
+import android.view.WindowManager.ScreenshotSource
+import com.android.internal.util.ScreenshotHelper.HardwareBitmapBundler
+import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
+import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import java.util.function.Consumer
+import org.junit.Test
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.isNull
+
+class RequestProcessorTest {
+ private val controller = mock<ScreenshotController>()
+ private val bitmapCaptor = argumentCaptor<Bitmap>()
+
+ @Test
+ fun testFullScreenshot() {
+ val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_KEY_CHORD)
+ val onSavedListener = mock<Consumer<Uri>>()
+ val callback = mock<RequestCallback>()
+ val processor = RequestProcessor(controller)
+
+ processor.processRequest(WindowManager.TAKE_SCREENSHOT_FULLSCREEN, onSavedListener,
+ request, callback)
+
+ verify(controller).takeScreenshotFullscreen(/* topComponent */ isNull(),
+ eq(onSavedListener), eq(callback))
+ }
+
+ @Test
+ fun testSelectedRegionScreenshot() {
+ val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_KEY_CHORD)
+ val onSavedListener = mock<Consumer<Uri>>()
+ val callback = mock<RequestCallback>()
+ val processor = RequestProcessor(controller)
+
+ processor.processRequest(WindowManager.TAKE_SCREENSHOT_SELECTED_REGION, onSavedListener,
+ request, callback)
+
+ verify(controller).takeScreenshotPartial(/* topComponent */ isNull(),
+ eq(onSavedListener), eq(callback))
+ }
+
+ @Test
+ fun testProvidedImageScreenshot() {
+ val taskId = 1111
+ val userId = 2222
+ val bounds = Rect(50, 50, 150, 150)
+ val topComponent = ComponentName("test", "test")
+ val processor = RequestProcessor(controller)
+
+ val buffer = HardwareBuffer.create(100, 100, HardwareBuffer.RGBA_8888, 1,
+ HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE)
+ val bitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB))!!
+ val bitmapBundle = HardwareBitmapBundler.hardwareBitmapToBundle(bitmap)
+
+ val request = ScreenshotRequest(ScreenshotSource.SCREENSHOT_OTHER, bitmapBundle,
+ bounds, Insets.NONE, taskId, userId, topComponent)
+
+ val onSavedListener = mock<Consumer<Uri>>()
+ val callback = mock<RequestCallback>()
+
+ processor.processRequest(WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE, onSavedListener,
+ request, callback)
+
+ verify(controller).handleImageAsScreenshot(
+ bitmapCaptor.capture(), eq(bounds), eq(Insets.NONE), eq(taskId), eq(userId),
+ eq(topComponent), eq(onSavedListener), eq(callback)
+ )
+
+ assertThat(bitmapCaptor.value.equalsHardwareBitmap(bitmap)).isTrue()
+ }
+
+ private fun Bitmap.equalsHardwareBitmap(bitmap: Bitmap): Boolean {
+ return bitmap.hardwareBuffer == this.hardwareBuffer &&
+ bitmap.colorSpace == this.colorSpace
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index c9405c8..fc28349 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -47,6 +47,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.graphics.PointF;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
@@ -461,6 +462,7 @@
NotificationWakeUpCoordinator coordinator =
new NotificationWakeUpCoordinator(
+ mDumpManager,
mock(HeadsUpManagerPhone.class),
new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager,
mInteractionJankMonitor),
@@ -527,7 +529,7 @@
mNotificationShadeWindowController,
mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper,
mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor,
- mMetricsLogger, mActivityManager, mConfigurationController,
+ mMetricsLogger, mConfigurationController,
() -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager,
mConversationNotificationManager, mMediaHiearchyManager,
mStatusBarKeyguardViewManager,
@@ -902,6 +904,76 @@
}
@Test
+ public void keyguardStatusView_splitShade_dozing_alwaysDozingOn_isCentered() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ setDozing(/* dozing= */ true, /* dozingAlwaysOn= */ true);
+
+ assertThat(isKeyguardStatusViewCentered()).isTrue();
+ }
+
+ @Test
+ public void keyguardStatusView_splitShade_dozing_alwaysDozingOff_isNotCentered() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ setDozing(/* dozing= */ true, /* dozingAlwaysOn= */ false);
+
+ assertThat(isKeyguardStatusViewCentered()).isFalse();
+ }
+
+ @Test
+ public void keyguardStatusView_splitShade_notDozing_alwaysDozingOn_isNotCentered() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
+
+ assertThat(isKeyguardStatusViewCentered()).isFalse();
+ }
+
+ @Test
+ public void keyguardStatusView_splitShade_pulsing_isCentered() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(true);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
+
+ assertThat(isKeyguardStatusViewCentered()).isFalse();
+ }
+
+ @Test
+ public void keyguardStatusView_splitShade_notPulsing_isNotCentered() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ true);
+
+ assertThat(isKeyguardStatusViewCentered()).isFalse();
+ }
+
+ @Test
+ public void keyguardStatusView_singleShade_isCentered() {
+ enableSplitShade(/* enabled= */ false);
+ // The conditions below would make the clock NOT be centered on split shade.
+ // On single shade it should always be centered though.
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ when(mNotificationListContainer.hasPulsingNotifications()).thenReturn(false);
+ mStatusBarStateController.setState(KEYGUARD);
+ setDozing(/* dozing= */ false, /* dozingAlwaysOn= */ false);
+
+ assertThat(isKeyguardStatusViewCentered()).isFalse();
+ }
+
+ @Test
public void testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() {
givenViewAttached();
when(mResources.getBoolean(
@@ -1473,4 +1545,19 @@
.thenReturn(splitShadeFullTransitionDistance);
mNotificationPanelViewController.updateResources();
}
+
+ private void setDozing(boolean dozing, boolean dozingAlwaysOn) {
+ when(mDozeParameters.getAlwaysOn()).thenReturn(dozingAlwaysOn);
+ mNotificationPanelViewController.setDozing(
+ /* dozing= */ dozing,
+ /* animate= */ false,
+ /* wakeUpTouchLocation= */ new PointF()
+ );
+ }
+
+ private boolean isKeyguardStatusViewCentered() {
+ mNotificationPanelViewController.updateResources();
+ return getConstraintSetLayout(R.id.keyguard_status_view).endToEnd
+ == ConstraintSet.PARENT_ID;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index 0d1879c..f8a0d2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -70,8 +70,6 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.log.LogBuffer;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
@@ -127,8 +125,8 @@
protected CarrierConfigTracker mCarrierConfigTracker;
protected FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
protected Handler mMainHandler;
- protected FeatureFlags mFeatureFlags;
protected WifiStatusTrackerFactory mWifiStatusTrackerFactory;
+ protected MobileSignalControllerFactory mMobileFactory;
protected int mSubId;
@@ -158,9 +156,6 @@
@Before
public void setUp() throws Exception {
- mFeatureFlags = mock(FeatureFlags.class);
- when(mFeatureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)).thenReturn(false);
-
mInstrumentation = InstrumentationRegistry.getInstrumentation();
Settings.Global.putInt(mContext.getContentResolver(), Global.AIRPLANE_MODE_ON, 0);
TestableResources res = mContext.getOrCreateTestableResources();
@@ -224,6 +219,11 @@
mWifiStatusTrackerFactory = new WifiStatusTrackerFactory(
mContext, mMockWm, mMockNsm, mMockCm, mMainHandler);
+ mMobileFactory = new MobileSignalControllerFactory(
+ mContext,
+ mCallbackHandler,
+ mCarrierConfigTracker
+ );
mNetworkController = new NetworkControllerImpl(mContext,
mMockCm,
@@ -243,8 +243,8 @@
mDemoModeController,
mCarrierConfigTracker,
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class)
);
@@ -438,10 +438,6 @@
updateSignalStrength();
}
- public void setImsType(int imsType) {
- mMobileSignalController.setImsType(imsType);
- }
-
public void setIsGsm(boolean gsm) {
when(mSignalStrength.isGsm()).thenReturn(gsm);
updateSignalStrength();
@@ -637,5 +633,4 @@
protected void assertDataNetworkNameEquals(String expected) {
assertEquals("Data network name", expected, mNetworkController.getMobileDataNetworkName());
}
-
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
index e3dd6f4..ed8a3e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerDataTest.java
@@ -145,8 +145,8 @@
mDemoModeController,
mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
+ mMobileFactory,
new Handler(TestableLooper.get(this).getLooper()),
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class));
setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
index 698899a..a76676e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerSignalTest.java
@@ -85,8 +85,8 @@
mDemoModeController,
mCarrierConfigTracker,
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class)
);
@@ -121,8 +121,8 @@
mDemoModeController,
mCarrierConfigTracker,
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class));
TestableLooper.get(this).processAllMessages();
@@ -155,8 +155,8 @@
mDemoModeController,
mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class));
setupNetworkController();
@@ -192,8 +192,8 @@
mDemoModeController,
mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class));
mNetworkController.registerListeners();
@@ -277,8 +277,8 @@
mDemoModeController,
mock(CarrierConfigTracker.class),
mWifiStatusTrackerFactory,
+ mMobileFactory,
mMainHandler,
- mFeatureFlags,
mock(DumpManager.class),
mock(LogBuffer.class));
setupNetworkController();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
index 3f71491..68170ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/NetworkControllerWifiTest.java
@@ -30,7 +30,6 @@
import android.net.vcn.VcnTransportInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.telephony.CellSignalStrength;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
@@ -285,44 +284,6 @@
verifyLastMobileDataIndicatorsForVcn(false, 1, 0, false);
}
- @Test
- public void testCallStrengh() {
- if (true) return;
- String testSsid = "Test SSID";
- setWifiEnabled(true);
- setWifiState(true, testSsid);
- // Set the ImsType to be IMS_TYPE_WLAN
- setImsType(2);
- setWifiLevel(1);
- for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
- setWifiLevel(testLevel);
- verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[testLevel]);
- }
- // Set the ImsType to be IMS_TYPE_WWAN
- setImsType(1);
- setupDefaultSignal();
- for (int testStrength = 0;
- testStrength < CellSignalStrength.getNumSignalStrengthLevels(); testStrength++) {
- setLevel(testStrength);
- verifyLastCallStrength(TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[testStrength]);
- }
- }
-
- @Test
- public void testNonPrimaryWiFi() {
- if (true) return;
- String testSsid = "Test SSID";
- setWifiEnabled(true);
- setWifiState(true, testSsid);
- // Set the ImsType to be IMS_TYPE_WLAN
- setImsType(2);
- setWifiLevel(1);
- verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[1]);
- when(mWifiInfo.isPrimary()).thenReturn(false);
- setWifiLevel(3);
- verifyLastCallStrength(TelephonyIcons.WIFI_CALL_STRENGTH_ICONS[1]);
- }
-
protected void setWifiActivity(int activity) {
// TODO: Not this, because this variable probably isn't sticking around.
mNetworkController.mWifiSignalController.setActivity(activity);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
index 2ff6dd4..086e5df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
@@ -153,6 +153,106 @@
assertTrue(iconContainer.hasOverflow())
}
+ @Test
+ fun shouldForceOverflow_appearingAboveSpeedBump_true() {
+ val forceOverflow = iconContainer.shouldForceOverflow(
+ /* i= */ 1,
+ /* speedBumpIndex= */ 0,
+ /* iconAppearAmount= */ 1f,
+ /* maxVisibleIcons= */ 5
+ )
+ assertTrue(forceOverflow);
+ }
+
+ @Test
+ fun shouldForceOverflow_moreThanMaxVisible_true() {
+ val forceOverflow = iconContainer.shouldForceOverflow(
+ /* i= */ 10,
+ /* speedBumpIndex= */ 11,
+ /* iconAppearAmount= */ 0f,
+ /* maxVisibleIcons= */ 5
+ )
+ assertTrue(forceOverflow);
+ }
+
+ @Test
+ fun shouldForceOverflow_belowSpeedBumpAndLessThanMaxVisible_false() {
+ val forceOverflow = iconContainer.shouldForceOverflow(
+ /* i= */ 0,
+ /* speedBumpIndex= */ 11,
+ /* iconAppearAmount= */ 0f,
+ /* maxVisibleIcons= */ 5
+ )
+ assertFalse(forceOverflow);
+ }
+
+ @Test
+ fun isOverflowing_lastChildXLessThanLayoutEnd_false() {
+ val isOverflowing = iconContainer.isOverflowing(
+ /* isLastChild= */ true,
+ /* translationX= */ 0f,
+ /* layoutEnd= */ 10f,
+ /* iconSize= */ 2f,
+ )
+ assertFalse(isOverflowing)
+ }
+
+
+ @Test
+ fun isOverflowing_lastChildXEqualToLayoutEnd_true() {
+ val isOverflowing = iconContainer.isOverflowing(
+ /* isLastChild= */ true,
+ /* translationX= */ 10f,
+ /* layoutEnd= */ 10f,
+ /* iconSize= */ 2f,
+ )
+ assertTrue(isOverflowing)
+ }
+
+ @Test
+ fun isOverflowing_lastChildXGreaterThanLayoutEnd_true() {
+ val isOverflowing = iconContainer.isOverflowing(
+ /* isLastChild= */ true,
+ /* translationX= */ 20f,
+ /* layoutEnd= */ 10f,
+ /* iconSize= */ 2f,
+ )
+ assertTrue(isOverflowing)
+ }
+
+ @Test
+ fun isOverflowing_notLastChildXLessThanDotX_false() {
+ val isOverflowing = iconContainer.isOverflowing(
+ /* isLastChild= */ false,
+ /* translationX= */ 0f,
+ /* layoutEnd= */ 10f,
+ /* iconSize= */ 2f,
+ )
+ assertFalse(isOverflowing)
+ }
+
+ @Test
+ fun isOverflowing_notLastChildXGreaterThanDotX_true() {
+ val isOverflowing = iconContainer.isOverflowing(
+ /* isLastChild= */ false,
+ /* translationX= */ 20f,
+ /* layoutEnd= */ 10f,
+ /* iconSize= */ 2f,
+ )
+ assertTrue(isOverflowing)
+ }
+
+ @Test
+ fun isOverflowing_notLastChildXEqualToDotX_true() {
+ val isOverflowing = iconContainer.isOverflowing(
+ /* isLastChild= */ false,
+ /* translationX= */ 8f,
+ /* layoutEnd= */ 10f,
+ /* iconSize= */ 2f,
+ )
+ assertTrue(isOverflowing)
+ }
+
private fun mockStatusBarIcon() : StatusBarIconView {
val iconView = mock(StatusBarIconView::class.java)
whenever(iconView.width).thenReturn(10)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 1046dbc..2dcdcfc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -202,12 +202,12 @@
}
@Test
- public void onPanelExpansionChanged_propagatesToBouncer_evenAfterHidden() {
+ public void onPanelExpansionChanged_hideBouncer_afterKeyguardHidden() {
mStatusBarKeyguardViewManager.hide(0, 0);
when(mBouncer.inTransit()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mBouncer).setExpansion(eq(EXPANSION_EVENT.getFraction()));
+ verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_HIDDEN));
}
@Test
@@ -249,6 +249,23 @@
}
@Test
+ public void onPanelExpansionChanged_neverTranslatesBouncerWhenDismissBouncer() {
+ // Since KeyguardBouncer.EXPANSION_VISIBLE = 0 panel expansion, if the unlock is dismissing
+ // the bouncer, there may be an onPanelExpansionChanged(0) call to collapse the panel
+ // which would mistakenly cause the bouncer to show briefly before its visibility
+ // is set to hide. Therefore, we don't want to propagate panelExpansionChanged to the
+ // bouncer if the bouncer is dismissing as a result of a biometric unlock.
+ when(mBiometricUnlockController.getMode())
+ .thenReturn(BiometricUnlockController.MODE_DISMISS_BOUNCER);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ expansionEvent(
+ /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* expanded= */ true,
+ /* tracking= */ false));
+ verify(mBouncer, never()).setExpansion(anyFloat());
+ }
+
+ @Test
public void onPanelExpansionChanged_neverTranslatesBouncerWhenLaunchingApp() {
when(mCentralSurfaces.isInLaunchTransition()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 1a8ffc1..4c8599d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -16,6 +16,11 @@
import static android.view.Display.DEFAULT_DISPLAY;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_IN;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.ANIMATING_OUT;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE;
+import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.RUNNING_CHIP_ANIM;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -25,6 +30,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import android.animation.Animator;
import android.app.Fragment;
import android.app.StatusBarManager;
import android.content.Context;
@@ -130,7 +136,8 @@
}
@Test
- public void testDisableSystemInfo() {
+ public void testDisableSystemInfo_systemAnimationIdle_doesHide() {
+ when(mAnimationScheduler.getAnimationState()).thenReturn(IDLE);
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
@@ -143,6 +150,98 @@
}
@Test
+ public void testSystemStatusAnimation_startedDisabled_finishedWithAnimator_showsSystemInfo() {
+ // GIVEN the status bar hides the system info via disable flags, while there is no event
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mAnimationScheduler.getAnimationState()).thenReturn(IDLE);
+ fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
+ assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
+
+ // WHEN the disable flags are cleared during a system event animation
+ when(mAnimationScheduler.getAnimationState()).thenReturn(RUNNING_CHIP_ANIM);
+ fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+ // THEN the view is made visible again, but still low alpha
+ assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+ assertEquals(0, getEndSideContentView().getAlpha(), 0.01);
+
+ // WHEN the system event animation finishes
+ when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_OUT);
+ Animator anim = fragment.onSystemEventAnimationFinish(false);
+ anim.start();
+ processAllMessages();
+ anim.end();
+
+ // THEN the system info is full alpha
+ assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
+ }
+
+ @Test
+ public void testSystemStatusAnimation_systemInfoDisabled_staysInvisible() {
+ // GIVEN the status bar hides the system info via disable flags, while there is no event
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mAnimationScheduler.getAnimationState()).thenReturn(IDLE);
+ fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false);
+ assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
+
+ // WHEN the system event animation finishes
+ when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_OUT);
+ Animator anim = fragment.onSystemEventAnimationFinish(false);
+ anim.start();
+ processAllMessages();
+ anim.end();
+
+ // THEN the system info is at full alpha, but still INVISIBLE (since the disable flag is
+ // still set)
+ assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
+ assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
+ }
+
+
+ @Test
+ public void testSystemStatusAnimation_notDisabled_animatesAlphaZero() {
+ // GIVEN the status bar is not disabled
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_IN);
+ // WHEN the system event animation begins
+ Animator anim = fragment.onSystemEventAnimationBegin();
+ anim.start();
+ processAllMessages();
+ anim.end();
+
+ // THEN the system info is visible but alpha 0
+ assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+ assertEquals(0, getEndSideContentView().getAlpha(), 0.01);
+ }
+
+ @Test
+ public void testSystemStatusAnimation_notDisabled_animatesBackToAlphaOne() {
+ // GIVEN the status bar is not disabled
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_IN);
+ // WHEN the system event animation begins
+ Animator anim = fragment.onSystemEventAnimationBegin();
+ anim.start();
+ processAllMessages();
+ anim.end();
+
+ // THEN the system info is visible but alpha 0
+ assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+ assertEquals(0, getEndSideContentView().getAlpha(), 0.01);
+
+ // WHEN the system event animation finishes
+ when(mAnimationScheduler.getAnimationState()).thenReturn(ANIMATING_OUT);
+ anim = fragment.onSystemEventAnimationFinish(false);
+ anim.start();
+ processAllMessages();
+ anim.end();
+
+ // THEN the syste info is full alpha and VISIBLE
+ assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
+ assertEquals(1, getEndSideContentView().getAlpha(), 0.01);
+ }
+
+ @Test
public void testDisableNotifications() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt
new file mode 100644
index 0000000..515a7c9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessorTest.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline
+
+import android.net.NetworkCapabilities
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.InternalCoroutinesApi
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.`when` as whenever
+
+@OptIn(InternalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class ConnectivityInfoProcessorTest : SysuiTestCase() {
+
+ private val statusBarPipelineFlags = mock<StatusBarPipelineFlags>()
+
+ @Before
+ fun setUp() {
+ whenever(statusBarPipelineFlags.isNewPipelineEnabled()).thenReturn(true)
+ }
+
+ @Test
+ fun collectorInfoUpdated_processedInfoAlsoUpdated() = runBlocking {
+ // GIVEN a processor hooked up to a collector
+ val scope = CoroutineScope(Dispatchers.Unconfined)
+ val collector = FakeConnectivityInfoCollector()
+ val processor = ConnectivityInfoProcessor(
+ collector,
+ context,
+ scope,
+ statusBarPipelineFlags,
+ )
+
+ var mostRecentValue: ProcessedConnectivityInfo? = null
+ val job = launch(start = CoroutineStart.UNDISPATCHED) {
+ processor.processedInfoFlow.collect {
+ mostRecentValue = it
+ }
+ }
+
+ // WHEN the collector emits a value
+ val networkCapabilityInfo = mapOf(
+ 10 to NetworkCapabilityInfo(mock(), NetworkCapabilities.Builder().build())
+ )
+ collector.emitValue(RawConnectivityInfo(networkCapabilityInfo))
+ // Because our job uses [CoroutineStart.UNDISPATCHED], it executes in the same thread as
+ // this test. So, our test needs to yield to let the job run.
+ // Note: Once we upgrade our Kotlin coroutines testing library, we won't need this.
+ yield()
+
+ // THEN the processor receives it
+ assertThat(mostRecentValue?.networkCapabilityInfo).isEqualTo(networkCapabilityInfo)
+
+ job.cancel()
+ scope.cancel()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt
new file mode 100644
index 0000000..710e5f6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/FakeConnectivityInfoCollector.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline
+
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/**
+ * A test-friendly implementation of [ConnectivityInfoCollector] that just emits whatever value it
+ * receives in [emitValue].
+ */
+class FakeConnectivityInfoCollector : ConnectivityInfoCollector {
+ private val _rawConnectivityInfoFlow = MutableStateFlow(RawConnectivityInfo())
+ override val rawConnectivityInfoFlow = _rawConnectivityInfoFlow.asStateFlow()
+
+ suspend fun emitValue(value: RawConnectivityInfo) {
+ _rawConnectivityInfoFlow.emit(value)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 35e9060..b255188 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -56,6 +56,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@@ -208,6 +209,10 @@
}
@VisibleForTesting
+ VirtualDeviceManagerInternal getLocalServiceInstance() {
+ return mLocalService;
+ }
+
class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub implements
VirtualDeviceImpl.PendingTrampolineCallback {
@@ -308,10 +313,11 @@
final long tokenTwo = Binder.clearCallingIdentity();
try {
virtualDeviceImpl.onVirtualDisplayCreatedLocked(gwpc, displayId);
- return displayId;
} finally {
Binder.restoreCallingIdentity(tokenTwo);
}
+ mLocalService.onVirtualDisplayCreated(displayId);
+ return displayId;
}
@Nullable
@@ -378,6 +384,10 @@
}
private final class LocalService extends VirtualDeviceManagerInternal {
+ @GuardedBy("mVirtualDeviceManagerLock")
+ private final ArrayList<VirtualDeviceManagerInternal.VirtualDisplayListener>
+ mVirtualDisplayListeners = new ArrayList<>();
+
@Override
public boolean isValidVirtualDevice(IVirtualDevice virtualDevice) {
synchronized (mVirtualDeviceManagerLock) {
@@ -386,10 +396,30 @@
}
@Override
+ public void onVirtualDisplayCreated(int displayId) {
+ final VirtualDisplayListener[] listeners;
+ synchronized (mVirtualDeviceManagerLock) {
+ listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
+ }
+ mHandler.post(() -> {
+ for (VirtualDisplayListener listener : listeners) {
+ listener.onVirtualDisplayCreated(displayId);
+ }
+ });
+ }
+
+ @Override
public void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId) {
+ final VirtualDisplayListener[] listeners;
synchronized (mVirtualDeviceManagerLock) {
((VirtualDeviceImpl) virtualDevice).onVirtualDisplayRemovedLocked(displayId);
+ listeners = mVirtualDisplayListeners.toArray(new VirtualDisplayListener[0]);
}
+ mHandler.post(() -> {
+ for (VirtualDisplayListener listener : listeners) {
+ listener.onVirtualDisplayRemoved(displayId);
+ }
+ });
}
@Override
@@ -435,6 +465,22 @@
}
return false;
}
+
+ @Override
+ public void registerVirtualDisplayListener(
+ @NonNull VirtualDisplayListener listener) {
+ synchronized (mVirtualDeviceManagerLock) {
+ mVirtualDisplayListeners.add(listener);
+ }
+ }
+
+ @Override
+ public void unregisterVirtualDisplayListener(
+ @NonNull VirtualDisplayListener listener) {
+ synchronized (mVirtualDeviceManagerLock) {
+ mVirtualDisplayListeners.remove(listener);
+ }
+ }
}
private static final class PendingTrampolineMap {
diff --git a/services/core/java/com/android/server/am/AssistDataRequester.java b/services/core/java/com/android/server/am/AssistDataRequester.java
index 98ad81d..98129ed 100644
--- a/services/core/java/com/android/server/am/AssistDataRequester.java
+++ b/services/core/java/com/android/server/am/AssistDataRequester.java
@@ -23,6 +23,8 @@
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
import android.app.IActivityTaskManager;
@@ -41,6 +43,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* Helper class to asynchronously fetch the assist data and screenshot from the current running
@@ -141,50 +144,35 @@
}
/**
- * Request that autofill data be loaded asynchronously. The resulting data will be provided
- * through the {@link AssistDataRequesterCallbacks}.
- *
- * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, int,
- * String, boolean)}.
- */
- public void requestAutofillData(List<IBinder> activityTokens, int callingUid,
- String callingPackage) {
- requestData(activityTokens, true /* requestAutofillData */,
- true /* fetchData */, false /* fetchScreenshot */,
- true /* fetchStructure */, true /* allowFetchData */,
- false /* allowFetchScreenshot */, false /* ignoreTopActivityCheck */, callingUid,
- callingPackage);
- }
-
- /**
* Request that assist data be loaded asynchronously. The resulting data will be provided
* through the {@link AssistDataRequesterCallbacks}.
*
- * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, int,
- * String, boolean)}.
+ * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, boolean,
+ * int, String, String)}.
*/
- public void requestAssistData(List<IBinder> activityTokens, final boolean fetchData,
+ public void requestAssistData(@NonNull List<IBinder> activityTokens, final boolean fetchData,
final boolean fetchScreenshot, boolean allowFetchData, boolean allowFetchScreenshot,
- int callingUid, String callingPackage) {
+ int callingUid, @NonNull String callingPackage,
+ @Nullable String callingAttributionTag) {
requestAssistData(activityTokens, fetchData, fetchScreenshot, true /* fetchStructure */,
allowFetchData, allowFetchScreenshot, false /* ignoreTopActivityCheck */,
- callingUid, callingPackage);
+ callingUid, callingPackage, callingAttributionTag);
}
/**
* Request that assist data be loaded asynchronously. The resulting data will be provided
* through the {@link AssistDataRequesterCallbacks}.
*
- * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, int,
- * String, boolean)}.
+ * See {@link #requestData(List, boolean, boolean, boolean, boolean, boolean, boolean, boolean,
+ * int, String, String)}.
*/
- public void requestAssistData(List<IBinder> activityTokens, final boolean fetchData,
+ public void requestAssistData(@NonNull List<IBinder> activityTokens, final boolean fetchData,
final boolean fetchScreenshot, final boolean fetchStructure, boolean allowFetchData,
boolean allowFetchScreenshot, boolean ignoreTopActivityCheck, int callingUid,
- String callingPackage) {
+ @NonNull String callingPackage, @Nullable String callingAttributionTag) {
requestData(activityTokens, false /* requestAutofillData */, fetchData, fetchScreenshot,
fetchStructure, allowFetchData, allowFetchScreenshot, ignoreTopActivityCheck,
- callingUid, callingPackage);
+ callingUid, callingPackage, callingAttributionTag);
}
/**
@@ -208,14 +196,22 @@
* requester is allowed to fetch the assist screenshot
* @param ignoreTopActivityCheck overrides the check for whether the activity is in focus when
* making the request. Used when passing an activity from Recents.
+ * @param callingUid the uid of the real caller
+ * @param callingPackage the package name of the real caller
+ * @param callingAttributionTag The {@link Context#createAttributionContext attribution tag}
+ * of the calling context or {@code null} for default attribution
*/
- private void requestData(List<IBinder> activityTokens, final boolean requestAutofillData,
- final boolean fetchData, final boolean fetchScreenshot, final boolean fetchStructure,
- boolean allowFetchData, boolean allowFetchScreenshot, boolean ignoreTopActivityCheck,
- int callingUid, String callingPackage) {
+ private void requestData(@NonNull List<IBinder> activityTokens,
+ final boolean requestAutofillData, final boolean fetchData,
+ final boolean fetchScreenshot, final boolean fetchStructure, boolean allowFetchData,
+ boolean allowFetchScreenshot, boolean ignoreTopActivityCheck, int callingUid,
+ @NonNull String callingPackage, @Nullable String callingAttributionTag) {
// TODO(b/34090158): Known issue, if the assist data is not allowed on the current activity,
// then no assist data is requested for any of the other activities
+ Objects.requireNonNull(activityTokens);
+ Objects.requireNonNull(callingPackage);
+
// Early exit if there are no activity to fetch for
if (activityTokens.isEmpty()) {
// No activities, just dispatch request-complete
@@ -241,8 +237,9 @@
mAssistScreenshot.clear();
if (fetchData) {
- if (mAppOpsManager.checkOpNoThrow(mRequestStructureAppOps, callingUid, callingPackage)
- == MODE_ALLOWED && allowFetchData) {
+ if (mAppOpsManager.noteOpNoThrow(mRequestStructureAppOps, callingUid,
+ callingPackage, callingAttributionTag, /* message */ null) == MODE_ALLOWED
+ && allowFetchData) {
final int numActivities = activityTokens.size();
for (int i = 0; i < numActivities; i++) {
IBinder topActivity = activityTokens.get(i);
@@ -291,8 +288,9 @@
}
if (fetchScreenshot) {
- if (mAppOpsManager.checkOpNoThrow(mRequestScreenshotAppOps, callingUid, callingPackage)
- == MODE_ALLOWED && allowFetchScreenshot) {
+ if (mAppOpsManager.noteOpNoThrow(mRequestScreenshotAppOps, callingUid,
+ callingPackage, callingAttributionTag, /* message */ null) == MODE_ALLOWED
+ && allowFetchScreenshot) {
try {
MetricsLogger.count(mContext, "assist_with_screen", 1);
mPendingScreenshotCount++;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index e4224ed..0c0ae7d 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1872,15 +1872,21 @@
/** Called on handler thread */
@VisibleForTesting
void dispatchUserSwitchComplete(@UserIdInt int userId) {
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("dispatchUserSwitchComplete-" + userId);
mInjector.getWindowManager().setSwitchingUser(false);
final int observerCount = mUserSwitchObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
try {
+ t.traceBegin("onUserSwitchComplete-" + userId + " #" + i + " "
+ + mUserSwitchObservers.getBroadcastCookie(i));
mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(userId);
+ t.traceEnd();
} catch (RemoteException e) {
}
}
mUserSwitchObservers.finishBroadcast();
+ t.traceEnd();
}
private void dispatchLockedBootComplete(@UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/app/GameTaskInfoProvider.java b/services/core/java/com/android/server/app/GameTaskInfoProvider.java
index f078d98..25a4f37 100644
--- a/services/core/java/com/android/server/app/GameTaskInfoProvider.java
+++ b/services/core/java/com/android/server/app/GameTaskInfoProvider.java
@@ -16,6 +16,8 @@
package com.android.server.app;
+import static android.view.Display.INVALID_DISPLAY;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
@@ -93,7 +95,8 @@
runningTaskInfos = mActivityTaskManager.getTasks(
/* maxNum= */ Integer.MAX_VALUE,
/* filterOnlyVisibleRecents= */ false,
- /* keepIntentExtra= */ false);
+ /* keepIntentExtra= */ false,
+ INVALID_DISPLAY);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to fetch running tasks");
return null;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f0a7df4..5a20db3 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -369,6 +369,7 @@
private static final int MSG_DISPATCH_DEVICE_VOLUME_BEHAVIOR = 47;
private static final int MSG_ROTATION_UPDATE = 48;
private static final int MSG_FOLD_UPDATE = 49;
+ private static final int MSG_RESET_SPATIALIZER = 50;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
@@ -376,6 +377,7 @@
private static final int MSG_DISABLE_AUDIO_FOR_UID = 100;
private static final int MSG_INIT_STREAMS_VOLUMES = 101;
private static final int MSG_INIT_SPATIALIZER = 102;
+
// end of messages handled under wakelock
// retry delay in case of failure to indicate system ready to AudioFlinger
@@ -3942,14 +3944,12 @@
case AudioSystem.MODE_IN_COMMUNICATION:
case AudioSystem.MODE_IN_CALL:
case AudioSystem.MODE_NORMAL:
+ case AudioSystem.MODE_CALL_SCREENING:
+ case AudioSystem.MODE_CALL_REDIRECT:
+ case AudioSystem.MODE_COMMUNICATION_REDIRECT:
break;
- case AudioSystem.MODE_RINGTONE:
- // not changing anything for ringtone
- return;
- case AudioSystem.MODE_CURRENT:
- case AudioSystem.MODE_INVALID:
default:
- // don't know what to do in this case, better bail
+ // no-op is enough for all other values
return;
}
@@ -3977,14 +3977,12 @@
case AudioSystem.MODE_IN_COMMUNICATION:
case AudioSystem.MODE_IN_CALL:
case AudioSystem.MODE_NORMAL:
+ case AudioSystem.MODE_CALL_SCREENING:
+ case AudioSystem.MODE_CALL_REDIRECT:
+ case AudioSystem.MODE_COMMUNICATION_REDIRECT:
break;
- case AudioSystem.MODE_RINGTONE:
- // not changing anything for ringtone
- return;
- case AudioSystem.MODE_CURRENT:
- case AudioSystem.MODE_INVALID:
default:
- // don't know what to do in this case, better bail
+ // no-op is enough for all other values
return;
}
@@ -8309,6 +8307,10 @@
onPersistSpatialAudioDeviceSettings();
break;
+ case MSG_RESET_SPATIALIZER:
+ mSpatializerHelper.reset(/* featureEnabled */ mHasSpatializerEffect);
+ break;
+
case MSG_CHECK_MUSIC_ACTIVE:
onCheckMusicActive((String) msg.obj);
break;
@@ -9287,6 +9289,16 @@
/*arg1*/ 0, /*arg2*/ 0, TAG, /*delay*/ 0);
}
+ /**
+ * post a message to schedule a reset of the spatializer state
+ */
+ void postResetSpatializer() {
+ sendMsg(mAudioHandler,
+ MSG_RESET_SPATIALIZER,
+ SENDMSG_REPLACE,
+ /*arg1*/ 0, /*arg2*/ 0, TAG, /*delay*/ 0);
+ }
+
void onInitSpatializer() {
final String settings = mSettings.getSecureStringForUser(mContentResolver,
Settings.Secure.SPATIAL_AUDIO_ENABLED, UserHandle.USER_CURRENT);
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index d9037fe..66a4527 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -53,6 +53,8 @@
private static final String TAG = "AS.BtHelper";
+ private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO remove in U
+
private final @NonNull AudioDeviceBroker mDeviceBroker;
BtHelper(@NonNull AudioDeviceBroker broker) {
@@ -911,6 +913,8 @@
return "ENCODING_APTX_HD";
case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
return "ENCODING_LDAC";
+ case SOURCE_CODEC_TYPE_OPUS: // TODO update in U
+ return "ENCODING_OPUS";
default:
return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")";
}
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 1def72b..e27fb11 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -389,10 +389,10 @@
try {
mSpat.setLevel(level);
} catch (RemoteException e) {
- Log.e(TAG, "Can't set spatializer level", e);
- mState = STATE_NOT_SUPPORTED;
- mCapableSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
- enabled = false;
+ Log.e(TAG, "onRoutingUpdated() Can't set spatializer level", e);
+ // try to recover by resetting the native spatializer state
+ postReset();
+ return;
}
}
@@ -404,6 +404,10 @@
}
}
+ private void postReset() {
+ mAudioService.postResetSpatializer();
+ }
+
//------------------------------------------------------
// spatializer callback from native
private final class SpatializerCallback extends INativeSpatializerCallback.Stub {
@@ -751,33 +755,29 @@
if (enabled) {
throw (new IllegalStateException("Can't enable when uninitialized"));
}
- return;
+ break;
case STATE_NOT_SUPPORTED:
if (enabled) {
Log.e(TAG, "Can't enable when unsupported");
}
- return;
+ break;
case STATE_DISABLED_UNAVAILABLE:
case STATE_DISABLED_AVAILABLE:
if (enabled) {
createSpat();
onRoutingUpdated();
- break;
- } else {
- // already in disabled state
- return;
- }
+ // onRoutingUpdated() can update the "enabled" state based on context
+ // and will call setDispatchFeatureEnabledState().
+ } // else { nothing to do as already disabled }
+ break;
case STATE_ENABLED_UNAVAILABLE:
case STATE_ENABLED_AVAILABLE:
if (!enabled) {
releaseSpat();
- break;
- } else {
- // already in enabled state
- return;
- }
+ setDispatchFeatureEnabledState(false, "setSpatializerEnabledInt");
+ } // else { nothing to do as already enabled }
+ break;
}
- setDispatchFeatureEnabledState(enabled, "setSpatializerEnabledInt");
}
synchronized int getCapableImmersiveAudioLevel() {
@@ -1161,8 +1161,11 @@
case STATE_DISABLED_AVAILABLE:
case STATE_ENABLED_AVAILABLE:
if (mSpat == null) {
- throw (new IllegalStateException(
- "null Spatializer when calling " + funcName));
+ // try to recover by resetting the native spatializer state
+ Log.e(TAG, "checkSpatForHeadTracking(): "
+ + "native spatializer should not be null in state: " + mState);
+ postReset();
+ return false;
}
break;
}
@@ -1252,8 +1255,8 @@
case STATE_DISABLED_AVAILABLE:
case STATE_ENABLED_AVAILABLE:
if (mSpat == null) {
- throw (new IllegalStateException(
- "null Spatializer for setParameter for key:" + key));
+ Log.e(TAG, "setParameter(" + key + "): null spatializer in state: " + mState);
+ return;
}
break;
}
@@ -1276,8 +1279,8 @@
case STATE_DISABLED_AVAILABLE:
case STATE_ENABLED_AVAILABLE:
if (mSpat == null) {
- throw (new IllegalStateException(
- "null Spatializer for getParameter for key:" + key));
+ Log.e(TAG, "getParameter(" + key + "): null spatializer in state: " + mState);
+ return;
}
break;
}
diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
index 23f0ffb..351a1e9 100644
--- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
@@ -39,7 +39,6 @@
public class BroadcastRadioService extends SystemService {
private static final String TAG = "BcRadioSrv";
- private static final boolean DEBUG = false;
private final ServiceImpl mServiceImpl = new ServiceImpl();
@@ -74,6 +73,7 @@
@Override
public List<RadioManager.ModuleProperties> listModules() {
+ Slog.v(TAG, "Listing HIDL modules");
enforcePolicyAccess();
List<RadioManager.ModuleProperties> modules = new ArrayList<>();
modules.addAll(mV1Modules);
@@ -84,7 +84,7 @@
@Override
public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig,
boolean withAudio, ITunerCallback callback) throws RemoteException {
- if (DEBUG) Slog.i(TAG, "Opening module " + moduleId);
+ Slog.v(TAG, "Opening module " + moduleId);
enforcePolicyAccess();
if (callback == null) {
throw new IllegalArgumentException("Callback must not be empty");
@@ -101,16 +101,14 @@
@Override
public ICloseHandle addAnnouncementListener(int[] enabledTypes,
IAnnouncementListener listener) {
- if (DEBUG) {
- Slog.i(TAG, "Adding announcement listener for " + Arrays.toString(enabledTypes));
- }
+ Slog.v(TAG, "Adding announcement listener for " + Arrays.toString(enabledTypes));
Objects.requireNonNull(enabledTypes);
Objects.requireNonNull(listener);
enforcePolicyAccess();
synchronized (mLock) {
if (!mHal2.hasAnyModules()) {
- Slog.i(TAG, "There are no HAL 2.x modules registered");
+ Slog.i(TAG, "There are no HAL 2.0 modules registered");
return new AnnouncementAggregator(listener, mLock);
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
index 5c07f76..534e828 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -132,6 +132,7 @@
}
public @NonNull Collection<RadioManager.ModuleProperties> listModules() {
+ Slog.v(TAG, "List HIDL 2.0 modules");
synchronized (mLock) {
return mModules.values().stream().map(module -> module.mProperties)
.collect(Collectors.toList());
@@ -152,10 +153,11 @@
public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig,
boolean withAudio, @NonNull ITunerCallback callback) throws RemoteException {
+ Slog.v(TAG, "Open HIDL 2.0 session");
Objects.requireNonNull(callback);
if (!withAudio) {
- throw new IllegalArgumentException("Non-audio sessions not supported with HAL 2.x");
+ throw new IllegalArgumentException("Non-audio sessions not supported with HAL 2.0");
}
RadioModule module = null;
@@ -175,6 +177,7 @@
public ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes,
@NonNull IAnnouncementListener listener) {
+ Slog.v(TAG, "Add announcementListener");
AnnouncementAggregator aggregator = new AnnouncementAggregator(listener, mLock);
boolean anySupported = false;
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index ef7f4c9..aeaa678 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -142,8 +142,12 @@
public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName,
Object lock) {
try {
+ Slog.i(TAG, "Try loading module for idx " + idx + ", fqName " + fqName);
IBroadcastRadio service = IBroadcastRadio.getService(fqName);
- if (service == null) return null;
+ if (service == null) {
+ Slog.w(TAG, "No service found for fqName " + fqName);
+ return null;
+ }
Mutable<AmFmRegionConfig> amfmConfig = new Mutable<>();
service.getAmFmRegionConfig(false, (result, config) -> {
@@ -160,7 +164,7 @@
return new RadioModule(service, prop, lock);
} catch (RemoteException ex) {
- Slog.e(TAG, "failed to load module " + fqName, ex);
+ Slog.e(TAG, "Failed to load module " + fqName, ex);
return null;
}
}
@@ -171,6 +175,7 @@
public @NonNull TunerSession openSession(@NonNull android.hardware.radio.ITunerCallback userCb)
throws RemoteException {
+ Slog.i(TAG, "Open TunerSession");
synchronized (mLock) {
if (mHalTunerSession == null) {
Mutable<ITunerSession> hwSession = new Mutable<>();
@@ -201,6 +206,7 @@
// Copy the contents of mAidlTunerSessions into a local array because TunerSession.close()
// must be called without mAidlTunerSessions locked because it can call
// onTunerSessionClosed().
+ Slog.i(TAG, "Close TunerSessions");
TunerSession[] tunerSessions;
synchronized (mLock) {
tunerSessions = new TunerSession[mAidlTunerSessions.size()];
@@ -313,7 +319,7 @@
}
onTunerSessionProgramListFilterChanged(null);
if (mAidlTunerSessions.isEmpty() && mHalTunerSession != null) {
- Slog.v(TAG, "closing HAL tuner session");
+ Slog.i(TAG, "Closing HAL tuner session");
try {
mHalTunerSession.close();
} catch (RemoteException ex) {
@@ -365,6 +371,7 @@
public android.hardware.radio.ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes,
@NonNull android.hardware.radio.IAnnouncementListener listener) throws RemoteException {
+ Slog.i(TAG, "Add AnnouncementListener");
ArrayList<Byte> enabledList = new ArrayList<>();
for (int type : enabledTypes) {
enabledList.add((byte)type);
@@ -401,6 +408,7 @@
}
Bitmap getImage(int id) {
+ Slog.i(TAG, "Get image for id " + id);
if (id == 0) throw new IllegalArgumentException("Image ID is missing");
byte[] rawImage;
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
index d476fd6..c13216b 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/TunerSession.java
@@ -27,6 +27,7 @@
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.os.RemoteException;
+import android.util.Log;
import android.util.MutableBoolean;
import android.util.MutableInt;
import android.util.Slog;
@@ -61,8 +62,13 @@
mLock = Objects.requireNonNull(lock);
}
+ private boolean isDebugEnabled() {
+ return Log.isLoggable(TAG, Log.DEBUG);
+ }
+
@Override
public void close() {
+ if (isDebugEnabled()) Slog.d(TAG, "Close");
close(null);
}
@@ -74,6 +80,7 @@
* @param error Optional error to send to client before session is closed.
*/
public void close(@Nullable Integer error) {
+ if (isDebugEnabled()) Slog.d(TAG, "Close on error " + error);
synchronized (mLock) {
if (mIsClosed) return;
if (error != null) {
@@ -104,7 +111,7 @@
synchronized (mLock) {
checkNotClosedLocked();
mDummyConfig = Objects.requireNonNull(config);
- Slog.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL 2.x");
+ Slog.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL 2.0");
mModule.fanoutAidlCallback(cb -> cb.onConfigurationChanged(config));
}
}
@@ -137,6 +144,10 @@
@Override
public void step(boolean directionDown, boolean skipSubChannel) throws RemoteException {
+ if (isDebugEnabled()) {
+ Slog.d(TAG, "Step with directionDown " + directionDown
+ + " skipSubChannel " + skipSubChannel);
+ }
synchronized (mLock) {
checkNotClosedLocked();
int halResult = mHwSession.step(!directionDown);
@@ -146,6 +157,10 @@
@Override
public void scan(boolean directionDown, boolean skipSubChannel) throws RemoteException {
+ if (isDebugEnabled()) {
+ Slog.d(TAG, "Scan with directionDown " + directionDown
+ + " skipSubChannel " + skipSubChannel);
+ }
synchronized (mLock) {
checkNotClosedLocked();
int halResult = mHwSession.scan(!directionDown, skipSubChannel);
@@ -155,6 +170,7 @@
@Override
public void tune(ProgramSelector selector) throws RemoteException {
+ if (isDebugEnabled()) Slog.d(TAG, "Tune with selector " + selector);
synchronized (mLock) {
checkNotClosedLocked();
int halResult = mHwSession.tune(Convert.programSelectorToHal(selector));
@@ -164,6 +180,7 @@
@Override
public void cancel() {
+ Slog.i(TAG, "Cancel");
synchronized (mLock) {
checkNotClosedLocked();
Utils.maybeRethrow(mHwSession::cancel);
@@ -172,23 +189,25 @@
@Override
public void cancelAnnouncement() {
- Slog.i(TAG, "Announcements control doesn't involve cancelling at the HAL level in 2.x");
+ Slog.i(TAG, "Announcements control doesn't involve cancelling at the HAL level in HAL 2.0");
}
@Override
public Bitmap getImage(int id) {
+ if (isDebugEnabled()) Slog.d(TAG, "Get image for " + id);
return mModule.getImage(id);
}
@Override
public boolean startBackgroundScan() {
- Slog.i(TAG, "Explicit background scan trigger is not supported with HAL 2.x");
+ Slog.i(TAG, "Explicit background scan trigger is not supported with HAL 2.0");
mModule.fanoutAidlCallback(cb -> cb.onBackgroundScanComplete());
return true;
}
@Override
public void startProgramListUpdates(ProgramList.Filter filter) throws RemoteException {
+ if (isDebugEnabled()) Slog.d(TAG, "start programList updates " + filter);
// If the AIDL client provides a null filter, it wants all updates, so use the most broad
// filter.
if (filter == null) {
@@ -247,6 +266,7 @@
@Override
public void stopProgramListUpdates() throws RemoteException {
+ if (isDebugEnabled()) Slog.d(TAG, "Stop programList updates");
synchronized (mLock) {
checkNotClosedLocked();
mProgramInfoCache = null;
@@ -270,7 +290,7 @@
@Override
public boolean isConfigFlagSet(int flag) {
- Slog.v(TAG, "isConfigFlagSet " + ConfigFlag.toString(flag));
+ if (isDebugEnabled()) Slog.d(TAG, "Is ConfigFlagSet for " + ConfigFlag.toString(flag));
synchronized (mLock) {
checkNotClosedLocked();
@@ -292,7 +312,9 @@
@Override
public void setConfigFlag(int flag, boolean value) throws RemoteException {
- Slog.v(TAG, "setConfigFlag " + ConfigFlag.toString(flag) + " = " + value);
+ if (isDebugEnabled()) {
+ Slog.d(TAG, "Set ConfigFlag " + ConfigFlag.toString(flag) + " = " + value);
+ }
synchronized (mLock) {
checkNotClosedLocked();
int halResult = mHwSession.setConfigFlag(flag, value);
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index f2b4d42..230bfc5 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -16,6 +16,7 @@
package com.android.server.companion.virtual;
+import android.annotation.NonNull;
import android.companion.virtual.IVirtualDevice;
/**
@@ -24,16 +25,41 @@
*/
public abstract class VirtualDeviceManagerInternal {
+ /** Interface to listen to the creation and destruction of virtual displays. */
+ public interface VirtualDisplayListener {
+ /** Notifies that a virtual display was created. */
+ void onVirtualDisplayCreated(int displayId);
+
+ /** Notifies that a virtual display was removed. */
+ void onVirtualDisplayRemoved(int displayId);
+ }
+
+ /** Register a listener for the creation and destruction of virtual displays. */
+ public abstract void registerVirtualDisplayListener(
+ @NonNull VirtualDisplayListener listener);
+
+ /** Unregister a listener for the creation and destruction of virtual displays. */
+ public abstract void unregisterVirtualDisplayListener(
+ @NonNull VirtualDisplayListener listener);
+
+
/**
* Validate the virtual device.
*/
public abstract boolean isValidVirtualDevice(IVirtualDevice virtualDevice);
/**
- * Notify a virtual display is removed.
+ * Notifies that a virtual display is created.
+ *
+ * @param displayId The display id of the created virtual display.
+ */
+ public abstract void onVirtualDisplayCreated(int displayId);
+
+ /**
+ * Notifies that a virtual display is removed.
*
* @param virtualDevice The virtual device where the virtual display located.
- * @param displayId The display id of the removed virtual display.
+ * @param displayId The display id of the removed virtual display.
*/
public abstract void onVirtualDisplayRemoved(IVirtualDevice virtualDevice, int displayId);
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 114690a..a817cea 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -345,7 +345,9 @@
brightnessEvent.setFlags(brightnessEvent.getFlags()
| (!mAmbientLuxValid ? BrightnessEvent.FLAG_INVALID_LUX : 0)
| (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE
- ? BrightnessEvent.FLAG_DOZE_SCALE : 0));
+ ? BrightnessEvent.FLAG_DOZE_SCALE : 0)
+ | (mCurrentBrightnessMapper.isForIdleMode()
+ ? BrightnessEvent.FLAG_IDLE_CURVE : 0));
}
if (!mAmbientLuxValid) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b343480..2dd3864 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2583,8 +2583,8 @@
final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore,
display, mSyncRoot);
final DisplayPowerController displayPowerController = new DisplayPowerController(
- mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager,
- mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
+ mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
+ mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
() -> handleBrightnessChange(display));
mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index c01a228..6150b49 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -48,6 +48,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.FloatProperty;
import android.util.Log;
import android.util.MathUtils;
import android.util.MutableFloat;
@@ -58,6 +59,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.logging.MetricsLogger;
@@ -97,7 +99,7 @@
* blocker as long as the display is not ready. So most of the work done here
* does not need to worry about holding a suspend blocker unless it happens
* independently of the display ready signal.
- *
+ *
* For debugging, you can make the color fade and brightness animations run
* slower by changing the "animator duration scale" option in Development Settings.
*/
@@ -256,6 +258,9 @@
// to reach the final state.
private final boolean mBrightnessBucketsInDozeConfig;
+ private final Clock mClock;
+ private final Injector mInjector;
+
// Maximum time a ramp animation can take.
private long mBrightnessRampIncreaseMaxTimeMillis;
private long mBrightnessRampDecreaseMaxTimeMillis;
@@ -495,20 +500,22 @@
/**
* Creates the display power controller.
*/
- public DisplayPowerController(Context context,
+ DisplayPowerController(Context context, Injector injector,
DisplayPowerCallbacks callbacks, Handler handler,
SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
Runnable onBrightnessChangeRunnable) {
+
+ mInjector = injector != null ? injector : new Injector();
+ mClock = mInjector.getClock();
mLogicalDisplay = logicalDisplay;
mDisplayId = mLogicalDisplay.getDisplayIdLocked();
- final String displayIdStr = "[" + mDisplayId + "]";
- TAG = "DisplayPowerController" + displayIdStr;
- mSuspendBlockerIdUnfinishedBusiness = displayIdStr + "unfinished business";
- mSuspendBlockerIdOnStateChanged = displayIdStr + "on state changed";
- mSuspendBlockerIdProxPositive = displayIdStr + "prox positive";
- mSuspendBlockerIdProxNegative = displayIdStr + "prox negative";
- mSuspendBlockerIdProxDebounce = displayIdStr + "prox debounce";
+ TAG = "DisplayPowerController[" + mDisplayId + "]";
+ mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId);
+ mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId);
+ mSuspendBlockerIdProxPositive = getSuspendBlockerProxPositiveId(mDisplayId);
+ mSuspendBlockerIdProxNegative = getSuspendBlockerProxNegativeId(mDisplayId);
+ mSuspendBlockerIdProxDebounce = getSuspendBlockerProxDebounceId(mDisplayId);
mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
@@ -681,7 +688,8 @@
/**
* Get the {@link BrightnessChangeEvent}s for the specified user.
- * @param userId userId to fetch data for
+ *
+ * @param userId userId to fetch data for
* @param includePackage if false will null out the package name in events
*/
@Nullable
@@ -723,10 +731,11 @@
* The controller makes a copy of the provided object and then
* begins adjusting the power state to match what was requested.
*
- * @param request The requested power state.
+ * @param request The requested power state.
* @param waitForNegativeProximity If true, issues a request to wait for
- * negative proximity before turning the screen back on, assuming the screen
- * was turned off by the proximity sensor.
+ * negative proximity before turning the screen back on,
+ * assuming the screen
+ * was turned off by the proximity sensor.
* @return True if display is ready, false if there are important changes that must
* be made asynchronously (such as turning the screen on), in which case the caller
* should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()}
@@ -834,7 +843,7 @@
synchronized (mLock) {
mStopped = true;
Message msg = mHandler.obtainMessage(MSG_STOP);
- mHandler.sendMessage(msg);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
if (mDisplayWhiteBalanceController != null) {
mDisplayWhiteBalanceController.setEnabled(false);
@@ -888,12 +897,12 @@
if (!mStopped && !mPendingUpdatePowerStateLocked) {
mPendingUpdatePowerStateLocked = true;
Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
- mHandler.sendMessage(msg);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
}
}
private void initialize(int displayState) {
- mPowerState = new DisplayPowerState(mBlanker,
+ mPowerState = mInjector.getDisplayPowerState(mBlanker,
mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId, displayState);
if (mColorFadeEnabled) {
@@ -908,7 +917,7 @@
mColorFadeOffAnimator.addListener(mAnimatorListener);
}
- mScreenBrightnessRampAnimator = new DualRampAnimator<>(mPowerState,
+ mScreenBrightnessRampAnimator = mInjector.getDualRampAnimator(mPowerState,
DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT,
DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT);
mScreenBrightnessRampAnimator.setAnimationTimeLimits(
@@ -1077,13 +1086,16 @@
@Override
public void onAnimationStart(Animator animation) {
}
+
@Override
public void onAnimationEnd(Animator animation) {
sendUpdatePowerState();
}
+
@Override
public void onAnimationRepeat(Animator animation) {
}
+
@Override
public void onAnimationCancel(Animator animation) {
}
@@ -1124,8 +1136,8 @@
mOnProximityNegativeMessages = 0;
final float brightness = mPowerState != null
- ? mPowerState.getScreenBrightness()
- : PowerManager.BRIGHTNESS_MIN;
+ ? mPowerState.getScreenBrightness()
+ : PowerManager.BRIGHTNESS_MIN;
reportStats(brightness);
if (mPowerState != null) {
@@ -1203,7 +1215,7 @@
state = Display.STATE_ON;
break;
}
- assert(state != Display.STATE_UNKNOWN);
+ assert (state != Display.STATE_UNKNOWN);
// Apply the proximity sensor.
if (mProximitySensor != null) {
@@ -1290,16 +1302,16 @@
final boolean autoBrightnessEnabledInDoze =
mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state);
final boolean autoBrightnessEnabled = mPowerRequest.useAutoBrightness
- && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
- && Float.isNaN(brightnessState)
- && mAutomaticBrightnessController != null;
+ && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
+ && Float.isNaN(brightnessState)
+ && mAutomaticBrightnessController != null;
final boolean autoBrightnessDisabledDueToDisplayOff = mPowerRequest.useAutoBrightness
- && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
+ && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
final int autoBrightnessState = autoBrightnessEnabled
? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
: autoBrightnessDisabledDueToDisplayOff
- ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
- : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
+ ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
+ : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
@@ -1589,9 +1601,9 @@
// AND if we are not in idle screen brightness mode.
if (!brightnessIsTemporary
&& (mAutomaticBrightnessController != null
- && !mAutomaticBrightnessController.isInIdleMode())) {
+ && !mAutomaticBrightnessController.isInIdleMode())) {
if (userInitiatedChange && (mAutomaticBrightnessController == null
- || !mAutomaticBrightnessController.hasValidAmbientLux())) {
+ || !mAutomaticBrightnessController.hasValidAmbientLux())) {
// If we don't have a valid lux reading we can't report a valid
// slider event so notify as if the system changed the brightness.
userInitiatedChange = false;
@@ -1782,26 +1794,26 @@
boolean changed = false;
changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness,
- brightness);
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness,
+ brightness);
changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness,
- adjustedBrightness);
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness,
+ adjustedBrightness);
changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
- minBrightness);
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
+ minBrightness);
changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
- maxBrightness);
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
+ maxBrightness);
changed |=
- mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
- mHbmController.getHighBrightnessMode());
+ mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
+ mHbmController.getHighBrightnessMode());
changed |=
- mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint,
- mHbmController.getTransitionPoint());
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint,
+ mHbmController.getTransitionPoint());
changed |=
- mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
- mBrightnessThrottler.getBrightnessMaxReason());
+ mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
+ mBrightnessThrottler.getBrightnessMaxReason());
return changed;
}
@@ -1943,7 +1955,7 @@
}
if (!isOff
&& (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF
- || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) {
+ || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) {
setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_ON);
if (mPowerState.getColorFadeLevel() == 0.0f) {
blockScreenOn();
@@ -2071,7 +2083,7 @@
} else if (mPowerState.prepareColorFade(mContext,
mColorFadeFadesConfig ?
ColorFade.MODE_FADE :
- ColorFade.MODE_WARM_UP)) {
+ ColorFade.MODE_WARM_UP)) {
mColorFadeOnAnimator.start();
} else {
mColorFadeOnAnimator.end();
@@ -2173,8 +2185,8 @@
mPowerState.dismissColorFadeResources();
} else if (performScreenOffTransition
&& mPowerState.prepareColorFade(mContext,
- mColorFadeFadesConfig ?
- ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN)
+ mColorFadeFadesConfig
+ ? ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN)
&& mPowerState.getScreenState() != Display.STATE_OFF) {
// Perform the screen off animation.
mColorFadeOffAnimator.start();
@@ -2245,7 +2257,7 @@
if (mProximitySensorEnabled
&& mPendingProximity != PROXIMITY_UNKNOWN
&& mPendingProximityDebounceTime >= 0) {
- final long now = SystemClock.uptimeMillis();
+ final long now = mClock.uptimeMillis();
if (mPendingProximityDebounceTime <= now) {
if (mProximity != mPendingProximity) {
// if the status of the sensor changed, stop ignoring.
@@ -2523,7 +2535,7 @@
pw.println(" mCachedBrightnessInfo.hbmTransitionPoint=" +
mCachedBrightnessInfo.hbmTransitionPoint.value);
pw.println(" mCachedBrightnessInfo.brightnessMaxReason =" +
- mCachedBrightnessInfo.brightnessMaxReason .value);
+ mCachedBrightnessInfo.brightnessMaxReason.value);
}
pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
@@ -2706,7 +2718,7 @@
}
float hbmTransitionPoint = PowerManager.BRIGHTNESS_MAX;
- synchronized(mCachedBrightnessInfo) {
+ synchronized (mCachedBrightnessInfo) {
if (mCachedBrightnessInfo.hbmTransitionPoint == null) {
return;
}
@@ -2740,8 +2752,6 @@
}
}
-
-
private final class DisplayControllerHandler extends Handler {
public DisplayControllerHandler(Looper looper) {
super(looper, null, true /*async*/);
@@ -2806,7 +2816,7 @@
break;
case MSG_BRIGHTNESS_RAMP_DONE:
- if (mPowerState != null) {
+ if (mPowerState != null) {
final float brightness = mPowerState.getScreenBrightness();
reportStats(brightness);
}
@@ -2823,7 +2833,7 @@
@Override
public void onSensorChanged(SensorEvent event) {
if (mProximitySensorEnabled) {
- final long time = SystemClock.uptimeMillis();
+ final long time = mClock.uptimeMillis();
final float distance = event.values[0];
boolean positive = distance >= 0.0f && distance < mProximityThreshold;
handleProximitySensorEvent(time, positive);
@@ -2892,19 +2902,68 @@
}
}
+ @VisibleForTesting
+ String getSuspendBlockerUnfinishedBusinessId(int displayId) {
+ return "[" + displayId + "]unfinished business";
+ }
+
+ String getSuspendBlockerOnStateChangedId(int displayId) {
+ return "[" + displayId + "]on state changed";
+ }
+
+ String getSuspendBlockerProxPositiveId(int displayId) {
+ return "[" + displayId + "]prox positive";
+ }
+
+ String getSuspendBlockerProxNegativeId(int displayId) {
+ return "[" + displayId + "]prox negative";
+ }
+
+ @VisibleForTesting
+ String getSuspendBlockerProxDebounceId(int displayId) {
+ return "[" + displayId + "]prox debounce";
+ }
+
+ /** Functional interface for providing time. */
+ @VisibleForTesting
+ interface Clock {
+ /**
+ * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
+ */
+ long uptimeMillis();
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ Clock getClock() {
+ return SystemClock::uptimeMillis;
+ }
+
+ DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
+ int displayId, int displayState) {
+ return new DisplayPowerState(blanker, colorFade, displayId, displayState);
+ }
+
+ DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
+ FloatProperty<DisplayPowerState> firstProperty,
+ FloatProperty<DisplayPowerState> secondProperty) {
+ return new DualRampAnimator(dps, firstProperty, secondProperty);
+ }
+ }
+
static class CachedBrightnessInfo {
public MutableFloat brightness = new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
public MutableFloat adjustedBrightness =
- new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
public MutableFloat brightnessMin =
- new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
public MutableFloat brightnessMax =
- new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
public MutableInt hbmMode = new MutableInt(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
public MutableFloat hbmTransitionPoint =
- new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
+ new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
public MutableInt brightnessMaxReason =
- new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
+ new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
public boolean checkAndSetFloat(MutableFloat mf, float f) {
if (mf.value != f) {
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
index 6698612..d831dbd 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
@@ -27,8 +27,9 @@
public final class BrightnessEvent {
public static final int FLAG_RBC = 0x1;
public static final int FLAG_INVALID_LUX = 0x2;
- public static final int FLAG_DOZE_SCALE = 0x3;
- public static final int FLAG_USER_SET = 0x4;
+ public static final int FLAG_DOZE_SCALE = 0x4;
+ public static final int FLAG_USER_SET = 0x8;
+ public static final int FLAG_IDLE_CURVE = 0x16;
private BrightnessReason mReason = new BrightnessReason();
private int mDisplayId;
@@ -257,6 +258,7 @@
return ((mFlags & FLAG_USER_SET) != 0 ? "user_set " : "")
+ ((mFlags & FLAG_RBC) != 0 ? "rbc " : "")
+ ((mFlags & FLAG_INVALID_LUX) != 0 ? "invalid_lux " : "")
- + ((mFlags & FLAG_DOZE_SCALE) != 0 ? "doze_scale " : "");
+ + ((mFlags & FLAG_DOZE_SCALE) != 0 ? "doze_scale " : "")
+ + ((mFlags & FLAG_IDLE_CURVE) != 0 ? "idle_curve " : "");
}
}
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
index 6c75dbf..eb73234 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java
@@ -223,6 +223,17 @@
}
@AnyThread
+ boolean updateEditorToolType(int toolType) {
+ try {
+ mTarget.updateEditorToolType(toolType);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ return false;
+ }
+ return true;
+ }
+
+ @AnyThread
void changeInputMethodSubtype(InputMethodSubtype subtype) {
try {
mTarget.changeInputMethodSubtype(subtype);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 6f37b51..d48acb1 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -132,6 +132,7 @@
import android.view.IWindowManager;
import android.view.InputChannel;
import android.view.InputDevice;
+import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
@@ -3320,7 +3321,8 @@
@Override
public boolean showSoftInput(IInputMethodClient client, IBinder windowToken, int flags,
- ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ int lastClickTooType, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.showSoftInput");
int uid = Binder.getCallingUid();
ImeTracing.getInstance().triggerManagerServiceDump(
@@ -3332,7 +3334,8 @@
final long ident = Binder.clearCallingIdentity();
try {
if (DEBUG) Slog.v(TAG, "Client requesting input be shown");
- return showCurrentInputLocked(windowToken, flags, resultReceiver, reason);
+ return showCurrentInputLocked(
+ windowToken, flags, lastClickTooType, resultReceiver, reason);
} finally {
Binder.restoreCallingIdentity(ident);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -3404,8 +3407,15 @@
}
@GuardedBy("ImfLock.class")
- boolean showCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
- @SoftInputShowHideReason int reason) {
+ boolean showCurrentInputLocked(IBinder windowToken, int flags,
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
+ return showCurrentInputLocked(
+ windowToken, flags, MotionEvent.TOOL_TYPE_UNKNOWN, resultReceiver, reason);
+ }
+
+ @GuardedBy("ImfLock.class")
+ private boolean showCurrentInputLocked(IBinder windowToken, int flags, int lastClickToolType,
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason) {
mShowRequested = true;
if (mAccessibilityRequestingNoSoftKeyboard || mImeHiddenByDisplayPolicy) {
return false;
@@ -3434,6 +3444,10 @@
+ ", " + showFlags + ", " + resultReceiver + ") for reason: "
+ InputMethodDebug.softInputDisplayReasonToString(reason));
}
+
+ if (lastClickToolType != MotionEvent.TOOL_TYPE_UNKNOWN) {
+ curMethod.updateEditorToolType(lastClickToolType);
+ }
// TODO(b/192412909): Check if we can always call onShowHideSoftInputRequested() or not.
if (curMethod.showSoftInput(showInputToken, showFlags, resultReceiver)) {
onShowHideSoftInputRequested(true /* show */, windowToken, reason);
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index d9cf353..5b75b6a 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -144,7 +144,7 @@
private static final int PASSWORD_SCRYPT_LOG_R = 3;
private static final int PASSWORD_SCRYPT_LOG_P = 1;
private static final int PASSWORD_SALT_LENGTH = 16;
- private static final int PASSWORD_TOKEN_LENGTH = 32;
+ private static final int STRETCHED_LSKF_LENGTH = 32;
private static final String TAG = "SyntheticPasswordManager";
private static final byte[] PERSONALIZATION_SECDISCARDABLE = "secdiscardable-transform".getBytes();
@@ -772,7 +772,7 @@
LockscreenCredential credential, SyntheticPassword sp, int userId) {
long protectorId = generateProtectorId();
PasswordData pwd = PasswordData.create(credential.getType());
- byte[] pwdToken = computePasswordToken(credential, pwd);
+ byte[] stretchedLskf = stretchLskf(credential, pwd);
final long sid;
final byte[] protectorSecret;
@@ -780,7 +780,7 @@
// Protector uses Weaver to verify the LSKF
int weaverSlot = getNextAvailableWeaverSlot();
Slog.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
- byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken),
+ byte[] weaverSecret = weaverEnroll(weaverSlot, stretchedLskfToWeaverKey(stretchedLskf),
null);
if (weaverSecret == null) {
throw new IllegalStateException(
@@ -793,7 +793,7 @@
pwd.passwordHandle = null;
sid = GateKeeper.INVALID_SECURE_USER_ID;
- protectorSecret = transformUnderWeaverSecret(pwdToken, weaverSecret);
+ protectorSecret = transformUnderWeaverSecret(stretchedLskf, weaverSecret);
} else {
// Protector uses Gatekeeper to verify the LSKF
@@ -807,7 +807,7 @@
GateKeeperResponse response;
try {
response = gatekeeper.enroll(fakeUid(userId), null, null,
- passwordTokenToGkInput(pwdToken));
+ stretchedLskfToGkPassword(stretchedLskf));
} catch (RemoteException e) {
throw new IllegalStateException("Failed to enroll LSKF for new SP protector for "
+ "user " + userId, e);
@@ -818,7 +818,7 @@
}
pwd.passwordHandle = response.getPayload();
sid = sidFromPasswordHandle(pwd.passwordHandle);
- protectorSecret = transformUnderSecdiscardable(pwdToken,
+ protectorSecret = transformUnderSecdiscardable(stretchedLskf,
createSecdiscardable(protectorId, userId));
// No need to pass in quality since the credential type already encodes sufficient info
synchronizeFrpPassword(pwd, 0, userId);
@@ -836,12 +836,13 @@
PersistentData persistentData = mStorage.readPersistentDataBlock();
if (persistentData.type == PersistentData.TYPE_SP) {
PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
- byte[] pwdToken = computePasswordToken(userCredential, pwd);
+ byte[] stretchedLskf = stretchLskf(userCredential, pwd);
GateKeeperResponse response;
try {
response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId),
- 0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken));
+ 0 /* challenge */, pwd.passwordHandle,
+ stretchedLskfToGkPassword(stretchedLskf));
} catch (RemoteException e) {
Slog.e(TAG, "FRP verifyChallenge failed", e);
return VerifyCredentialResponse.ERROR;
@@ -853,10 +854,10 @@
return VerifyCredentialResponse.ERROR;
}
PasswordData pwd = PasswordData.fromBytes(persistentData.payload);
- byte[] pwdToken = computePasswordToken(userCredential, pwd);
+ byte[] stretchedLskf = stretchLskf(userCredential, pwd);
int weaverSlot = persistentData.userId;
- return weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken)).stripPayload();
+ return weaverVerify(weaverSlot, stretchedLskfToWeaverKey(stretchedLskf)).stripPayload();
} else {
Slog.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is "
+ persistentData.type);
@@ -1034,7 +1035,7 @@
return result;
}
- byte[] pwdToken = computePasswordToken(credential, pwd);
+ byte[] stretchedLskf = stretchLskf(credential, pwd);
final byte[] protectorSecret;
final long sid;
@@ -1046,20 +1047,20 @@
result.gkResponse = VerifyCredentialResponse.ERROR;
return result;
}
- result.gkResponse = weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken));
+ result.gkResponse = weaverVerify(weaverSlot, stretchedLskfToWeaverKey(stretchedLskf));
if (result.gkResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
return result;
}
sid = GateKeeper.INVALID_SECURE_USER_ID;
- protectorSecret = transformUnderWeaverSecret(pwdToken,
+ protectorSecret = transformUnderWeaverSecret(stretchedLskf,
result.gkResponse.getGatekeeperHAT());
} else {
// Protector uses Gatekeeper to verify the LSKF
- byte[] gkPwdToken = passwordTokenToGkInput(pwdToken);
+ byte[] gkPassword = stretchedLskfToGkPassword(stretchedLskf);
GateKeeperResponse response;
try {
response = gatekeeper.verifyChallenge(fakeUid(userId), 0L,
- pwd.passwordHandle, gkPwdToken);
+ pwd.passwordHandle, gkPassword);
} catch (RemoteException e) {
Slog.e(TAG, "gatekeeper verify failed", e);
result.gkResponse = VerifyCredentialResponse.ERROR;
@@ -1072,7 +1073,7 @@
GateKeeperResponse reenrollResponse;
try {
reenrollResponse = gatekeeper.enroll(fakeUid(userId),
- pwd.passwordHandle, gkPwdToken, gkPwdToken);
+ pwd.passwordHandle, gkPassword, gkPassword);
} catch (RemoteException e) {
Slog.w(TAG, "Fail to invoke gatekeeper.enroll", e);
reenrollResponse = GateKeeperResponse.ERROR;
@@ -1098,7 +1099,7 @@
return result;
}
sid = sidFromPasswordHandle(pwd.passwordHandle);
- protectorSecret = transformUnderSecdiscardable(pwdToken,
+ protectorSecret = transformUnderSecdiscardable(stretchedLskf,
loadSecdiscardable(protectorId, userId));
}
// Supplied credential passes first stage weaver/gatekeeper check so it should be correct.
@@ -1468,18 +1469,20 @@
return String.format("%s%x", PROTECTOR_KEY_ALIAS_PREFIX, protectorId);
}
- private byte[] computePasswordToken(LockscreenCredential credential, PasswordData data) {
+ private byte[] stretchLskf(LockscreenCredential credential, PasswordData data) {
final byte[] password = credential.isNone() ? DEFAULT_PASSWORD : credential.getCredential();
return scrypt(password, data.salt, 1 << data.scryptLogN, 1 << data.scryptLogR,
- 1 << data.scryptLogP, PASSWORD_TOKEN_LENGTH);
+ 1 << data.scryptLogP, STRETCHED_LSKF_LENGTH);
}
- private byte[] passwordTokenToGkInput(byte[] token) {
- return SyntheticPasswordCrypto.personalizedHash(PERSONALIZATION_USER_GK_AUTH, token);
+ private byte[] stretchedLskfToGkPassword(byte[] stretchedLskf) {
+ return SyntheticPasswordCrypto.personalizedHash(PERSONALIZATION_USER_GK_AUTH,
+ stretchedLskf);
}
- private byte[] passwordTokenToWeaverKey(byte[] token) {
- byte[] key = SyntheticPasswordCrypto.personalizedHash(PERSONALIZATION_WEAVER_KEY, token);
+ private byte[] stretchedLskfToWeaverKey(byte[] stretchedLskf) {
+ byte[] key = SyntheticPasswordCrypto.personalizedHash(PERSONALIZATION_WEAVER_KEY,
+ stretchedLskf);
if (key.length < mWeaverConfig.keySize) {
throw new IllegalArgumentException("weaver key length too small");
}
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index a5c762a..c12dc8e 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -119,7 +119,7 @@
ComponentName componentName = getComponentName(pendingIntent, componentType);
if (componentName != null) {
if (!TextUtils.equals(componentName.getPackageName(), sessionPackageName)) {
- EventLog.writeEvent(0x534e4554, "238177121", -1, "");
+ EventLog.writeEvent(0x534e4554, "238177121", -1, ""); // SafetyNet Logging.
throw new IllegalArgumentException("ComponentName does not belong to "
+ "sessionPackageName. sessionPackageName = " + sessionPackageName
+ ", ComponentName pkg = " + componentName.getPackageName());
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 1ee9a87..4f8771a 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -914,16 +914,7 @@
}
@Override
- public void setMediaButtonReceiver(PendingIntent pi, String sessionPackageName)
- throws RemoteException {
- //mPackageName has been verified in MediaSessionService.enforcePackageName().
- if (!TextUtils.equals(sessionPackageName, mPackageName)) {
- EventLog.writeEvent(0x534e4554, "238177121", -1, "");
- throw new IllegalArgumentException("sessionPackageName name does not match "
- + "package name provided to MediaSessionRecord. sessionPackageName = "
- + sessionPackageName + ", pkg = "
- + mPackageName);
- }
+ public void setMediaButtonReceiver(PendingIntent pi) throws RemoteException {
final long token = Binder.clearCallingIdentity();
try {
if ((mPolicies & MediaSessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
@@ -931,7 +922,7 @@
return;
}
mMediaButtonReceiverHolder =
- MediaButtonReceiverHolder.create(mContext, mUserId, pi, sessionPackageName);
+ MediaButtonReceiverHolder.create(mContext, mUserId, pi, mPackageName);
mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
} finally {
Binder.restoreCallingIdentity(token);
@@ -945,7 +936,7 @@
//mPackageName has been verified in MediaSessionService.enforcePackageName().
if (receiver != null && !TextUtils.equals(
mPackageName, receiver.getPackageName())) {
- EventLog.writeEvent(0x534e4554, "238177121", -1, "");
+ EventLog.writeEvent(0x534e4554, "238177121", -1, ""); // SafetyNet Logging.
throw new IllegalArgumentException("receiver does not belong to "
+ "package name provided to MediaSessionRecord. Pkg = " + mPackageName
+ ", Receiver Pkg = " + receiver.getPackageName());
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index b7a5f70..e022f15 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -89,6 +89,7 @@
import android.provider.Settings;
import android.security.GateKeeper;
import android.service.gatekeeper.IGateKeeperService;
+import android.service.voice.VoiceInteractionManagerInternal;
import android.stats.devicepolicy.DevicePolicyEnums;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -4366,6 +4367,11 @@
Binder.withCleanCallingIdentity(() -> {
mPm.onNewUserCreated(preCreatedUser.id, /* convertedFromPreCreated= */ true);
dispatchUserAdded(preCreatedUser, token);
+ VoiceInteractionManagerInternal vimi = LocalServices
+ .getService(VoiceInteractionManagerInternal.class);
+ if (vimi != null) {
+ vimi.onPreCreatedUserConversion(preCreatedUser.id);
+ }
});
return preCreatedUser;
}
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index d49227d..905bcf9 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -64,7 +64,7 @@
COMPILATION_REASON_MAP.put(PackageManagerService.REASON_FIRST_BOOT, ArtStatsLog.
ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_FIRST_BOOT);
COMPILATION_REASON_MAP.put(PackageManagerService.REASON_BOOT_AFTER_OTA, ArtStatsLog.
- ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BOOT);
+ ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_BOOT_AFTER_OTA);
COMPILATION_REASON_MAP.put(PackageManagerService.REASON_POST_BOOT, ArtStatsLog.
ART_DATUM_REPORTED__COMPILATION_REASON__ART_COMPILATION_REASON_POST_BOOT);
COMPILATION_REASON_MAP.put(PackageManagerService.REASON_INSTALL, ArtStatsLog.
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index d2a00af..cceb58d 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -1020,6 +1020,7 @@
public boolean showAssistFromActivity(IBinder token, Bundle args) {
final long ident = Binder.clearCallingIdentity();
try {
+ final String callingAttributionTag;
synchronized (mGlobalLock) {
final ActivityRecord caller = ActivityRecord.forTokenLocked(token);
final Task topRootTask = mService.getTopDisplayFocusedRootTask();
@@ -1035,9 +1036,10 @@
+ " is not visible");
return false;
}
+ callingAttributionTag = top.launchedFromFeatureId;
}
return mAssistUtils.showSessionForActiveService(args, SHOW_SOURCE_APPLICATION,
- null /* showCallback */, token);
+ callingAttributionTag, null /* showCallback */, token);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1054,6 +1056,7 @@
@Override
public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
Slog.i(TAG, "Activity tried to startLocalVoiceInteraction");
+ final String callingAttributionTag;
synchronized (mGlobalLock) {
final Task topRootTask = mService.getTopDisplayFocusedRootTask();
final ActivityRecord activity = topRootTask != null
@@ -1071,9 +1074,10 @@
return;
}
activity.pendingVoiceInteractionStart = true;
+ callingAttributionTag = activity.launchedFromFeatureId;
}
LocalServices.getService(VoiceInteractionManagerInternal.class)
- .startLocalVoiceInteraction(callingActivity, options);
+ .startLocalVoiceInteraction(callingActivity, callingAttributionTag, options);
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index de2bbb6..135cf5d 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4736,9 +4736,6 @@
mPendingRemoteAnimation = options.getRemoteAnimationAdapter();
}
mPendingRemoteTransition = options.getRemoteTransition();
- // Since options gets sent to client apps, remove transition information from it.
- options.setRemoteTransition(null);
- options.setRemoteAnimationAdapter(null);
}
void applyOptionsAnimation() {
@@ -4959,8 +4956,12 @@
ActivityOptions takeOptions() {
if (DEBUG_TRANSITION) Slog.i(TAG, "Taking options for " + this + " callers="
+ Debug.getCallers(6));
+ if (mPendingOptions == null) return null;
final ActivityOptions opts = mPendingOptions;
mPendingOptions = null;
+ // Strip sensitive information from options before sending it to app.
+ opts.setRemoteTransition(null);
+ opts.setRemoteAnimationAdapter(null);
return opts;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 471424b..87a4fe9 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2311,16 +2311,25 @@
* @return a list of {@link ActivityManager.RunningTaskInfo} with up to {@code maxNum} items
*/
public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum) {
- return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */);
+ return getTasks(maxNum, false /* filterForVisibleRecents */, false /* keepIntentExtra */,
+ INVALID_DISPLAY);
}
/**
* @param filterOnlyVisibleRecents whether to filter the tasks based on whether they would ever
* be visible in the recent task list in systemui
*/
- @Override
public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum,
boolean filterOnlyVisibleRecents, boolean keepIntentExtra) {
+ return getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra, INVALID_DISPLAY);
+ }
+
+ /**
+ * @param displayId the target display id, or {@link INVALID_DISPLAY} not to filter by displayId
+ */
+ @Override
+ public List<ActivityManager.RunningTaskInfo> getTasks(int maxNum,
+ boolean filterOnlyVisibleRecents, boolean keepIntentExtra, int displayId) {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
@@ -2342,7 +2351,7 @@
final boolean allowed = isGetTasksAllowed("getTasks", callingPid, callingUid);
flags |= (allowed ? RunningTasks.FLAG_ALLOWED : 0);
mRootWindowContainer.getRunningTasks(
- maxNum, list, flags, callingUid, callingProfileIds);
+ maxNum, list, flags, callingUid, callingProfileIds, displayId);
}
return list;
@@ -2568,6 +2577,9 @@
}
task = r.getTask();
}
+ // If {@code isSystemCaller} is {@code true}, it means the user intends to stop
+ // pinned mode through UI; otherwise, it's called by an app and we need to stop
+ // locked or pinned mode, subject to checks.
getLockTaskController().stopLockTaskMode(task, isSystemCaller, callingUid);
}
// Launch in-call UI if a call is ongoing. This is necessary to allow stopping the lock
@@ -2986,7 +2998,7 @@
@Override
public boolean requestAssistDataForTask(IAssistDataReceiver receiver, int taskId,
- String callingPackageName) {
+ String callingPackageName, @Nullable String callingAttributionTag) {
mAmInternal.enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
"requestAssistDataForTask()");
final long callingId = Binder.clearCallingIdentity();
@@ -3013,7 +3025,7 @@
requester.requestAssistData(topActivityToken, true /* fetchData */,
false /* fetchScreenshot */, false /* fetchStructure */, true /* allowFetchData */,
false /* allowFetchScreenshot*/, true /* ignoreFocusCheck */,
- Binder.getCallingUid(), callingPackageName);
+ Binder.getCallingUid(), callingPackageName, callingAttributionTag);
return true;
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 7aa0541..25d187f 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1900,8 +1900,10 @@
/**
* Called when the resource overlays change.
*/
- public void onOverlayChangedLw() {
+ void onOverlayChanged() {
updateCurrentUserResources();
+ // Update the latest display size, cutout.
+ mDisplayContent.updateDisplayInfo();
onConfigurationChanged();
mSystemGestures.onConfigurationChanged();
}
@@ -2933,7 +2935,10 @@
return;
}
- mDisplayContent.unregisterPointerEventListener(mPointerLocationView);
+ if (!mDisplayContent.isRemoved()) {
+ mDisplayContent.unregisterPointerEventListener(mPointerLocationView);
+ }
+
final WindowManager wm = mContext.getSystemService(WindowManager.class);
wm.removeView(mPointerLocationView);
mPointerLocationView = null;
@@ -2958,6 +2963,9 @@
mHandler.post(mGestureNavigationSettingsObserver::unregister);
mHandler.post(mForceShowNavBarSettingsObserver::unregister);
mImmersiveModeConfirmation.release();
+ if (mService.mPointerLocationEnabled) {
+ setPointerLocationEnabled(false);
+ }
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index 7a055d2..f11c2a7 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -481,27 +481,24 @@
*
* @param task the task that requested the end of lock task mode ({@code null} for quitting app
* pinning mode)
- * @param isSystemCaller indicates whether this request comes from the system via
- * {@link ActivityTaskManagerService#stopSystemLockTaskMode()}. If
- * {@code true}, it means the user intends to stop pinned mode through UI;
- * otherwise, it's called by an app and we need to stop locked or pinned
- * mode, subject to checks.
+ * @param stopAppPinning indicates whether to stop app pinning mode or to stop a task from
+ * being locked.
* @param callingUid the caller that requested the end of lock task mode.
* @throws IllegalArgumentException if the calling task is invalid (e.g., {@code null} or not in
* foreground)
* @throws SecurityException if the caller is not authorized to stop the lock task mode, i.e. if
* they differ from the one that launched lock task mode.
*/
- void stopLockTaskMode(@Nullable Task task, boolean isSystemCaller, int callingUid) {
+ void stopLockTaskMode(@Nullable Task task, boolean stopAppPinning, int callingUid) {
if (mLockTaskModeState == LOCK_TASK_MODE_NONE) {
return;
}
- if (isSystemCaller) {
+ if (stopAppPinning) {
if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
clearLockedTasks("stopAppPinning");
} else {
- Slog.e(TAG_LOCKTASK, "Attempted to stop LockTask with isSystemCaller=true");
+ Slog.e(TAG_LOCKTASK, "Attempted to stop app pinning while fully locked");
showLockTaskToast();
}
@@ -642,6 +639,10 @@
* @param callingUid the caller that requested the launch of lock task mode.
*/
void startLockTaskMode(@NonNull Task task, boolean isSystemCaller, int callingUid) {
+ if (task.mLockTaskAuth == LOCK_TASK_AUTH_DONT_LOCK) {
+ ProtoLog.w(WM_DEBUG_LOCKTASK, "startLockTaskMode: Can't lock due to auth");
+ return;
+ }
if (!isSystemCaller) {
task.mLockTaskUid = callingUid;
if (task.mLockTaskAuth == LOCK_TASK_AUTH_PINNABLE) {
@@ -654,6 +655,11 @@
statusBarManager.showScreenPinningRequest(task.mTaskId);
}
return;
+ } else if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
+ // startLockTask() called by app, and app is part of lock task allowlist.
+ // Deactivate the currently pinned task before doing so.
+ Slog.i(TAG, "Stop app pinning before entering full lock task mode");
+ stopLockTaskMode(/* task= */ null, /* stopAppPinning= */ true, callingUid);
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index d8c3795..7d9dc21 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -3326,9 +3326,16 @@
@VisibleForTesting
void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list,
- int flags, int callingUid, ArraySet<Integer> profileIds) {
- mTaskSupervisor.getRunningTasks().getTasks(maxNum, list, flags, this, callingUid,
- profileIds);
+ int flags, int callingUid, ArraySet<Integer> profileIds, int displayId) {
+ WindowContainer root = this;
+ if (displayId != INVALID_DISPLAY) {
+ root = getDisplayContent(displayId);
+ if (root == null) {
+ return;
+ }
+ }
+ mTaskSupervisor.getRunningTasks().getTasks(maxNum, list, flags, mService.getRecentTasks(),
+ root, callingUid, profileIds);
}
void startPowerModeLaunchIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 1ec191e..120fec0 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -60,8 +60,8 @@
private RecentTasks mRecentTasks;
private boolean mKeepIntentExtra;
- void getTasks(int maxNum, List<RunningTaskInfo> list, int flags,
- RootWindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+ void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
+ WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
// Return early if there are no tasks to fetch
if (maxNum <= 0) {
return;
@@ -76,7 +76,7 @@
mAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
mFilterOnlyVisibleRecents =
(flags & FLAG_FILTER_ONLY_VISIBLE_RECENTS) == FLAG_FILTER_ONLY_VISIBLE_RECENTS;
- mRecentTasks = root.mService.getRecentTasks();
+ mRecentTasks = recentTasks;
mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA;
final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e65f0bb..74e1fe4 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -5319,7 +5319,23 @@
parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
(destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
- parent.deliverNewIntentLocked(callingUid, destIntent, destGrants, srec.packageName);
+ boolean abort;
+ try {
+ abort = !mTaskSupervisor.checkStartAnyActivityPermission(destIntent,
+ parent.info, null /* resultWho */, -1 /* requestCode */, srec.getPid(),
+ callingUid, srec.info.packageName, null /* callingFeatureId */,
+ false /* ignoreTargetSecurity */, false /* launchingInTask */, srec.app,
+ null /* resultRecord */, null /* resultRootTask */);
+ } catch (SecurityException e) {
+ abort = true;
+ }
+ if (abort) {
+ Slog.e(TAG, "Cannot navigateUpTo, intent =" + destIntent);
+ foundParentInTask = false;
+ } else {
+ parent.deliverNewIntentLocked(callingUid, destIntent, destGrants,
+ srec.packageName);
+ }
} else {
try {
ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 602579f..a4d6f70 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -17,6 +17,12 @@
package com.android.server.wm;
import static android.window.TaskFragmentOrganizer.putErrorInfoInBundle;
+import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENT_TO_TASK;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
+import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
@@ -38,6 +44,7 @@
import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController;
import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentTransaction;
import com.android.internal.protolog.common.ProtoLog;
@@ -68,6 +75,11 @@
private final ArrayList<PendingTaskFragmentEvent> mPendingTaskFragmentEvents =
new ArrayList<>();
+ /** Map from {@link ITaskFragmentOrganizer} to {@link TaskFragmentTransaction}. */
+ private final ArrayMap<IBinder, TaskFragmentTransaction> mTmpOrganizerToTransactionMap =
+ new ArrayMap<>();
+ private final ArrayList<ITaskFragmentOrganizer> mTmpOrganizerList = new ArrayList<>();
+
TaskFragmentOrganizerController(ActivityTaskManagerService atm) {
mAtmService = atm;
mGlobalLock = atm.mGlobalLock;
@@ -145,107 +157,138 @@
mOrganizer.asBinder().unlinkToDeath(this, 0 /*flags*/);
}
- void onTaskFragmentAppeared(TaskFragment tf) {
+ @NonNull
+ TaskFragmentTransaction.Change prepareTaskFragmentAppeared(@NonNull TaskFragment tf) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment appeared name=%s", tf.getName());
final TaskFragmentInfo info = tf.getTaskFragmentInfo();
- try {
- mOrganizer.onTaskFragmentAppeared(info);
- mLastSentTaskFragmentInfos.put(tf, info);
- tf.mTaskFragmentAppearedSent = true;
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onTaskFragmentAppeared callback", e);
+ tf.mTaskFragmentAppearedSent = true;
+ mLastSentTaskFragmentInfos.put(tf, info);
+ final TaskFragmentTransaction.Change change =
+ new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_APPEARED)
+ .setTaskFragmentToken(tf.getFragmentToken())
+ .setTaskFragmentInfo(info);
+ if (shouldSendTaskFragmentParentInfoChanged(tf)) {
+ // TODO(b/240519866): convert to pass TaskConfiguration for all TFs in the same Task
+ final Task task = tf.getTask();
+ mLastSentTaskFragmentParentConfigs
+ .put(tf, new Configuration(task.getConfiguration()));
+ change.setTaskId(task.mTaskId)
+ .setTaskConfiguration(task.getConfiguration());
}
- onTaskFragmentParentInfoChanged(tf);
+ return change;
}
- void onTaskFragmentVanished(TaskFragment tf) {
+ @NonNull
+ TaskFragmentTransaction.Change prepareTaskFragmentVanished(@NonNull TaskFragment tf) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment vanished name=%s", tf.getName());
- try {
- mOrganizer.onTaskFragmentVanished(tf.getTaskFragmentInfo());
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onTaskFragmentVanished callback", e);
- }
tf.mTaskFragmentAppearedSent = false;
mLastSentTaskFragmentInfos.remove(tf);
mLastSentTaskFragmentParentConfigs.remove(tf);
+ return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_VANISHED)
+ .setTaskFragmentToken(tf.getFragmentToken())
+ .setTaskFragmentInfo(tf.getTaskFragmentInfo());
}
- void onTaskFragmentInfoChanged(TaskFragment tf) {
- // Parent config may have changed. The controller will check if there is any important
- // config change for the organizer.
- onTaskFragmentParentInfoChanged(tf);
-
+ @Nullable
+ TaskFragmentTransaction.Change prepareTaskFragmentInfoChanged(
+ @NonNull TaskFragment tf) {
// Check if the info is different from the last reported info.
final TaskFragmentInfo info = tf.getTaskFragmentInfo();
final TaskFragmentInfo lastInfo = mLastSentTaskFragmentInfos.get(tf);
if (info.equalsForTaskFragmentOrganizer(lastInfo) && configurationsAreEqualForOrganizer(
info.getConfiguration(), lastInfo.getConfiguration())) {
- return;
+ // Parent config may have changed. The controller will check if there is any
+ // important config change for the organizer.
+ return prepareTaskFragmentParentInfoChanged(tf);
}
+
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "TaskFragment info changed name=%s",
tf.getName());
- try {
- mOrganizer.onTaskFragmentInfoChanged(info);
- mLastSentTaskFragmentInfos.put(tf, info);
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onTaskFragmentInfoChanged callback", e);
+ mLastSentTaskFragmentInfos.put(tf, info);
+ final TaskFragmentTransaction.Change change =
+ new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_INFO_CHANGED)
+ .setTaskFragmentToken(tf.getFragmentToken())
+ .setTaskFragmentInfo(info);
+ if (shouldSendTaskFragmentParentInfoChanged(tf)) {
+ // TODO(b/240519866): convert to pass TaskConfiguration for all TFs in the same Task
+ // at once.
+ // Parent config may have changed. The controller will check if there is any
+ // important config change for the organizer.
+ final Task task = tf.getTask();
+ mLastSentTaskFragmentParentConfigs
+ .put(tf, new Configuration(task.getConfiguration()));
+ change.setTaskId(task.mTaskId)
+ .setTaskConfiguration(task.getConfiguration());
}
+ return change;
}
- void onTaskFragmentParentInfoChanged(TaskFragment tf) {
- // Check if the parent info is different from the last reported parent info.
- if (tf.getParent() == null || tf.getParent().asTask() == null) {
- mLastSentTaskFragmentParentConfigs.remove(tf);
- return;
+ @Nullable
+ TaskFragmentTransaction.Change prepareTaskFragmentParentInfoChanged(
+ @NonNull TaskFragment tf) {
+ if (!shouldSendTaskFragmentParentInfoChanged(tf)) {
+ return null;
}
- final Task parent = tf.getParent().asTask();
+
+ final Task parent = tf.getTask();
final Configuration parentConfig = parent.getConfiguration();
- final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(tf);
- if (configurationsAreEqualForOrganizer(parentConfig, lastParentConfig)
- && parentConfig.windowConfiguration.getWindowingMode()
- == lastParentConfig.windowConfiguration.getWindowingMode()) {
- return;
- }
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
"TaskFragment parent info changed name=%s parentTaskId=%d",
tf.getName(), parent.mTaskId);
- try {
- mOrganizer.onTaskFragmentParentInfoChanged(tf.getFragmentToken(), parentConfig);
- mLastSentTaskFragmentParentConfigs.put(tf, new Configuration(parentConfig));
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onTaskFragmentParentInfoChanged callback", e);
- }
+ mLastSentTaskFragmentParentConfigs.put(tf, new Configuration(parentConfig));
+ return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
+ .setTaskFragmentToken(tf.getFragmentToken())
+ .setTaskId(parent.mTaskId)
+ .setTaskConfiguration(parent.getConfiguration());
}
- void onTaskFragmentError(IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
- int opType, Throwable exception) {
+ /** Whether the system should report TaskFragment parent info changed to the organizer. */
+ private boolean shouldSendTaskFragmentParentInfoChanged(@NonNull TaskFragment tf) {
+ final Task parent = tf.getTask();
+ if (parent == null) {
+ // The TaskFragment is not attached.
+ mLastSentTaskFragmentParentConfigs.remove(tf);
+ return false;
+ }
+ // Check if the parent info is different from the last reported parent info.
+ final Configuration parentConfig = parent.getConfiguration();
+ final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(tf);
+ return !configurationsAreEqualForOrganizer(parentConfig, lastParentConfig)
+ || parentConfig.windowConfiguration.getWindowingMode()
+ != lastParentConfig.windowConfiguration.getWindowingMode();
+ }
+
+ @NonNull
+ TaskFragmentTransaction.Change prepareTaskFragmentError(
+ @Nullable IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
+ int opType, @NonNull Throwable exception) {
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
"Sending TaskFragment error exception=%s", exception.toString());
final TaskFragmentInfo info =
taskFragment != null ? taskFragment.getTaskFragmentInfo() : null;
final Bundle errorBundle = putErrorInfoInBundle(exception, info, opType);
- try {
- mOrganizer.onTaskFragmentError(errorCallbackToken, errorBundle);
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onTaskFragmentError callback", e);
- }
+ return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_ERROR)
+ .setErrorCallbackToken(errorCallbackToken)
+ .setErrorBundle(errorBundle);
}
- void onActivityReparentToTask(ActivityRecord activity) {
+ @Nullable
+ TaskFragmentTransaction.Change prepareActivityReparentToTask(
+ @NonNull ActivityRecord activity) {
if (activity.finishing) {
Slog.d(TAG, "Reparent activity=" + activity.token + " is finishing");
- return;
+ return null;
}
final Task task = activity.getTask();
if (task == null || task.effectiveUid != mOrganizerUid) {
Slog.d(TAG, "Reparent activity=" + activity.token
+ " is not in a task belong to the organizer app.");
- return;
+ return null;
}
if (task.isAllowedToEmbedActivity(activity, mOrganizerUid) != EMBEDDING_ALLOWED) {
Slog.d(TAG, "Reparent activity=" + activity.token
+ " is not allowed to be embedded.");
- return;
+ return null;
}
final IBinder activityToken;
@@ -268,11 +311,10 @@
}
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Activity=%s reparent to taskId=%d",
activity.token, task.mTaskId);
- try {
- mOrganizer.onActivityReparentToTask(task.mTaskId, activity.intent, activityToken);
- } catch (RemoteException e) {
- Slog.d(TAG, "Exception sending onActivityReparentToTask callback", e);
- }
+ return new TaskFragmentTransaction.Change(TYPE_ACTIVITY_REPARENT_TO_TASK)
+ .setTaskId(task.mTaskId)
+ .setActivityIntent(activity.intent)
+ .setActivityToken(activityToken);
}
}
@@ -375,7 +417,7 @@
*/
@Nullable
public RemoteAnimationDefinition getRemoteAnimationDefinition(
- ITaskFragmentOrganizer organizer, int taskId) {
+ @NonNull ITaskFragmentOrganizer organizer, int taskId) {
synchronized (mGlobalLock) {
final TaskFragmentOrganizerState organizerState =
mTaskFragmentOrganizerState.get(organizer.asBinder());
@@ -385,12 +427,13 @@
}
}
- int getTaskFragmentOrganizerUid(ITaskFragmentOrganizer organizer) {
+ int getTaskFragmentOrganizerUid(@NonNull ITaskFragmentOrganizer organizer) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
return state.mOrganizerUid;
}
- void onTaskFragmentAppeared(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+ void onTaskFragmentAppeared(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull TaskFragment taskFragment) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
if (!state.addTaskFragment(taskFragment)) {
return;
@@ -406,19 +449,20 @@
}
}
- void onTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+ void onTaskFragmentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull TaskFragment taskFragment) {
handleTaskFragmentInfoChanged(organizer, taskFragment,
PendingTaskFragmentEvent.EVENT_INFO_CHANGED);
}
- void onTaskFragmentParentInfoChanged(ITaskFragmentOrganizer organizer,
- TaskFragment taskFragment) {
+ void onTaskFragmentParentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull TaskFragment taskFragment) {
handleTaskFragmentInfoChanged(organizer, taskFragment,
PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED);
}
- private void handleTaskFragmentInfoChanged(ITaskFragmentOrganizer organizer,
- TaskFragment taskFragment, int eventType) {
+ private void handleTaskFragmentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull TaskFragment taskFragment, int eventType) {
validateAndGetState(organizer);
if (!taskFragment.mTaskFragmentAppearedSent) {
// Skip if TaskFragment still not appeared.
@@ -444,7 +488,8 @@
mPendingTaskFragmentEvents.add(pendingEvent);
}
- void onTaskFragmentVanished(ITaskFragmentOrganizer organizer, TaskFragment taskFragment) {
+ void onTaskFragmentVanished(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull TaskFragment taskFragment) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
@@ -467,8 +512,9 @@
state.removeTaskFragment(taskFragment);
}
- void onTaskFragmentError(ITaskFragmentOrganizer organizer, IBinder errorCallbackToken,
- TaskFragment taskFragment, int opType, Throwable exception) {
+ void onTaskFragmentError(@NonNull ITaskFragmentOrganizer organizer,
+ @Nullable IBinder errorCallbackToken, @Nullable TaskFragment taskFragment,
+ int opType, @NonNull Throwable exception) {
validateAndGetState(organizer);
Slog.w(TAG, "onTaskFragmentError ", exception);
final PendingTaskFragmentEvent pendingEvent = new PendingTaskFragmentEvent.Builder(
@@ -483,7 +529,7 @@
mAtmService.mWindowManager.mWindowPlacerLocked.requestTraversal();
}
- void onActivityReparentToTask(ActivityRecord activity) {
+ void onActivityReparentToTask(@NonNull ActivityRecord activity) {
final ITaskFragmentOrganizer organizer;
if (activity.mLastTaskFragmentOrganizerBeforePip != null) {
// If the activity is previously embedded in an organized TaskFragment.
@@ -515,11 +561,11 @@
mPendingTaskFragmentEvents.add(pendingEvent);
}
- boolean isOrganizerRegistered(ITaskFragmentOrganizer organizer) {
+ boolean isOrganizerRegistered(@NonNull ITaskFragmentOrganizer organizer) {
return mTaskFragmentOrganizerState.containsKey(organizer.asBinder());
}
- private void removeOrganizer(ITaskFragmentOrganizer organizer) {
+ private void removeOrganizer(@NonNull ITaskFragmentOrganizer organizer) {
final TaskFragmentOrganizerState state = validateAndGetState(organizer);
// remove all of the children of the organized TaskFragment
state.dispose();
@@ -539,7 +585,9 @@
* we wouldn't register {@link DeathRecipient} for the organizer, and might not remove the
* {@link TaskFragment} after the organizer process died.
*/
- private TaskFragmentOrganizerState validateAndGetState(ITaskFragmentOrganizer organizer) {
+ @NonNull
+ private TaskFragmentOrganizerState validateAndGetState(
+ @NonNull ITaskFragmentOrganizer organizer) {
final TaskFragmentOrganizerState state =
mTaskFragmentOrganizerState.get(organizer.asBinder());
if (state == null) {
@@ -672,7 +720,7 @@
}
@Nullable
- private PendingTaskFragmentEvent getLastPendingLifecycleEvent(TaskFragment tf) {
+ private PendingTaskFragmentEvent getLastPendingLifecycleEvent(@NonNull TaskFragment tf) {
for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
if (tf == entry.mTaskFragment && entry.isLifecycleEvent()) {
@@ -683,7 +731,7 @@
}
@Nullable
- private PendingTaskFragmentEvent getPendingTaskFragmentEvent(TaskFragment taskFragment,
+ private PendingTaskFragmentEvent getPendingTaskFragmentEvent(@NonNull TaskFragment taskFragment,
int type) {
for (int i = mPendingTaskFragmentEvents.size() - 1; i >= 0; i--) {
PendingTaskFragmentEvent entry = mPendingTaskFragmentEvents.get(i);
@@ -731,16 +779,36 @@
candidateEvents.add(event);
}
final int numEvents = candidateEvents.size();
+ if (numEvents == 0) {
+ return;
+ }
+
+ mTmpOrganizerToTransactionMap.clear();
+ mTmpOrganizerList.clear();
for (int i = 0; i < numEvents; i++) {
- dispatchEvent(candidateEvents.get(i));
+ final PendingTaskFragmentEvent event = candidateEvents.get(i);
+ if (!mTmpOrganizerToTransactionMap.containsKey(event.mTaskFragmentOrg.asBinder())) {
+ mTmpOrganizerToTransactionMap.put(event.mTaskFragmentOrg.asBinder(),
+ new TaskFragmentTransaction());
+ mTmpOrganizerList.add(event.mTaskFragmentOrg);
+ }
+ mTmpOrganizerToTransactionMap.get(event.mTaskFragmentOrg.asBinder())
+ .addChange(prepareChange(event));
}
- if (numEvents > 0) {
- mPendingTaskFragmentEvents.removeAll(candidateEvents);
+ final int numOrganizers = mTmpOrganizerList.size();
+ for (int i = 0; i < numOrganizers; i++) {
+ final ITaskFragmentOrganizer organizer = mTmpOrganizerList.get(i);
+ dispatchTransactionInfo(organizer,
+ mTmpOrganizerToTransactionMap.get(organizer.asBinder()));
}
+ mPendingTaskFragmentEvents.removeAll(candidateEvents);
+ mTmpOrganizerToTransactionMap.clear();
+ mTmpOrganizerList.clear();
}
- private static boolean isTaskVisible(Task task, ArrayList<Task> knownVisibleTasks,
- ArrayList<Task> knownInvisibleTasks) {
+ private static boolean isTaskVisible(@NonNull Task task,
+ @NonNull ArrayList<Task> knownVisibleTasks,
+ @NonNull ArrayList<Task> knownInvisibleTasks) {
if (knownVisibleTasks.contains(task)) {
return true;
}
@@ -756,44 +824,57 @@
}
}
- void dispatchPendingInfoChangedEvent(TaskFragment taskFragment) {
- PendingTaskFragmentEvent event = getPendingTaskFragmentEvent(taskFragment,
+ void dispatchPendingInfoChangedEvent(@NonNull TaskFragment taskFragment) {
+ final PendingTaskFragmentEvent event = getPendingTaskFragmentEvent(taskFragment,
PendingTaskFragmentEvent.EVENT_INFO_CHANGED);
if (event == null) {
return;
}
- dispatchEvent(event);
+ final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
+ transaction.addChange(prepareChange(event));
+ dispatchTransactionInfo(event.mTaskFragmentOrg, transaction);
mPendingTaskFragmentEvents.remove(event);
}
- private void dispatchEvent(PendingTaskFragmentEvent event) {
+ private void dispatchTransactionInfo(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull TaskFragmentTransaction transaction) {
+ if (transaction.isEmpty()) {
+ return;
+ }
+ try {
+ organizer.onTransactionReady(transaction);
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Exception sending TaskFragmentTransaction", e);
+ }
+ }
+
+ @Nullable
+ private TaskFragmentTransaction.Change prepareChange(
+ @NonNull PendingTaskFragmentEvent event) {
final ITaskFragmentOrganizer taskFragmentOrg = event.mTaskFragmentOrg;
final TaskFragment taskFragment = event.mTaskFragment;
final TaskFragmentOrganizerState state =
mTaskFragmentOrganizerState.get(taskFragmentOrg.asBinder());
if (state == null) {
- return;
+ return null;
}
switch (event.mEventType) {
case PendingTaskFragmentEvent.EVENT_APPEARED:
- state.onTaskFragmentAppeared(taskFragment);
- break;
+ return state.prepareTaskFragmentAppeared(taskFragment);
case PendingTaskFragmentEvent.EVENT_VANISHED:
- state.onTaskFragmentVanished(taskFragment);
- break;
+ return state.prepareTaskFragmentVanished(taskFragment);
case PendingTaskFragmentEvent.EVENT_INFO_CHANGED:
- state.onTaskFragmentInfoChanged(taskFragment);
- break;
+ return state.prepareTaskFragmentInfoChanged(taskFragment);
case PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED:
- state.onTaskFragmentParentInfoChanged(taskFragment);
- break;
+ return state.prepareTaskFragmentParentInfoChanged(taskFragment);
case PendingTaskFragmentEvent.EVENT_ERROR:
- state.onTaskFragmentError(event.mErrorCallbackToken, taskFragment, event.mOpType,
- event.mException);
- break;
+ return state.prepareTaskFragmentError(event.mErrorCallbackToken, taskFragment,
+ event.mOpType, event.mException);
case PendingTaskFragmentEvent.EVENT_ACTIVITY_REPARENT_TO_TASK:
- state.onActivityReparentToTask(event.mActivity);
+ return state.prepareActivityReparentToTask(event.mActivity);
+ default:
+ throw new IllegalArgumentException("Unknown TaskFragmentEvent=" + event.mEventType);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 6527bbb..79037ab 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7041,13 +7041,14 @@
}
public void onOverlayChanged() {
- synchronized (mGlobalLock) {
- mRoot.forAllDisplays(displayContent -> {
- displayContent.getDisplayPolicy().onOverlayChangedLw();
- displayContent.updateDisplayInfo();
- });
- requestTraversal();
- }
+ // Post to display thread so it can get the latest display info.
+ mH.post(() -> {
+ synchronized (mGlobalLock) {
+ mAtmService.deferWindowLayout();
+ mRoot.forAllDisplays(dc -> dc.getDisplayPolicy().onOverlayChanged());
+ mAtmService.continueWindowLayout();
+ }
+ });
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3bc1e35..283010e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2452,8 +2452,7 @@
dc.setImeLayeringTarget(null);
dc.computeImeTarget(true /* updateImeTarget */);
}
- if (dc.getImeInputTarget() == this
- && (mActivityRecord == null || !mActivityRecord.isRelaunching())) {
+ if (dc.getImeInputTarget() == this && !inRelaunchingActivity()) {
dc.updateImeInputAndControlTarget(null);
}
@@ -2580,7 +2579,10 @@
// usually unnoticeable (e.g. covered by rotation animation) and the animation
// bounds could be inconsistent, such as depending on when the window applies
// its draw transaction with new rotation.
- final boolean allowExitAnimation = !getDisplayContent().inTransition();
+ final boolean allowExitAnimation = !getDisplayContent().inTransition()
+ // There will be a new window so the exit animation may not be visible or
+ // look weird if its orientation is changed.
+ && !inRelaunchingActivity();
if (wasVisible) {
final int transit = (!startingWindow) ? TRANSIT_EXIT : TRANSIT_PREVIEW_DONE;
@@ -3870,7 +3872,7 @@
// If the activity is scheduled to relaunch, skip sending the resized to ViewRootImpl now
// since it will be destroyed anyway. This also prevents the client from receiving
// windowing mode change before it is destroyed.
- if (mActivityRecord != null && mActivityRecord.isRelaunching()) {
+ if (inRelaunchingActivity()) {
return;
}
// If this is an activity or wallpaper and is invisible or going invisible, don't report
@@ -3956,6 +3958,10 @@
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
+ boolean inRelaunchingActivity() {
+ return mActivityRecord != null && mActivityRecord.isRelaunching();
+ }
+
boolean isClientLocal() {
return mClient instanceof IWindow.Stub;
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index bbb21f8..72e7e65 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -46,6 +46,7 @@
import android.view.InsetsState;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import android.view.WindowManager.LayoutParams.WindowType;
import android.window.WindowContext;
@@ -558,6 +559,12 @@
// The window may be detached or detaching.
return;
}
+ if (mTransitionController.isShellTransitionsEnabled()
+ && asActivityRecord() != null && isVisible()) {
+ // Trigger an activity level rotation transition.
+ mTransitionController.requestTransitionIfNeeded(WindowManager.TRANSIT_CHANGE, this);
+ mTransitionController.setReady(this);
+ }
final int originalRotation = getWindowConfiguration().getRotation();
onConfigurationChanged(parent.getConfiguration());
onCancelFixedRotationTransform(originalRotation);
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 74ed3df..891bc0f 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -462,10 +462,10 @@
mInputManager->getReader().dump(dump);
dump += "\n";
- mInputManager->getUnwantedInteractionBlocker().dump(dump);
+ mInputManager->getBlocker().dump(dump);
dump += "\n";
- mInputManager->getClassifier().dump(dump);
+ mInputManager->getProcessor().dump(dump);
dump += "\n";
mInputManager->getDispatcher().dump(dump);
@@ -702,7 +702,7 @@
void NativeInputManager::notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) {
ATRACE_CALL();
- mInputManager->getUnwantedInteractionBlocker().notifyInputDevicesChanged(inputDevices);
+ mInputManager->getBlocker().notifyInputDevicesChanged(inputDevices);
JNIEnv* env = jniEnv();
size_t count = inputDevices.size();
@@ -1483,7 +1483,7 @@
}
void NativeInputManager::setMotionClassifierEnabled(bool enabled) {
- mInputManager->getClassifier().setMotionClassifierEnabled(enabled);
+ mInputManager->getProcessor().setMotionClassifierEnabled(enabled);
}
bool NativeInputManager::isPerDisplayTouchModeEnabled() {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b4cade3..c1c7bce 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3552,11 +3552,6 @@
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle));
- if (mInjector.getPackageManagerInternal().filterAppAccess(packageName, caller.getUid(),
- userHandle)) {
- return false;
- }
-
synchronized (getLockObject()) {
DevicePolicyData policy = getUserData(userHandle);
final int N = policy.mAdminList.size();
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index a8b340c..e4f9eaf 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -218,8 +218,8 @@
}).when(mMockWindowManagerInternal).unregisterTaskSystemBarsListener(any());
mRunningTaskInfos = new ArrayList<>();
- when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean())).thenReturn(
- mRunningTaskInfos);
+ when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean(), anyInt()))
+ .thenReturn(mRunningTaskInfos);
final UserHandle userHandle = new UserHandle(USER_ID);
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
new file mode 100644
index 0000000..6a18dc1
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+import android.util.FloatProperty;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.display.RampAnimator.DualRampAnimator;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.testutils.OffsettableClock;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class DisplayPowerControllerTest {
+ private static final String UNIQUE_DISPLAY_ID = "unique_id_test123";
+ private static final int DISPLAY_ID = 42;
+
+ private OffsettableClock mClock;
+ private TestLooper mTestLooper;
+ private Handler mHandler;
+ private DisplayPowerController.Injector mInjector;
+ private Context mContextSpy;
+
+ @Mock
+ private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
+ @Mock
+ private SensorManager mSensorManagerMock;
+ @Mock
+ private DisplayBlanker mDisplayBlankerMock;
+ @Mock
+ private LogicalDisplay mLogicalDisplayMock;
+ @Mock
+ private DisplayDevice mDisplayDeviceMock;
+ @Mock
+ private BrightnessTracker mBrightnessTrackerMock;
+ @Mock
+ private BrightnessSetting mBrightnessSettingMock;
+ @Mock
+ private WindowManagerPolicy mWindowManagerPolicyMock;
+ @Mock
+ private PowerManager mPowerManagerMock;
+ @Mock
+ private Resources mResourcesMock;
+ @Mock
+ private DisplayDeviceConfig mDisplayDeviceConfigMock;
+ @Mock
+ private DisplayPowerState mDisplayPowerStateMock;
+ @Mock
+ private DualRampAnimator<DisplayPowerState> mDualRampAnimatorMock;
+
+ @Captor
+ private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+ mClock = new OffsettableClock.Stopped();
+ mTestLooper = new TestLooper(mClock::now);
+ mHandler = new Handler(mTestLooper.getLooper());
+ mInjector = new DisplayPowerController.Injector() {
+ @Override
+ DisplayPowerController.Clock getClock() {
+ return mClock::now;
+ }
+
+ @Override
+ DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
+ int displayId, int displayState) {
+ return mDisplayPowerStateMock;
+ }
+
+ @Override
+ DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
+ FloatProperty<DisplayPowerState> firstProperty,
+ FloatProperty<DisplayPowerState> secondProperty) {
+ return mDualRampAnimatorMock;
+ }
+ };
+
+ addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
+
+ when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock);
+ when(mContextSpy.getResources()).thenReturn(mResourcesMock);
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+ }
+
+ @Test
+ public void testReleaseProxSuspendBlockersOnExit() throws Exception {
+ setUpDisplay(DISPLAY_ID, UNIQUE_DISPLAY_ID);
+
+ Sensor proxSensor = setUpProxSensor();
+
+ DisplayPowerController dpc = new DisplayPowerController(
+ mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
+ mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
+ mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
+ });
+
+ when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
+ // send a display power request
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ dpr.useProximitySensor = true;
+ dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
+
+ // Run updatePowerState to start listener for the prox sensor
+ advanceTime(1);
+
+ SensorEventListener listener = getSensorEventListener(proxSensor);
+ assertNotNull(listener);
+
+ listener.onSensorChanged(TestUtils.createSensorEvent(proxSensor, 5 /* lux */));
+ advanceTime(1);
+
+ // two times, one for unfinished business and one for proximity
+ verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
+ dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
+ verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
+ dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));
+
+ dpc.stop();
+ advanceTime(1);
+
+ // two times, one for unfinished business and one for proximity
+ verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
+ dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
+ verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
+ dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));
+ }
+
+ /**
+ * Creates a mock and registers it to {@link LocalServices}.
+ */
+ private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService(clazz, mock);
+ }
+
+ private void advanceTime(long timeMs) {
+ mClock.fastForward(timeMs);
+ mTestLooper.dispatchAll();
+ }
+
+ private Sensor setUpProxSensor() throws Exception {
+ Sensor proxSensor = TestUtils.createSensor(
+ Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY);
+ when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL)))
+ .thenReturn(List.of(proxSensor));
+ return proxSensor;
+ }
+
+ private SensorEventListener getSensorEventListener(Sensor sensor) {
+ verify(mSensorManagerMock).registerListener(mSensorEventListenerCaptor.capture(),
+ eq(sensor), eq(SensorManager.SENSOR_DELAY_NORMAL), isA(Handler.class));
+ return mSensorEventListenerCaptor.getValue();
+ }
+
+ private void setUpDisplay(int displayId, String uniqueId) {
+ DisplayInfo info = new DisplayInfo();
+ DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo();
+
+ when(mLogicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId);
+ when(mLogicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(mDisplayDeviceMock);
+ when(mLogicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
+ when(mLogicalDisplayMock.isEnabled()).thenReturn(true);
+ when(mLogicalDisplayMock.getPhase()).thenReturn(LogicalDisplay.DISPLAY_PHASE_ENABLED);
+ when(mDisplayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
+ when(mDisplayDeviceMock.getUniqueId()).thenReturn(uniqueId);
+ when(mDisplayDeviceMock.getDisplayDeviceConfig()).thenReturn(mDisplayDeviceConfigMock);
+ when(mDisplayDeviceConfigMock.getProximitySensor()).thenReturn(
+ new DisplayDeviceConfig.SensorData() {
+ {
+ type = Sensor.STRING_TYPE_PROXIMITY;
+ name = null;
+ }
+ });
+ when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
index 5f9f1b2..bcdfc35 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/PrefetchControllerTest.java
@@ -24,6 +24,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static com.android.server.job.JobSchedulerService.sSystemClock;
@@ -185,13 +186,17 @@
JobStatus js = JobStatus.createFromJobInfo(
jobInfo, callingUid, packageName, SOURCE_USER_ID, testTag);
js.serviceInfo = mock(ServiceInfo.class);
+ js.setStandbyBucket(FREQUENT_INDEX);
// Make sure Doze and background-not-restricted don't affect tests.
js.setDeviceNotDozingConstraintSatisfied(/* nowElapsed */ sElapsedRealtimeClock.millis(),
/* state */ true, /* allowlisted */false);
js.setBackgroundNotRestrictedConstraintSatisfied(
sElapsedRealtimeClock.millis(), true, false);
+ js.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
js.setTareWealthConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
+ js.setExpeditedJobQuotaApproved(sElapsedRealtimeClock.millis(), true);
js.setExpeditedJobTareApproved(sElapsedRealtimeClock.millis(), true);
+ js.setFlexibilityConstraintSatisfied(sElapsedRealtimeClock.millis(), true);
return js;
}
@@ -294,6 +299,7 @@
verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS))
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
assertFalse(job.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertFalse(job.isReady());
}
@Test
@@ -309,6 +315,7 @@
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any());
assertTrue(job.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(job.isReady());
}
@Test
@@ -331,19 +338,25 @@
inOrder.verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS))
.onControllerStateChanged(any());
assertTrue(jobPending.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobPending.isReady());
assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobRunning.isReady());
setUidBias(uid, JobInfo.BIAS_TOP_APP);
// Processing happens on the handler, so wait until we're sure the change has been processed
inOrder.verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS))
.onControllerStateChanged(any());
// Already running job should continue but pending job must wait.
assertFalse(jobPending.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertFalse(jobPending.isReady());
assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobRunning.isReady());
setUidBias(uid, JobInfo.BIAS_DEFAULT);
inOrder.verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS))
.onControllerStateChanged(any());
assertTrue(jobPending.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobPending.isReady());
assertTrue(jobRunning.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobRunning.isReady());
}
@Test
@@ -367,11 +380,13 @@
verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS))
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
assertFalse(jobNonWidget.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertFalse(jobNonWidget.isReady());
when(appWidgetManager.isBoundWidgetPackage(SOURCE_PACKAGE, SOURCE_USER_ID))
.thenReturn(true);
trackJobs(jobWidget);
assertTrue(jobWidget.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobWidget.isReady());
}
@Test
@@ -390,6 +405,7 @@
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any());
assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobStatus.isReady());
mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID,
SOURCE_PACKAGE, sSystemClock.millis() + 10 * HOUR_IN_MILLIS);
@@ -401,6 +417,7 @@
anyInt(), eq(sElapsedRealtimeClock.millis() + 3 * HOUR_IN_MILLIS),
anyLong(), eq(TAG_PREFETCH), any(), any());
assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertFalse(jobStatus.isReady());
}
@Test
@@ -418,6 +435,7 @@
inOrder.verify(mUsageStatsManagerInternal, timeout(DEFAULT_WAIT_MS))
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertFalse(jobStatus.isReady());
mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID,
SOURCE_PACKAGE, sSystemClock.millis() + MINUTE_IN_MILLIS);
@@ -426,6 +444,7 @@
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any());
assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobStatus.isReady());
}
@Test
@@ -448,6 +467,7 @@
anyInt(), eq(sElapsedRealtimeClock.millis() + 3 * HOUR_IN_MILLIS),
anyLong(), eq(TAG_PREFETCH), any(), any());
assertFalse(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertFalse(jobStatus.isReady());
mEstimatedLaunchTimeChangedListener.onEstimatedLaunchTimeChanged(SOURCE_USER_ID,
SOURCE_PACKAGE, sSystemClock.millis() + HOUR_IN_MILLIS);
@@ -456,6 +476,7 @@
.getEstimatedPackageLaunchTime(SOURCE_PACKAGE, SOURCE_USER_ID);
verify(mJobSchedulerService, timeout(DEFAULT_WAIT_MS)).onControllerStateChanged(any());
assertTrue(jobStatus.isConstraintSatisfied(JobStatus.CONSTRAINT_PREFETCH));
+ assertTrue(jobStatus.isReady());
sSystemClock = getShiftedClock(sSystemClock, HOUR_IN_MILLIS + MINUTE_IN_MILLIS);
}
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index a9ee84f..1921ce7 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -135,8 +135,9 @@
java_library {
name: "servicestests-core-utils",
srcs: [
- "src/com/android/server/pm/PackageSettingBuilder.java",
"src/com/android/server/am/DeviceConfigSession.java",
+ "src/com/android/server/display/TestUtils.java",
+ "src/com/android/server/pm/PackageSettingBuilder.java",
],
static_libs: [
"services.core",
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index c80547c..966df4f 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -124,6 +124,8 @@
private VirtualDeviceImpl mDeviceImpl;
private InputController mInputController;
private AssociationInfo mAssociationInfo;
+ private VirtualDeviceManagerService mVdms;
+ private VirtualDeviceManagerInternal mLocalService;
@Mock
private InputController.NativeWrapper mNativeWrapperMock;
@Mock
@@ -139,6 +141,8 @@
@Mock
private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
@Mock
+ private VirtualDeviceManagerInternal.VirtualDisplayListener mDisplayListener;
+ @Mock
IPowerManager mIPowerManagerMock;
@Mock
IThermalService mIThermalServiceMock;
@@ -215,6 +219,9 @@
mAssociationInfo = new AssociationInfo(1, 0, null,
MacAddress.BROADCAST_ADDRESS, "", null, true, false, false, 0, 0);
+ mVdms = new VirtualDeviceManagerService(mContext);
+ mLocalService = mVdms.getLocalServiceInstance();
+
VirtualDeviceParams params = new VirtualDeviceParams
.Builder()
.setBlockedActivities(getBlockedActivities())
@@ -235,6 +242,28 @@
}
@Test
+ public void onVirtualDisplayCreatedLocked_listenersNotified() throws RemoteException {
+ mLocalService.registerVirtualDisplayListener(mDisplayListener);
+
+ mLocalService.onVirtualDisplayCreated(DISPLAY_ID);
+ TestableLooper.get(this).processAllMessages();
+
+ verify(mDisplayListener).onVirtualDisplayCreated(DISPLAY_ID);
+ }
+
+ @Test
+ public void onVirtualDisplayRemovedLocked_listenersNotified() throws RemoteException {
+ mLocalService.registerVirtualDisplayListener(mDisplayListener);
+ mDeviceImpl.onVirtualDisplayCreatedLocked(
+ mDeviceImpl.createWindowPolicyController(), DISPLAY_ID);
+
+ mLocalService.onVirtualDisplayRemoved(mDeviceImpl, DISPLAY_ID);
+ TestableLooper.get(this).processAllMessages();
+
+ verify(mDisplayListener).onVirtualDisplayRemoved(DISPLAY_ID);
+ }
+
+ @Test
public void onVirtualDisplayCreatedLocked_wakeLockIsAcquired() throws RemoteException {
verify(mIPowerManagerMock, never()).acquireWakeLock(any(Binder.class), anyInt(),
nullable(String.class), nullable(String.class), nullable(WorkSource.class),
diff --git a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
index 2e85897..06b4ad9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AssistDataRequesterTest.java
@@ -87,6 +87,7 @@
private static final int TEST_UID = 0;
private static final String TEST_PACKAGE = "";
+ private static final String TEST_ATTRIBUTION_TAG = "";
private AssistDataRequester mDataRequester;
private Callbacks mCallbacks;
@@ -148,9 +149,9 @@
boolean assistScreenshotAllowed) throws Exception {
doReturn(currentActivityAssistAllowed).when(mAtm).isAssistDataAllowedOnCurrentActivity();
doReturn(assistStructureAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
- .checkOpNoThrow(eq(OP_ASSIST_STRUCTURE), anyInt(), anyString());
+ .noteOpNoThrow(eq(OP_ASSIST_STRUCTURE), anyInt(), anyString(), any(), any());
doReturn(assistScreenshotAllowed ? MODE_ALLOWED : MODE_ERRORED).when(mAppOpsManager)
- .checkOpNoThrow(eq(OP_ASSIST_SCREENSHOT), anyInt(), anyString());
+ .noteOpNoThrow(eq(OP_ASSIST_SCREENSHOT), anyInt(), anyString(), any(), any());
}
@Test
@@ -160,7 +161,8 @@
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
assertReceivedDataCount(5, 5, 1, 1);
}
@@ -170,7 +172,8 @@
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(0), FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
assertReceivedDataCount(0, 0, 0, 0);
}
@@ -180,7 +183,8 @@
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
assertReceivedDataCount(0, 1, 0, 1);
}
@@ -191,7 +195,8 @@
mCallbacks.mCanHandleReceivedData = false;
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
assertEquals(5, mDataRequester.getPendingDataCount());
assertEquals(1, mDataRequester.getPendingScreenshotCount());
mGate.countDown();
@@ -226,7 +231,8 @@
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), !FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
assertReceivedDataCount(0, 0, 0, 1);
}
@@ -236,7 +242,8 @@
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
// Expect a single null data when the appops is denied
assertReceivedDataCount(0, 1, 0, 1);
}
@@ -249,7 +256,8 @@
anyBoolean(), anyBoolean());
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
// Expect a single null data when requestAssistContextExtras() fails
assertReceivedDataCount(0, 1, 0, 1);
}
@@ -261,7 +269,8 @@
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, !FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
assertReceivedDataCount(5, 5, 0, 0);
}
@@ -272,7 +281,8 @@
!CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
// Expect a single null screenshot when the appops is denied
assertReceivedDataCount(5, 5, 0, 1);
}
@@ -284,7 +294,8 @@
mCallbacks.mCanHandleReceivedData = false;
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ ALLOW_FETCH_DATA, ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
mGate.countDown();
waitForIdle(mHandler);
assertThat(mCallbacks.mReceivedData).isEmpty();
@@ -297,7 +308,8 @@
CALLER_ASSIST_SCREENSHOT_ALLOWED);
mDataRequester.requestAssistData(createActivityList(5), FETCH_DATA, FETCH_SCREENSHOTS,
- !ALLOW_FETCH_DATA, !ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE);
+ !ALLOW_FETCH_DATA, !ALLOW_FETCH_SCREENSHOTS, TEST_UID, TEST_PACKAGE,
+ TEST_ATTRIBUTION_TAG);
assertReceivedDataCount(0, 1, 0, 1);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 83f375f..adf694c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1531,10 +1531,10 @@
public boolean mLastAllowed;
@Override
- void getTasks(int maxNum, List<RunningTaskInfo> list, int flags,
- RootWindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+ void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
+ WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
mLastAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
- super.getTasks(maxNum, list, flags, root, callingUid, profileIds);
+ super.getTasks(maxNum, list, flags, recentTasks, root, callingUid, profileIds);
}
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index 33b2366..b1acae2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -35,6 +35,8 @@
import androidx.test.filters.MediumTest;
+import com.google.common.truth.Correspondence;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,6 +54,9 @@
public class RunningTasksTest extends WindowTestsBase {
private static final ArraySet<Integer> PROFILE_IDS = new ArraySet<>();
+ private static final Correspondence<RunningTaskInfo, Integer> TASKINFO_HAS_ID =
+ Correspondence.transforming((RunningTaskInfo t) -> t.taskId, "has id");
+
private RunningTasks mRunningTasks;
@@ -91,8 +96,8 @@
// collected from all tasks across all the stacks
final int numFetchTasks = 5;
ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
- mRunningTasks.getTasks(5, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS, mRootWindowContainer,
- -1 /* callingUid */, PROFILE_IDS);
+ mRunningTasks.getTasks(5, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
assertThat(tasks).hasSize(numFetchTasks);
for (int i = 0; i < numFetchTasks; i++) {
assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -102,7 +107,7 @@
// and does not crash
tasks.clear();
mRunningTasks.getTasks(100, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
- mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
assertThat(tasks).hasSize(numTasks);
for (int i = 0; i < numTasks; i++) {
assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -126,7 +131,7 @@
final int numFetchTasks = 5;
final ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
mRunningTasks.getTasks(numFetchTasks, tasks, FLAG_ALLOWED | FLAG_CROSS_USERS,
- mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
assertThat(tasks).hasSize(numFetchTasks);
for (int i = 0; i < tasks.size(); i++) {
final Bundle extras = tasks.get(i).baseIntent.getExtras();
@@ -151,8 +156,8 @@
final int numFetchTasks = 5;
final ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
mRunningTasks.getTasks(numFetchTasks, tasks,
- FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer,
- -1 /* callingUid */, PROFILE_IDS);
+ FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA,
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
assertThat(tasks).hasSize(numFetchTasks);
for (int i = 0; i < tasks.size(); i++) {
final Bundle extras = tasks.get(i).baseIntent.getExtras();
@@ -184,8 +189,8 @@
final int numFetchTasks = 5;
final ArrayList<RunningTaskInfo> fetchTasks = new ArrayList<>();
mRunningTasks.getTasks(numFetchTasks, fetchTasks,
- FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA, mRootWindowContainer,
- -1 /* callingUid */, PROFILE_IDS);
+ FLAG_ALLOWED | FLAG_CROSS_USERS | FLAG_KEEP_INTENT_EXTRA,
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
assertThat(fetchTasks).hasSize(numFetchTasks);
assertEquals(fetchTasks.get(0).id, focusedTask.mTaskId);
assertEquals(fetchTasks.get(1).id, visibleTask.mTaskId);
@@ -210,4 +215,46 @@
task.intent = activity.intent;
return task;
}
+
+ @Test
+ public void testMultipleDisplays() {
+ final DisplayContent display0 = new TestDisplayContent.Builder(mAtm, 1000, 2500).build();
+ final DisplayContent display1 = new TestDisplayContent.Builder(mAtm, 1000, 2500).build();
+ final int numTasks = 10;
+ final ArrayList<Task> tasks = new ArrayList<>();
+ for (int i = 0; i < numTasks; i++) {
+ final Task stack = new TaskBuilder(mSupervisor)
+ .setDisplay(i % 2 == 0 ? display0 : display1)
+ .setOnTop(true)
+ .build();
+ final Task task = createTask(stack, ".Task" + i, i, i, null);
+ tasks.add(task);
+ }
+
+ final int numFetchTasks = numTasks;
+ final ArrayList<RunningTaskInfo> fetchTasks = new ArrayList<>();
+
+ mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+ FLAG_ALLOWED | FLAG_CROSS_USERS,
+ mAtm.getRecentTasks(), display0, -1 /* callingUid */, PROFILE_IDS);
+ assertThat(fetchTasks).hasSize(numTasks / 2);
+ assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+ .containsExactly(0, 2, 4, 6, 8);
+
+ fetchTasks.clear();
+ mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+ FLAG_ALLOWED | FLAG_CROSS_USERS,
+ mAtm.getRecentTasks(), display1, -1 /* callingUid */, PROFILE_IDS);
+ assertThat(fetchTasks).hasSize(numTasks / 2);
+ assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+ .containsExactly(1, 3, 5, 7, 9);
+
+ fetchTasks.clear();
+ mRunningTasks.getTasks(numFetchTasks, fetchTasks,
+ FLAG_ALLOWED | FLAG_CROSS_USERS,
+ mAtm.getRecentTasks(), mRootWindowContainer, -1 /* callingUid */, PROFILE_IDS);
+ assertThat(fetchTasks).hasSize(numTasks);
+ assertThat(fetchTasks).comparingElementsUsing(TASKINFO_HAS_ID)
+ .containsExactly(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index eba2755..f38731b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -199,9 +199,11 @@
final Task parent = mock(Task.class);
final Configuration parentConfig = new Configuration();
parentConfig.smallestScreenWidthDp = 10;
- doReturn(parent).when(mTaskFragment).getParent();
+ doReturn(parent).when(mTaskFragment).getTask();
doReturn(parentConfig).when(parent).getConfiguration();
- doReturn(parent).when(parent).asTask();
+ // Task needs to be visible
+ parent.lastActiveTime = 100;
+ doReturn(true).when(parent).shouldBeVisible(any());
mTaskFragment.mTaskFragmentAppearedSent = true;
mController.onTaskFragmentParentInfoChanged(
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 7699262..4b9b4a9 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -102,6 +102,7 @@
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.soundtrigger.SoundTriggerInternal;
+import com.android.server.utils.Slogf;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -134,18 +135,21 @@
private final RemoteCallbackList<IVoiceInteractionSessionListener>
mVoiceInteractionSessionListeners = new RemoteCallbackList<>();
+ // TODO(b/226201975): remove once RoleService supports pre-created users
+ private final ArrayList<UserHandle> mIgnoredPreCreatedUsers = new ArrayList<>();
+
public VoiceInteractionManagerService(Context context) {
super(context);
mContext = context;
mResolver = context.getContentResolver();
+ mUserManagerInternal = Objects.requireNonNull(
+ LocalServices.getService(UserManagerInternal.class));
mDbHelper = new DatabaseHelper(context);
mServiceStub = new VoiceInteractionManagerServiceStub();
mAmInternal = Objects.requireNonNull(
LocalServices.getService(ActivityManagerInternal.class));
mAtmInternal = Objects.requireNonNull(
LocalServices.getService(ActivityTaskManagerInternal.class));
- mUserManagerInternal = Objects.requireNonNull(
- LocalServices.getService(UserManagerInternal.class));
LegacyPermissionManagerInternal permissionManagerInternal = LocalServices.getService(
LegacyPermissionManagerInternal.class);
@@ -223,12 +227,13 @@
class LocalService extends VoiceInteractionManagerInternal {
@Override
- public void startLocalVoiceInteraction(IBinder callingActivity, Bundle options) {
+ public void startLocalVoiceInteraction(@NonNull IBinder callingActivity,
+ @Nullable String attributionTag, @NonNull Bundle options) {
if (DEBUG) {
Slog.i(TAG, "startLocalVoiceInteraction " + callingActivity);
}
VoiceInteractionManagerService.this.mServiceStub.startLocalVoiceInteraction(
- callingActivity, options);
+ callingActivity, attributionTag, options);
}
@Override
@@ -300,6 +305,25 @@
}
return hotwordDetectionConnection.mIdentity;
}
+
+ @Override
+ public void onPreCreatedUserConversion(int userId) {
+ Slogf.d(TAG, "onPreCreatedUserConversion(%d)", userId);
+
+ for (int i = 0; i < mIgnoredPreCreatedUsers.size(); i++) {
+ UserHandle preCreatedUser = mIgnoredPreCreatedUsers.get(i);
+ if (preCreatedUser.getIdentifier() == userId) {
+ Slogf.d(TAG, "Updating role on pre-created user %d", userId);
+ mServiceStub.mRoleObserver.onRoleHoldersChanged(RoleManager.ROLE_ASSISTANT,
+ preCreatedUser);
+ mIgnoredPreCreatedUsers.remove(i);
+ return;
+ }
+ }
+ Slogf.w(TAG, "onPreCreatedUserConversion(%d): not available on "
+ + "mIgnoredPreCreatedUserIds (%s)", userId, mIgnoredPreCreatedUsers);
+ }
+
}
// implementation entry point and binder service
@@ -317,10 +341,12 @@
private boolean mTemporarilyDisabled;
private final boolean mEnableService;
+ // TODO(b/226201975): remove reference once RoleService supports pre-created users
+ private final RoleObserver mRoleObserver;
VoiceInteractionManagerServiceStub() {
mEnableService = shouldEnableService(mContext);
- new RoleObserver(mContext.getMainExecutor());
+ mRoleObserver = new RoleObserver(mContext.getMainExecutor());
}
void handleUserStop(String packageName, int userHandle) {
@@ -383,14 +409,15 @@
}
// TODO: VI Make sure the caller is the current user or profile
- void startLocalVoiceInteraction(final IBinder token, Bundle options) {
+ void startLocalVoiceInteraction(@NonNull final IBinder token,
+ @Nullable String attributionTag, @NonNull Bundle options) {
if (mImpl == null) return;
final int callingUid = Binder.getCallingUid();
final long caller = Binder.clearCallingIdentity();
try {
mImpl.showSessionLocked(options,
- VoiceInteractionSession.SHOW_SOURCE_ACTIVITY,
+ VoiceInteractionSession.SHOW_SOURCE_ACTIVITY, attributionTag,
new IVoiceInteractionSessionShowCallback.Stub() {
@Override
public void onFailed() {
@@ -898,13 +925,13 @@
}
@Override
- public void showSession(Bundle args, int flags) {
+ public void showSession(@NonNull Bundle args, int flags, @Nullable String attributionTag) {
synchronized (this) {
enforceIsCurrentVoiceInteractionService();
final long caller = Binder.clearCallingIdentity();
try {
- mImpl.showSessionLocked(args, flags, null, null);
+ mImpl.showSessionLocked(args, flags, attributionTag, null, null);
} finally {
Binder.restoreCallingIdentity(caller);
}
@@ -929,7 +956,8 @@
}
@Override
- public boolean showSessionFromSession(IBinder token, Bundle sessionArgs, int flags) {
+ public boolean showSessionFromSession(@NonNull IBinder token, @NonNull Bundle sessionArgs,
+ int flags, @Nullable String attributionTag) {
synchronized (this) {
if (mImpl == null) {
Slog.w(TAG, "showSessionFromSession without running voice interaction service");
@@ -937,7 +965,7 @@
}
final long caller = Binder.clearCallingIdentity();
try {
- return mImpl.showSessionLocked(sessionArgs, flags, null, null);
+ return mImpl.showSessionLocked(sessionArgs, flags, attributionTag, null, null);
} finally {
Binder.restoreCallingIdentity(caller);
}
@@ -961,8 +989,8 @@
}
@Override
- public int startVoiceActivity(IBinder token, Intent intent, String resolvedType,
- String callingFeatureId) {
+ public int startVoiceActivity(@NonNull IBinder token, @NonNull Intent intent,
+ @Nullable String resolvedType, @Nullable String attributionTag) {
synchronized (this) {
if (mImpl == null) {
Slog.w(TAG, "startVoiceActivity without running voice interaction service");
@@ -980,8 +1008,8 @@
} else {
Slog.w(TAG, "Cannot find ActivityInfo in startVoiceActivity.");
}
- return mImpl.startVoiceActivityLocked(
- callingFeatureId, callingPid, callingUid, token, intent, resolvedType);
+ return mImpl.startVoiceActivityLocked(attributionTag, callingPid, callingUid,
+ token, intent, resolvedType);
} finally {
Binder.restoreCallingIdentity(caller);
}
@@ -989,8 +1017,8 @@
}
@Override
- public int startAssistantActivity(IBinder token, Intent intent, String resolvedType,
- String callingFeatureId) {
+ public int startAssistantActivity(@NonNull IBinder token, @NonNull Intent intent,
+ @Nullable String resolvedType, @Nullable String attributionTag) {
synchronized (this) {
if (mImpl == null) {
Slog.w(TAG, "startAssistantActivity without running voice interaction service");
@@ -1000,7 +1028,7 @@
final int callingUid = Binder.getCallingUid();
final long caller = Binder.clearCallingIdentity();
try {
- return mImpl.startAssistantActivityLocked(callingFeatureId, callingPid,
+ return mImpl.startAssistantActivityLocked(attributionTag, callingPid,
callingUid, token, intent, resolvedType);
} finally {
Binder.restoreCallingIdentity(caller);
@@ -1669,8 +1697,10 @@
@android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE)
@Override
- public boolean showSessionForActiveService(Bundle args, int sourceFlags,
- IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
+ public boolean showSessionForActiveService(@NonNull Bundle args, int sourceFlags,
+ @Nullable String attributionTag,
+ @Nullable IVoiceInteractionSessionShowCallback showCallback,
+ @Nullable IBinder activityToken) {
if (DEBUG_USER) Slog.d(TAG, "showSessionForActiveService()");
synchronized (this) {
@@ -1691,7 +1721,7 @@
sourceFlags
| VoiceInteractionSession.SHOW_WITH_ASSIST
| VoiceInteractionSession.SHOW_WITH_SCREENSHOT,
- showCallback, activityToken);
+ attributionTag, showCallback, activityToken);
} finally {
Binder.restoreCallingIdentity(caller);
}
@@ -1882,6 +1912,7 @@
pw.println(" mTemporarilyDisabled: " + mTemporarilyDisabled);
pw.println(" mCurUser: " + mCurUser);
pw.println(" mCurUserSupported: " + mCurUserSupported);
+ pw.println(" mIgnoredPreCreatedUsers: " + mIgnoredPreCreatedUsers);
dumpSupportedUsers(pw, " ");
mDbHelper.dump(pw);
if (mImpl == null) {
@@ -1995,6 +2026,23 @@
List<String> roleHolders = mRm.getRoleHoldersAsUser(roleName, user);
+ // TODO(b/226201975): this method is beling called when a pre-created user is added,
+ // at which point it doesn't have any role holders. But it's not called again when
+ // the actual user is added (i.e., when the pre-created user is converted), so we
+ // need to save the user id and call this method again when it's converted
+ // (at onPreCreatedUserConversion()).
+ // Once RoleService properly handles pre-created users, this workaround should be
+ // removed.
+ if (roleHolders.isEmpty()) {
+ UserInfo userInfo = mUserManagerInternal.getUserInfo(user.getIdentifier());
+ if (userInfo != null && userInfo.preCreated) {
+ Slogf.d(TAG, "onRoleHoldersChanged(): ignoring pre-created user %s for now",
+ userInfo.toFullString());
+ mIgnoredPreCreatedUsers.add(user);
+ return;
+ }
+ }
+
int userId = user.getIdentifier();
if (roleHolders.isEmpty()) {
Settings.Secure.putStringForUser(getContext().getContentResolver(),
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index b9793ca..9f66059 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -244,8 +244,10 @@
/* direct= */ true);
}
- public boolean showSessionLocked(Bundle args, int flags,
- IVoiceInteractionSessionShowCallback showCallback, IBinder activityToken) {
+ public boolean showSessionLocked(@NonNull Bundle args, int flags,
+ @Nullable String attributionTag,
+ @Nullable IVoiceInteractionSessionShowCallback showCallback,
+ @Nullable IBinder activityToken) {
if (mActiveSession == null) {
mActiveSession = new VoiceInteractionSessionConnection(mServiceStub,
mSessionComponentName, mUser, mContext, this,
@@ -269,8 +271,8 @@
} else {
visibleActivities = allVisibleActivities;
}
- return mActiveSession.showLocked(args, flags, mDisabledShowContext, showCallback,
- visibleActivities);
+ return mActiveSession.showLocked(args, flags, attributionTag, mDisabledShowContext,
+ showCallback, visibleActivities);
}
public void getActiveServiceSupportedActions(List<String> commands,
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
index 9bdf4e4..80f5dc5 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceShellCommand.java
@@ -114,8 +114,8 @@
try {
Bundle args = new Bundle();
- boolean ok = mService.showSessionForActiveService(args, /* sourceFlags= */ 0, callback,
- /* activityToken= */ null);
+ boolean ok = mService.showSessionForActiveService(args, /* sourceFlags= */ 0,
+ /* attributionTag= */ null, callback, /* activityToken= */ null);
if (!ok) {
pw.println("showSessionForActiveService() returned false");
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 093e976..63781cc 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -29,6 +29,8 @@
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_TASK_ID;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
@@ -263,9 +265,9 @@
return flags;
}
- public boolean showLocked(Bundle args, int flags, int disabledContext,
- IVoiceInteractionSessionShowCallback showCallback,
- List<ActivityAssistInfo> topActivities) {
+ public boolean showLocked(@NonNull Bundle args, int flags, @Nullable String attributionTag,
+ int disabledContext, @Nullable IVoiceInteractionSessionShowCallback showCallback,
+ @NonNull List<ActivityAssistInfo> topActivities) {
if (mBound) {
if (!mFullyBound) {
mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
@@ -291,12 +293,15 @@
for (int i = 0; i < topActivitiesCount; i++) {
topActivitiesToken.add(topActivities.get(i).getActivityToken());
}
+
mAssistDataRequester.requestAssistData(topActivitiesToken,
fetchData,
fetchScreenshot,
(disabledContext & VoiceInteractionSession.SHOW_WITH_ASSIST) == 0,
(disabledContext & VoiceInteractionSession.SHOW_WITH_SCREENSHOT) == 0,
- mCallingUid, mSessionComponentName.getPackageName());
+ mCallingUid,
+ mSessionComponentName.getPackageName(),
+ attributionTag);
boolean needDisclosure = mAssistDataRequester.getPendingDataCount() > 0
|| mAssistDataRequester.getPendingScreenshotCount() > 0;
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index 4469ffc..7eec86a 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -115,15 +115,15 @@
/** @hide */
public static @RadioAccessNetworkType int fromString(@NonNull String str) {
switch (str.toUpperCase()) {
- case "GERAN" : return GERAN;
- case "UTRAN" : return UTRAN;
- case "EUTRAN" : return EUTRAN;
- case "CDMA2000" : return CDMA2000;
- case "IWLAN" : return IWLAN;
- case "NGRAN" : return NGRAN;
+ case "UNKNOWN": return UNKNOWN;
+ case "GERAN": return GERAN;
+ case "UTRAN": return UTRAN;
+ case "EUTRAN": return EUTRAN;
+ case "CDMA2000": return CDMA2000;
+ case "IWLAN": return IWLAN;
+ case "NGRAN": return NGRAN;
default:
- Rlog.e(TAG, "Invalid access network type " + str);
- return UNKNOWN;
+ throw new IllegalArgumentException("Invalid access network type " + str);
}
}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 6deb6f8..4d18dfe 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -8596,6 +8596,13 @@
* IWLAN handover rules that determine whether handover is allowed or disallowed between
* cellular and IWLAN.
*
+ * Rule syntax: "source=[GERAN|UTRAN|EUTRAN|NGRAN|IWLAN|UNKNOWN], target=[GERAN|UTRAN|EUTRAN
+ * |NGRAN|IWLAN], type=[allowed|disallowed], roaming=[true|false], capabilities=[INTERNET|MMS
+ * |FOTA|IMS|CBS|SUPL|EIMS|XCAP|DUN]"
+ *
+ * Note that UNKNOWN can be only specified in the source access network and can be only used
+ * in the disallowed rule.
+ *
* The handover rules will be matched in the order. Here are some sample rules.
* <string-array name="iwlan_handover_rules" num="5">
* <!-- Handover from IWLAN to 2G/3G is not allowed -->
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt
new file mode 100644
index 0000000..bd0ae4d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/CameraAppHelper.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.provider.MediaStore
+import com.android.server.wm.traces.common.ComponentMatcher
+import com.android.server.wm.traces.common.IComponentMatcher
+
+class CameraAppHelper @JvmOverloads constructor(
+ instrumentation: Instrumentation,
+ private val pkgManager: PackageManager = instrumentation.context.packageManager
+) : StandardAppHelper(instrumentation, getCameraLauncherName(pkgManager),
+ getCameraComponent(pkgManager)){
+ companion object{
+ private fun getCameraIntent(): Intent {
+ return Intent(MediaStore.ACTION_IMAGE_CAPTURE)
+ }
+
+ private fun getResolveInfo(pkgManager: PackageManager): ResolveInfo {
+ val intent = getCameraIntent()
+ return pkgManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY)
+ ?: error("unable to resolve camera activity")
+ }
+
+ private fun getCameraComponent(pkgManager: PackageManager): IComponentMatcher {
+ val resolveInfo = getResolveInfo(pkgManager)
+ return ComponentMatcher(resolveInfo.activityInfo.packageName,
+ className = resolveInfo.activityInfo.name)
+ }
+
+ private fun getCameraLauncherName(pkgManager: PackageManager): String {
+ val resolveInfo = getResolveInfo(pkgManager)
+ return resolveInfo.activityInfo.name
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
new file mode 100644
index 0000000..3ff59e9
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.launch
+
+import android.platform.test.annotations.FlakyTest
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.RequiresDevice
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.annotation.Group1
+import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.CameraAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching an app after cold opening camera
+ *
+ * To run this test: `atest FlickerTests:OpenAppAfterCameraTest`
+ *
+ * Notes:
+ * Some default assertions are inherited [OpenAppTransition]
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group1
+open class OpenAppAfterCameraTest(
+ testSpec: FlickerTestParameter
+) : OpenAppFromLauncherTransition(testSpec) {
+ private val cameraApp = CameraAppHelper(instrumentation) /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit
+ get() = {
+ super.transition(this)
+ setup {
+ test{
+ tapl.setExpectedRotationCheckEnabled(false)
+ }
+ eachRun {
+ // 1. Open camera - cold -> close it first
+ cameraApp.exit(wmHelper)
+ cameraApp.launchViaIntent(wmHelper)
+ // 2. Press home button (button nav mode) / swipe up to home (gesture nav mode)
+ tapl.goHome()
+ }
+ }
+ teardown {
+ eachRun {
+ testApp.exit(wmHelper)
+ }
+ }
+ transitions {
+ testApp.launchViaIntent(wmHelper)
+ }
+ }
+
+ /** {@inheritDoc} */
+ @FlakyTest(bugId = 206753786)
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun focusChanges() = super.focusChanges()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appWindowReplacesLauncherAsTopWindow() =
+ super.appWindowReplacesLauncherAsTopWindow()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appWindowAsTopWindowAtEnd() = super.appWindowAsTopWindowAtEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appLayerBecomesVisible() = super.appLayerBecomesVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appWindowBecomesVisible() = super.appWindowBecomesVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun appWindowIsTopWindowAtEnd() = super.appWindowIsTopWindowAtEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun entireScreenCovered() = super.entireScreenCovered()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() = super.navBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() =
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() = super.statusBarLayerPositionAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ companion object {
+ /**
+ * Creates the test configurations.
+ *
+ * See [FlickerTestParameterFactory.getConfigNonRotationTests] for configuring
+ * repetitions, screen orientation and navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests()
+ }
+ }
+}