Merge "Move the compose code to tm-qpr-dev (1/2)" into tm-qpr-dev
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 57dacd0..877e7d3 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -263,11 +263,6 @@
// After orientation change, the onResume can come in before the top Activity has
// left, so if the Activity is not top, wait a second for the top Activity to exit.
if (mEnterTransitionCoordinator == null || activity.isTopOfTask()) {
- if (mEnterTransitionCoordinator != null) {
- mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> {
- mEnterTransitionCoordinator = null;
- });
- }
restoreExitedViews();
restoreReenteringViews();
} else {
@@ -276,11 +271,6 @@
public void run() {
if (mEnterTransitionCoordinator == null ||
mEnterTransitionCoordinator.isWaitingForRemoteExit()) {
- if (mEnterTransitionCoordinator != null) {
- mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> {
- mEnterTransitionCoordinator = null;
- });
- }
restoreExitedViews();
restoreReenteringViews();
} else if (mEnterTransitionCoordinator.isReturning()) {
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index 8be2b48..e3bca9c 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -103,7 +103,6 @@
private boolean mOnLightBackground;
private SizeF mCurrentSize = null;
private RemoteViews.ColorResources mColorResources = null;
- private SparseIntArray mColorMapping = null;
// Stores the last remote views last inflated.
private RemoteViews mLastInflatedRemoteViews = null;
private long mLastInflatedRemoteViewsId = -1;
@@ -900,11 +899,19 @@
* {@link android.R.color#system_neutral1_500}.
*/
public void setColorResources(@NonNull SparseIntArray colorMapping) {
- if (mColorMapping != null && isSameColorMapping(mColorMapping, colorMapping)) {
+ if (mColorResources != null
+ && isSameColorMapping(mColorResources.getColorMapping(), colorMapping)) {
return;
}
- mColorMapping = colorMapping.clone();
- mColorResources = RemoteViews.ColorResources.create(mContext, mColorMapping);
+ setColorResources(RemoteViews.ColorResources.create(mContext, colorMapping));
+ }
+
+ /** @hide **/
+ public void setColorResources(RemoteViews.ColorResources colorResources) {
+ if (colorResources == mColorResources) {
+ return;
+ }
+ mColorResources = colorResources;
mColorMappingChanged = true;
mViewMode = VIEW_MODE_NOINIT;
reapplyLastRemoteViews();
@@ -934,7 +941,6 @@
public void resetColorResources() {
if (mColorResources != null) {
mColorResources = null;
- mColorMapping = null;
mColorMappingChanged = true;
mViewMode = VIEW_MODE_NOINIT;
reapplyLastRemoteViews();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7690af6..fa37ca1 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -276,7 +276,7 @@
* @hide
*/
public static final boolean CAPTION_ON_SHELL =
- SystemProperties.getBoolean("persist.debug.caption_on_shell", false);
+ SystemProperties.getBoolean("persist.wm.debug.caption_on_shell", false);
/**
* Whether the client should compute the window frame on its own.
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 2879cd8..5eec054 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -594,8 +594,8 @@
* SUBCLASSES MUST BE IMMUTABLE SO CLONE WORKS!!!!!
*/
private abstract static class Action implements Parcelable {
- public abstract void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException;
+ public abstract void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+ throws ActionException;
public static final int MERGE_REPLACE = 0;
public static final int MERGE_APPEND = 1;
@@ -626,7 +626,7 @@
* Override this if some of the tasks can be performed async.
*/
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
return this;
}
@@ -661,9 +661,7 @@
// Constant used during async execution. It is not parcelable.
private static final Action ACTION_NOOP = new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
- }
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) { }
};
/**
@@ -798,8 +796,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View view = root.findViewById(viewId);
if (!(view instanceof AdapterView<?>)) return;
@@ -834,8 +831,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -846,7 +842,7 @@
OnItemClickListener listener = (parent, view, position, id) -> {
RemoteResponse response = findRemoteResponseTag(view);
if (response != null) {
- response.handleViewInteraction(view, handler);
+ response.handleViewInteraction(view, params.handler);
}
};
av.setOnItemClickListener(listener);
@@ -910,8 +906,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -935,7 +930,7 @@
((RemoteViewsListAdapter) a).setViewsList(list);
} else {
v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
- colorResources));
+ params.colorResources));
}
} else if (target instanceof AdapterViewAnimator) {
AdapterViewAnimator v = (AdapterViewAnimator) target;
@@ -944,7 +939,7 @@
((RemoteViewsListAdapter) a).setViewsList(list);
} else {
v.setAdapter(new RemoteViewsListAdapter(v.getContext(), list, viewTypeCount,
- colorResources));
+ params.colorResources));
}
}
}
@@ -1025,8 +1020,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+ throws ActionException {
View target = root.findViewById(viewId);
if (target == null) return;
@@ -1053,7 +1048,7 @@
&& adapter.getViewTypeCount() >= mItems.getViewTypeCount()) {
try {
((RemoteCollectionItemsAdapter) adapter).setData(
- mItems, handler, colorResources);
+ mItems, params.handler, params.colorResources);
} catch (Throwable throwable) {
// setData should never failed with the validation in the items builder, but if
// it does, catch and rethrow.
@@ -1063,8 +1058,8 @@
}
try {
- adapterView.setAdapter(
- new RemoteCollectionItemsAdapter(mItems, handler, colorResources));
+ adapterView.setAdapter(new RemoteCollectionItemsAdapter(mItems,
+ params.handler, params.colorResources));
} catch (Throwable throwable) {
// This could throw if the AdapterView somehow doesn't accept BaseAdapter due to
// a type error.
@@ -1095,8 +1090,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -1124,17 +1118,17 @@
if (target instanceof AbsListView) {
AbsListView v = (AbsListView) target;
v.setRemoteViewsAdapter(intent, isAsync);
- v.setRemoteViewsInteractionHandler(handler);
+ v.setRemoteViewsInteractionHandler(params.handler);
} else if (target instanceof AdapterViewAnimator) {
AdapterViewAnimator v = (AdapterViewAnimator) target;
v.setRemoteViewsAdapter(intent, isAsync);
- v.setRemoteViewsOnClickHandler(handler);
+ v.setRemoteViewsOnClickHandler(params.handler);
}
}
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
SetRemoteViewsAdapterIntent copy = new SetRemoteViewsAdapterIntent(viewId, intent);
copy.isAsync = true;
return copy;
@@ -1173,8 +1167,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -1215,7 +1208,7 @@
target.setTagInternal(com.android.internal.R.id.fillInIntent, null);
return;
}
- target.setOnClickListener(v -> mResponse.handleViewInteraction(v, handler));
+ target.setOnClickListener(v -> mResponse.handleViewInteraction(v, params.handler));
}
@Override
@@ -1253,8 +1246,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, final InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
if (!(target instanceof CompoundButton)) {
@@ -1287,7 +1279,7 @@
}
OnCheckedChangeListener onCheckedChangeListener =
- (v, isChecked) -> mResponse.handleViewInteraction(v, handler);
+ (v, isChecked) -> mResponse.handleViewInteraction(v, params.handler);
button.setTagInternal(R.id.remote_checked_change_listener_tag, onCheckedChangeListener);
button.setOnCheckedChangeListener(onCheckedChangeListener);
}
@@ -1459,8 +1451,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -1517,8 +1508,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -1561,8 +1551,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View view = root.findViewById(viewId);
if (view == null) return;
@@ -1675,12 +1664,12 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+ throws ActionException {
ReflectionAction ra = new ReflectionAction(viewId, methodName,
BaseReflectionAction.BITMAP,
bitmap);
- ra.apply(root, rootParent, handler, colorResources);
+ ra.apply(root, rootParent, params);
}
@Override
@@ -1756,8 +1745,7 @@
protected abstract Object getParameterValue(@Nullable View view) throws ActionException;
@Override
- public final void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public final void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View view = root.findViewById(viewId);
if (view == null) return;
@@ -1775,7 +1763,7 @@
@Override
public final Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
final View view = root.findViewById(viewId);
if (view == null) return ACTION_NOOP;
@@ -2307,8 +2295,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
mRunnable.run();
}
}
@@ -2421,8 +2408,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final Context context = root.getContext();
final ViewGroup target = root.findViewById(viewId);
@@ -2451,8 +2437,7 @@
target.removeViews(nextChild, recycledViewIndex - nextChild);
}
setNextRecyclableChild(target, nextChild + 1, target.getChildCount());
- rvToApply.reapplyNestedViews(context, child, rootParent, handler,
- null /* size */, colorResources);
+ rvToApply.reapplyNestedViews(context, child, rootParent, params);
return;
}
// If we cannot recycle the views, we still remove all views in between to
@@ -2463,8 +2448,7 @@
// If we cannot recycle, insert the new view before the next recyclable child.
// Inflate nested views and add as children
- View nestedView = rvToApply.applyNestedViews(context, target, rootParent, handler,
- null /* size */, colorResources);
+ View nestedView = rvToApply.apply(context, target, rootParent, null /* size */, params);
if (mStableId != NO_ID) {
setStableId(nestedView, mStableId);
}
@@ -2477,7 +2461,7 @@
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
// In the async implementation, update the view tree so that subsequent calls to
// findViewById return the current view.
root.createTree();
@@ -2511,8 +2495,7 @@
setNextRecyclableChild(targetVg, nextChild + 1, target.mChildren.size());
final AsyncApplyTask reapplyTask = rvToApply.getInternalAsyncApplyTask(
context,
- targetVg, null /* listener */, handler, null /* size */,
- colorResources,
+ targetVg, null /* listener */, params, null /* size */,
recycled.mRoot);
final ViewTree tree = reapplyTask.doInBackground();
if (tree == null) {
@@ -2521,8 +2504,7 @@
return new RuntimeAction() {
@Override
public void apply(View root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources)
- throws ActionException {
+ ActionApplyParams params) throws ActionException {
reapplyTask.onPostExecute(tree);
if (recycledViewIndex > nextChild) {
targetVg.removeViews(nextChild, recycledViewIndex - nextChild);
@@ -2533,23 +2515,22 @@
// If the layout id is different, still remove the children as if we recycled
// the view, to insert at the same place.
target.removeChildren(nextChild, recycledViewIndex - nextChild + 1);
- return insertNewView(context, target, handler, colorResources,
+ return insertNewView(context, target, params,
() -> targetVg.removeViews(nextChild,
recycledViewIndex - nextChild + 1));
}
}
// If we cannot recycle, simply add the view at the same available slot.
- return insertNewView(context, target, handler, colorResources, () -> {});
+ return insertNewView(context, target, params, () -> {});
}
- private Action insertNewView(Context context, ViewTree target, InteractionHandler handler,
- ColorResources colorResources, Runnable finalizeAction) {
+ private Action insertNewView(Context context, ViewTree target,
+ ActionApplyParams params, Runnable finalizeAction) {
ViewGroup targetVg = (ViewGroup) target.mRoot;
int nextChild = getNextRecyclableChild(targetVg);
final AsyncApplyTask task = mNestedViews.getInternalAsyncApplyTask(context, targetVg,
- null /* listener */, handler, null /* size */, colorResources,
- null /* result */);
+ null /* listener */, params, null /* size */, null /* result */);
final ViewTree tree = task.doInBackground();
if (tree == null) {
@@ -2569,8 +2550,7 @@
return new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
task.onPostExecute(tree);
finalizeAction.run();
targetVg.addView(task.mResult, insertIndex);
@@ -2627,8 +2607,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final ViewGroup target = root.findViewById(viewId);
if (target == null) {
@@ -2652,7 +2631,7 @@
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
// In the async implementation, update the view tree so that subsequent calls to
// findViewById return the current view.
root.createTree();
@@ -2676,8 +2655,7 @@
}
return new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
for (int i = targetVg.getChildCount() - 1; i >= 0; i--) {
if (!hasStableId(targetVg.getChildAt(i))) {
@@ -2736,8 +2714,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null || target == root) {
@@ -2752,7 +2729,7 @@
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
// In the async implementation, update the view tree so that subsequent calls to
// findViewById return the correct view.
root.createTree();
@@ -2771,8 +2748,7 @@
parent.mChildren.remove(target);
return new RuntimeAction() {
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
parentVg.removeView(target.mRoot);
}
};
@@ -2851,8 +2827,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final TextView target = root.findViewById(viewId);
if (target == null) return;
if (drawablesLoaded) {
@@ -2883,7 +2858,7 @@
@Override
public Action initActionAsync(ViewTree root, ViewGroup rootParent,
- InteractionHandler handler, ColorResources colorResources) {
+ ActionApplyParams params) {
final TextView target = root.findViewById(viewId);
if (target == null) return ACTION_NOOP;
@@ -2961,8 +2936,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final TextView target = root.findViewById(viewId);
if (target == null) return;
target.setTextSize(units, size);
@@ -3007,8 +2981,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
target.setPadding(left, top, right, bottom);
@@ -3084,8 +3057,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) {
return;
@@ -3230,8 +3202,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -3266,8 +3237,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
// Let's traverse the viewtree and override all textColors!
Stack<View> viewsToProcess = new Stack<>();
viewsToProcess.add(root);
@@ -3317,8 +3287,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params) {
final View target = root.findViewById(mViewId);
if (target == null) return;
@@ -3352,8 +3321,7 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources)
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
throws ActionException {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -3404,8 +3372,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+ throws ActionException {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -3483,8 +3451,8 @@
}
@Override
- public void apply(View root, ViewGroup rootParent, InteractionHandler handler,
- ColorResources colorResources) throws ActionException {
+ public void apply(View root, ViewGroup rootParent, ActionApplyParams params)
+ throws ActionException {
final View target = root.findViewById(viewId);
if (target == null) return;
@@ -5578,52 +5546,39 @@
/** @hide */
public View apply(@NonNull Context context, @NonNull ViewGroup parent,
@Nullable InteractionHandler handler, @Nullable SizeF size) {
- RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
- View result = inflateView(context, rvToApply, parent);
- rvToApply.performApply(result, parent, handler, null);
- return result;
+ return apply(context, parent, size, new ActionApplyParams()
+ .withInteractionHandler(handler));
}
/** @hide */
public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
@Nullable InteractionHandler handler, @StyleRes int applyThemeResId) {
- return applyWithTheme(context, parent, handler, applyThemeResId, null);
- }
-
- /** @hide */
- public View applyWithTheme(@NonNull Context context, @NonNull ViewGroup parent,
- @Nullable InteractionHandler handler, @StyleRes int applyThemeResId,
- @Nullable SizeF size) {
- RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
- View result = inflateView(context, rvToApply, parent, applyThemeResId, null);
- rvToApply.performApply(result, parent, handler, null);
- return result;
+ return apply(context, parent, null, new ActionApplyParams()
+ .withInteractionHandler(handler)
+ .withThemeResId(applyThemeResId));
}
/** @hide */
public View apply(Context context, ViewGroup parent, InteractionHandler handler,
@Nullable SizeF size, @Nullable ColorResources colorResources) {
- RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
- View result = inflateView(context, rvToApply, parent, 0, colorResources);
- rvToApply.performApply(result, parent, handler, colorResources);
- return result;
+ return apply(context, parent, size, new ActionApplyParams()
+ .withInteractionHandler(handler)
+ .withColorResources(colorResources));
}
- private View applyNestedViews(Context context, ViewGroup directParent,
- ViewGroup rootParent, InteractionHandler handler, SizeF size,
- ColorResources colorResources) {
- RemoteViews rvToApply = getRemoteViewsToApply(context, size);
-
- View result = inflateView(context, rvToApply, directParent, 0, colorResources);
- rvToApply.performApply(result, rootParent, handler, colorResources);
- return result;
+ /** @hide **/
+ public View apply(Context context, ViewGroup parent, @Nullable SizeF size,
+ ActionApplyParams params) {
+ return apply(context, parent, parent, size, params);
}
- private View inflateView(Context context, RemoteViews rv, ViewGroup parent) {
- return inflateView(context, rv, parent, 0, null);
+ private View apply(Context context, ViewGroup directParent, ViewGroup rootParent,
+ @Nullable SizeF size, ActionApplyParams params) {
+ RemoteViews rvToApply = getRemoteViewsToApply(context, size);
+ View result = inflateView(context, rvToApply, directParent,
+ params.applyThemeResId, params.colorResources);
+ rvToApply.performApply(result, rootParent, params);
+ return result;
}
private View inflateView(Context context, RemoteViews rv, @Nullable ViewGroup parent,
@@ -5704,7 +5659,6 @@
return applyAsync(context, parent, executor, listener, null /* handler */);
}
-
/** @hide */
public CancellationSignal applyAsync(Context context, ViewGroup parent,
Executor executor, OnViewAppliedListener listener, InteractionHandler handler) {
@@ -5723,16 +5677,19 @@
public CancellationSignal applyAsync(Context context, ViewGroup parent, Executor executor,
OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
ColorResources colorResources) {
+
+ ActionApplyParams params = new ActionApplyParams()
+ .withInteractionHandler(handler)
+ .withColorResources(colorResources)
+ .withExecutor(executor);
return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
- handler, colorResources, null /* result */,
- true /* topLevel */).startTaskOnExecutor(executor);
+ params, null /* result */, true /* topLevel */).startTaskOnExecutor(executor);
}
private AsyncApplyTask getInternalAsyncApplyTask(Context context, ViewGroup parent,
- OnViewAppliedListener listener, InteractionHandler handler, SizeF size,
- ColorResources colorResources, View result) {
+ OnViewAppliedListener listener, ActionApplyParams params, SizeF size, View result) {
return new AsyncApplyTask(getRemoteViewsToApply(context, size), parent, context, listener,
- handler, colorResources, result, false /* topLevel */);
+ params, result, false /* topLevel */);
}
private class AsyncApplyTask extends AsyncTask<Void, Void, ViewTree>
@@ -5742,8 +5699,8 @@
final ViewGroup mParent;
final Context mContext;
final OnViewAppliedListener mListener;
- final InteractionHandler mHandler;
- final ColorResources mColorResources;
+ final ActionApplyParams mApplyParams;
+
/**
* Whether the remote view is the top-level one (i.e. not within an action).
*
@@ -5758,16 +5715,13 @@
private AsyncApplyTask(
RemoteViews rv, ViewGroup parent, Context context, OnViewAppliedListener listener,
- InteractionHandler handler, ColorResources colorResources,
- View result, boolean topLevel) {
+ ActionApplyParams applyParams, View result, boolean topLevel) {
mRV = rv;
mParent = parent;
mContext = context;
mListener = listener;
- mColorResources = colorResources;
- mHandler = handler;
mTopLevel = topLevel;
-
+ mApplyParams = applyParams;
mResult = result;
}
@@ -5776,17 +5730,18 @@
protected ViewTree doInBackground(Void... params) {
try {
if (mResult == null) {
- mResult = inflateView(mContext, mRV, mParent, 0, mColorResources);
+ mResult = inflateView(mContext, mRV, mParent, 0, mApplyParams.colorResources);
}
mTree = new ViewTree(mResult);
+
if (mRV.mActions != null) {
int count = mRV.mActions.size();
mActions = new Action[count];
for (int i = 0; i < count && !isCancelled(); i++) {
// TODO: check if isCancelled in nested views.
- mActions[i] = mRV.mActions.get(i).initActionAsync(mTree, mParent, mHandler,
- mColorResources);
+ mActions[i] = mRV.mActions.get(i)
+ .initActionAsync(mTree, mParent, mApplyParams);
}
} else {
mActions = null;
@@ -5808,10 +5763,13 @@
try {
if (mActions != null) {
- InteractionHandler handler = mHandler == null
- ? DEFAULT_INTERACTION_HANDLER : mHandler;
+
+ ActionApplyParams applyParams = mApplyParams.clone();
+ if (applyParams.handler == null) {
+ applyParams.handler = DEFAULT_INTERACTION_HANDLER;
+ }
for (Action a : mActions) {
- a.apply(viewTree.mRoot, mParent, handler, mColorResources);
+ a.apply(viewTree.mRoot, mParent, applyParams);
}
}
// If the parent of the view is has is a root, resolve the recycling.
@@ -5859,18 +5817,43 @@
* the {@link #apply(Context,ViewGroup)} call.
*/
public void reapply(Context context, View v) {
- reapply(context, v, null /* handler */);
+ reapply(context, v, null /* size */, new ActionApplyParams());
}
/** @hide */
public void reapply(Context context, View v, InteractionHandler handler) {
- reapply(context, v, handler, null /* size */, null /* colorResources */);
+ reapply(context, v, null /* size */,
+ new ActionApplyParams().withInteractionHandler(handler));
}
/** @hide */
public void reapply(Context context, View v, InteractionHandler handler, SizeF size,
ColorResources colorResources) {
- reapply(context, v, handler, size, colorResources, true);
+ reapply(context, v, size, new ActionApplyParams()
+ .withInteractionHandler(handler).withColorResources(colorResources));
+ }
+
+ /** @hide */
+ public void reapply(Context context, View v, @Nullable SizeF size, ActionApplyParams params) {
+ reapply(context, v, (ViewGroup) v.getParent(), size, params, true);
+ }
+
+ private void reapplyNestedViews(Context context, View v, ViewGroup rootParent,
+ ActionApplyParams params) {
+ reapply(context, v, rootParent, null, params, false);
+ }
+
+ // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
+ // should set it to false.
+ private void reapply(Context context, View v, ViewGroup rootParent,
+ @Nullable SizeF size, ActionApplyParams params, boolean topLevel) {
+ RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
+ rvToApply.performApply(v, rootParent, params);
+
+ // If the parent of the view is has is a root, resolve the recycling.
+ if (topLevel && v instanceof ViewGroup) {
+ finalizeViewRecycling((ViewGroup) v);
+ }
}
/** @hide */
@@ -5922,27 +5905,6 @@
return rvToApply;
}
- // Note: topLevel should be true only for calls on the topLevel RemoteViews, internal calls
- // should set it to false.
- private void reapply(Context context, View v, InteractionHandler handler, SizeF size,
- ColorResources colorResources, boolean topLevel) {
-
- RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
-
- rvToApply.performApply(v, (ViewGroup) v.getParent(), handler, colorResources);
-
- // If the parent of the view is has is a root, resolve the recycling.
- if (topLevel && v instanceof ViewGroup) {
- finalizeViewRecycling((ViewGroup) v);
- }
- }
-
- private void reapplyNestedViews(Context context, View v, ViewGroup rootParent,
- InteractionHandler handler, SizeF size, ColorResources colorResources) {
- RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
- rvToApply.performApply(v, rootParent, handler, colorResources);
- }
-
/**
* Applies all the actions to the provided view, moving as much of the task on the background
* thread as possible.
@@ -5973,19 +5935,25 @@
ColorResources colorResources) {
RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size);
+ ActionApplyParams params = new ActionApplyParams()
+ .withColorResources(colorResources)
+ .withInteractionHandler(handler)
+ .withExecutor(executor);
+
return new AsyncApplyTask(rvToApply, (ViewGroup) v.getParent(),
- context, listener, handler, colorResources, v, true /* topLevel */)
+ context, listener, params, v, true /* topLevel */)
.startTaskOnExecutor(executor);
}
- private void performApply(View v, ViewGroup parent, InteractionHandler handler,
- ColorResources colorResources) {
+ private void performApply(View v, ViewGroup parent, ActionApplyParams params) {
+ params = params.clone();
+ if (params.handler == null) {
+ params.handler = DEFAULT_INTERACTION_HANDLER;
+ }
if (mActions != null) {
- handler = handler == null ? DEFAULT_INTERACTION_HANDLER : handler;
final int count = mActions.size();
for (int i = 0; i < count; i++) {
- Action a = mActions.get(i);
- a.apply(v, parent, handler, colorResources);
+ mActions.get(i).apply(v, parent, params);
}
}
}
@@ -6043,6 +6011,47 @@
}
/**
+ * Utility class to hold all the options when applying the remote views
+ * @hide
+ */
+ public class ActionApplyParams {
+
+ public InteractionHandler handler;
+ public ColorResources colorResources;
+ public Executor executor;
+ @StyleRes public int applyThemeResId;
+
+ @Override
+ public ActionApplyParams clone() {
+ return new ActionApplyParams()
+ .withInteractionHandler(handler)
+ .withColorResources(colorResources)
+ .withExecutor(executor)
+ .withThemeResId(applyThemeResId);
+ }
+
+ public ActionApplyParams withInteractionHandler(InteractionHandler handler) {
+ this.handler = handler;
+ return this;
+ }
+
+ public ActionApplyParams withColorResources(ColorResources colorResources) {
+ this.colorResources = colorResources;
+ return this;
+ }
+
+ public ActionApplyParams withThemeResId(@StyleRes int themeResId) {
+ this.applyThemeResId = themeResId;
+ return this;
+ }
+
+ public ActionApplyParams withExecutor(Executor executor) {
+ this.executor = executor;
+ return this;
+ }
+ }
+
+ /**
* Object allowing the modification of a context to overload the system's dynamic colors.
*
* Only colors from {@link android.R.color#system_accent1_0} to
@@ -6056,10 +6065,12 @@
// Size, in bytes, of an entry in the array of colors in an ARSC file.
private static final int ARSC_ENTRY_SIZE = 16;
- private ResourcesLoader mLoader;
+ private final ResourcesLoader mLoader;
+ private final SparseIntArray mColorMapping;
- private ColorResources(ResourcesLoader loader) {
+ private ColorResources(ResourcesLoader loader, SparseIntArray colorMapping) {
mLoader = loader;
+ mColorMapping = colorMapping;
}
/**
@@ -6071,6 +6082,10 @@
context.getResources().addLoaders(mLoader);
}
+ public SparseIntArray getColorMapping() {
+ return mColorMapping;
+ }
+
private static ByteArrayOutputStream readFileContent(InputStream input) throws IOException {
ByteArrayOutputStream content = new ByteArrayOutputStream(2048);
byte[] buffer = new byte[4096];
@@ -6145,7 +6160,7 @@
ResourcesLoader colorsLoader = new ResourcesLoader();
colorsLoader.addProvider(ResourcesProvider
.loadFromTable(pfd, null /* assetsProvider */));
- return new ColorResources(colorsLoader);
+ return new ColorResources(colorsLoader, colorMapping.clone());
}
}
} finally {
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 9b583be..00b0105 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -339,6 +339,8 @@
// A map from package name of vendor APEXes that can be updated to an installer package name
// allowed to install updates for it.
private final ArrayMap<String, String> mAllowedVendorApexes = new ArrayMap<>();
+ // A set of package names that are allowed to use <install-constraints> manifest tag.
+ private final Set<String> mInstallConstraintsAllowlist = new ArraySet<>();
private String mModulesInstallerPackageName;
@@ -535,6 +537,10 @@
return mAllowedVendorApexes;
}
+ public Set<String> getInstallConstraintsAllowlist() {
+ return mInstallConstraintsAllowlist;
+ }
+
public String getModulesInstallerPackageName() {
return mModulesInstallerPackageName;
}
@@ -1455,6 +1461,20 @@
}
XmlUtils.skipCurrentTag(parser);
} break;
+ case "install-constraints-allowed": {
+ if (allowAppConfigs) {
+ String packageName = parser.getAttributeValue(null, "package");
+ if (packageName == null) {
+ Slog.w(TAG, "<" + name + "> without package in " + permFile
+ + " at " + parser.getPositionDescription());
+ } else {
+ mInstallConstraintsAllowlist.add(packageName);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
default: {
Slog.w(TAG, "Tag " + name + " is unknown in "
+ permFile + " at " + parser.getPositionDescription());
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 7562b9a..a62f6ad 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -3602,4 +3602,19 @@
false, the application cannot be profiled at all. Defaults to true. -->
<attr name="enabled" format="boolean" />
</declare-styleable>
+
+ <!-- <code>install-constraints</code> tag rejects installs unless one the constraints defined by
+ its child elements is true.
+ It is possible to have multiple <code>install-constraints</code> tags in a single manifest,
+ where each tag is evaluated independently.
+ @hide -->
+ <declare-styleable name="AndroidManifestInstallConstraints" parent="AndroidManifest" />
+
+ <!-- A constraint for <code>install-constraints</code>. Checks that the device fingerprint
+ starts with the given prefix.
+ @hide -->
+ <declare-styleable name="AndroidManifestInstallConstraintsFingerprintPrefix"
+ parent="AndroidManifestInstallConstraints">
+ <attr name="value" />
+ </declare-styleable>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index 31fc6a5..2c02006 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -452,6 +452,7 @@
*/
void setEntry(@NonNull final BubbleEntry entry) {
Objects.requireNonNull(entry);
+ boolean showingDotPreviously = showDot();
mLastUpdated = entry.getStatusBarNotification().getPostTime();
mIsBubble = entry.getStatusBarNotification().getNotification().isBubbleNotification();
mPackageName = entry.getStatusBarNotification().getPackageName();
@@ -498,6 +499,10 @@
mShouldSuppressNotificationDot = entry.shouldSuppressNotificationDot();
mShouldSuppressNotificationList = entry.shouldSuppressNotificationList();
mShouldSuppressPeek = entry.shouldSuppressPeek();
+ if (showingDotPreviously != showDot()) {
+ // This will update the UI if needed
+ setShowDot(showDot());
+ }
}
@Nullable
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 3982616..8771ceb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1055,18 +1055,28 @@
public void updateBubble(BubbleEntry notif, boolean suppressFlyout, boolean showInShade) {
// If this is an interruptive notif, mark that it's interrupted
mSysuiProxy.setNotificationInterruption(notif.getKey());
- if (!notif.getRanking().isTextChanged()
+ boolean isNonInterruptiveNotExpanding = !notif.getRanking().isTextChanged()
&& (notif.getBubbleMetadata() != null
- && !notif.getBubbleMetadata().getAutoExpandBubble())
+ && !notif.getBubbleMetadata().getAutoExpandBubble());
+ if (isNonInterruptiveNotExpanding
&& mBubbleData.hasOverflowBubbleWithKey(notif.getKey())) {
// Update the bubble but don't promote it out of overflow
Bubble b = mBubbleData.getOverflowBubbleWithKey(notif.getKey());
- b.setEntry(notif);
+ if (notif.isBubble()) {
+ notif.setFlagBubble(false);
+ }
+ updateNotNotifyingEntry(b, notif, showInShade);
+ } else if (mBubbleData.hasAnyBubbleWithKey(notif.getKey())
+ && isNonInterruptiveNotExpanding) {
+ Bubble b = mBubbleData.getAnyBubbleWithkey(notif.getKey());
+ if (b != null) {
+ updateNotNotifyingEntry(b, notif, showInShade);
+ }
} else if (mBubbleData.isSuppressedWithLocusId(notif.getLocusId())) {
// Update the bubble but don't promote it out of overflow
Bubble b = mBubbleData.getSuppressedBubbleWithKey(notif.getKey());
if (b != null) {
- b.setEntry(notif);
+ updateNotNotifyingEntry(b, notif, showInShade);
}
} else {
Bubble bubble = mBubbleData.getOrCreateBubble(notif, null /* persistedBubble */);
@@ -1076,13 +1086,25 @@
if (bubble.shouldAutoExpand()) {
bubble.setShouldAutoExpand(false);
}
+ mImpl.mCachedState.updateBubbleSuppressedState(bubble);
} else {
inflateAndAdd(bubble, suppressFlyout, showInShade);
}
}
}
- void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
+ void updateNotNotifyingEntry(Bubble b, BubbleEntry entry, boolean showInShade) {
+ boolean isBubbleSelected = Objects.equals(b, mBubbleData.getSelectedBubble());
+ boolean isBubbleExpandedAndSelected = isStackExpanded() && isBubbleSelected;
+ b.setEntry(entry);
+ boolean suppress = isBubbleExpandedAndSelected || !showInShade || !b.showInShade();
+ b.setSuppressNotification(suppress);
+ b.setShowDot(!isBubbleExpandedAndSelected);
+ mImpl.mCachedState.updateBubbleSuppressedState(b);
+ }
+
+ @VisibleForTesting
+ public void inflateAndAdd(Bubble bubble, boolean suppressFlyout, boolean showInShade) {
// Lazy init stack view when a bubble is created
ensureStackViewCreated();
bubble.setInflateSynchronously(mInflateSynchronously);
@@ -1111,7 +1133,10 @@
}
@VisibleForTesting
- public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
+ public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem) {
+ if (!fromSystem) {
+ return;
+ }
// shouldBubbleUp checks canBubble & for bubble metadata
boolean shouldBubble = shouldBubbleUp && canLaunchInTaskView(mContext, entry);
if (!shouldBubble && mBubbleData.hasAnyBubbleWithKey(entry.getKey())) {
@@ -1172,9 +1197,9 @@
// notification, so that the bubble will be re-created if shouldBubbleUp returns
// true.
mBubbleData.dismissBubbleWithKey(key, DISMISS_NO_BUBBLE_UP);
- } else if (entry != null && mTmpRanking.isBubble() && !isActive) {
+ } else if (entry != null && mTmpRanking.isBubble() && !isActiveOrInOverflow) {
entry.setFlagBubble(true);
- onEntryUpdated(entry, shouldBubbleUp);
+ onEntryUpdated(entry, shouldBubbleUp, /* fromSystem= */ true);
}
}
}
@@ -1773,9 +1798,9 @@
}
@Override
- public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp) {
+ public void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem) {
mMainExecutor.execute(() -> {
- BubbleController.this.onEntryUpdated(entry, shouldBubbleUp);
+ BubbleController.this.onEntryUpdated(entry, shouldBubbleUp, fromSystem);
});
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
index cf792cd..37b96ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java
@@ -171,8 +171,9 @@
*
* @param entry the {@link BubbleEntry} by the notification.
* @param shouldBubbleUp {@code true} if this notification should bubble up.
+ * @param fromSystem {@code true} if this update is from NotificationManagerService.
*/
- void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp);
+ void onEntryUpdated(BubbleEntry entry, boolean shouldBubbleUp, boolean fromSystem);
/**
* Called when new notification entry removed.
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 de7e7bd..a372acb 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,6 +21,7 @@
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;
@@ -90,7 +91,6 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Executor;
@@ -337,17 +337,39 @@
}
public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
+ final int[] result = new int[1];
+ IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
+ @Override
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ final IRemoteAnimationFinishedCallback finishedCallback) {
+ try {
+ finishedCallback.onAnimationFinished();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to invoke onAnimationFinished", e);
+ }
+ if (result[0] == START_SUCCESS || result[0] == START_TASK_TO_FRONT) {
+ final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ mStageCoordinator.prepareEvictNonOpeningChildTasks(position, apps, evictWct);
+ mSyncQueue.queue(evictWct);
+ }
+ }
+ @Override
+ public void onAnimationCancelled(boolean isKeyguardOccluded) {
+ }
+ };
options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
null /* wct */);
+ RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(wrapper,
+ 0 /* duration */, 0 /* statusBarTransitionDelay */);
+ ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
+ activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
try {
- final WindowContainerTransaction evictWct = new WindowContainerTransaction();
- mStageCoordinator.prepareEvictChildTasks(position, evictWct);
- final int result =
- ActivityTaskManager.getService().startActivityFromRecents(taskId, options);
- if (result == START_SUCCESS || result == START_TASK_TO_FRONT) {
- mSyncQueue.queue(evictWct);
- }
+ result[0] = ActivityTaskManager.getService().startActivityFromRecents(taskId,
+ activityOptions.toBundle());
} catch (RemoteException e) {
Slog.e(TAG, "Failed to launch task", e);
}
@@ -403,7 +425,7 @@
// Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the
// split.
- if (isLaunchingAdjacently(intent.getIntent(), position)) {
+ if (shouldAddMultipleTaskFlag(intent.getIntent(), position)) {
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
}
@@ -418,8 +440,7 @@
/** Returns {@code true} if it's launching the same component on both sides of the split. */
@VisibleForTesting
- boolean isLaunchingAdjacently(@Nullable Intent startIntent,
- @SplitPosition int position) {
+ boolean shouldAddMultipleTaskFlag(@Nullable Intent startIntent, @SplitPosition int position) {
if (startIntent == null) {
return false;
}
@@ -430,6 +451,16 @@
}
if (isSplitScreenVisible()) {
+ // To prevent users from constantly dropping the same app to the same side resulting in
+ // a large number of instances in the background.
+ final ActivityManager.RunningTaskInfo targetTaskInfo = getTaskInfo(position);
+ final ComponentName targetActivity = targetTaskInfo != null
+ ? targetTaskInfo.baseIntent.getComponent() : null;
+ if (Objects.equals(launchingActivity, targetActivity)) {
+ return false;
+ }
+
+ // Allow users to start a new instance the same to adjacent side.
final ActivityManager.RunningTaskInfo pairedTaskInfo =
getTaskInfo(SplitLayout.reversePosition(position));
final ComponentName pairedActivity = pairedTaskInfo != null
@@ -453,12 +484,12 @@
mStageCoordinator.prepareEvictInvisibleChildTasks(wct);
mSyncQueue.queue(wct);
}
- return reparentSplitTasksForAnimation(apps, true /*splitExpectedToBeVisible*/);
+ return reparentSplitTasksForAnimation(apps, false /* enterSplitScreen */);
}
RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
try {
- return reparentSplitTasksForAnimation(apps, false /*splitExpectedToBeVisible*/);
+ return reparentSplitTasksForAnimation(apps, true /* enterSplitScreen */);
} finally {
for (RemoteAnimationTarget appTarget : apps) {
if (appTarget.leash != null) {
@@ -469,14 +500,23 @@
}
private RemoteAnimationTarget[] reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps,
- boolean splitExpectedToBeVisible) {
+ boolean enterSplitScreen) {
if (ENABLE_SHELL_TRANSITIONS) return null;
- // TODO(b/206487881): Integrate this with shell transition.
- if (splitExpectedToBeVisible && !isSplitScreenVisible()) return null;
- // Split not visible, but not enough apps to have split, also return null
- if (!splitExpectedToBeVisible && apps.length < 2) return null;
- SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+ if (enterSplitScreen) {
+ int openingApps = 0;
+ for (int i = 0; i < apps.length; ++i) {
+ if (apps[i].mode == MODE_OPENING) openingApps++;
+ }
+ if (openingApps < 2) {
+ // Not having enough apps to enter split screen
+ return null;
+ }
+ } else if (!isSplitScreenVisible()) {
+ return null;
+ }
+
+ final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
if (mSplitTasksContainerLayer != null) {
// Remove the previous layer before recreating
transaction.remove(mSplitTasksContainerLayer);
@@ -489,17 +529,14 @@
mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder);
mSplitTasksContainerLayer = builder.build();
- // Ensure that we order these in the parent in the right z-order as their previous order
- Arrays.sort(apps, (a1, a2) -> a1.prefixOrderIndex - a2.prefixOrderIndex);
- int layer = 1;
- for (RemoteAnimationTarget appTarget : apps) {
+ for (int i = 0; i < apps.length; ++i) {
+ final RemoteAnimationTarget appTarget = apps[i];
transaction.reparent(appTarget.leash, mSplitTasksContainerLayer);
transaction.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
appTarget.screenSpaceBounds.top);
- transaction.setLayer(appTarget.leash, layer++);
}
transaction.apply();
- transaction.close();
+ mTransactionPool.release(transaction);
return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
}
/**
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 80ef74e..8e1ae39 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
@@ -452,10 +452,16 @@
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);
+ if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
+ mMainExecutor.execute(() ->
+ exitSplitScreen(mMainStage.getChildCount() == 0
+ ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
+ } else {
+ // 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.
@@ -651,7 +657,7 @@
mShouldUpdateRecents = true;
// If any stage has no child after animation finished, it means that split will display
// nothing, such status will happen if task and intent is same app but not support
- // multi-instagce, we should exit split and expand that app as full screen.
+ // multi-instance, we should exit split and expand that app as full screen.
if (!cancel && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
mMainExecutor.execute(() ->
exitSplitScreen(mMainStage.getChildCount() == 0
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index f414d69..1af9415 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -33,6 +33,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
+import android.util.Slog;
import android.util.SparseArray;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -376,7 +377,13 @@
SurfaceControl leash, boolean firstAppeared) {
final Point taskPositionInParent = taskInfo.positionInParent;
mSyncQueue.runInSync(t -> {
- t.setWindowCrop(leash, null);
+ // The task surface might be released before running in the sync queue for the case like
+ // trampoline launch, so check if the surface is valid before processing it.
+ if (!leash.isValid()) {
+ Slog.w(TAG, "Skip updating invalid child task surface of task#" + taskInfo.taskId);
+ return;
+ }
+ t.setCrop(leash, null);
t.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y);
if (firstAppeared && !ENABLE_SHELL_TRANSITIONS) {
t.setAlpha(leash, 1f);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 087304b..506a4c0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -233,7 +233,9 @@
mTmpColor[2] = (float) Color.blue(backgroundColorInt) / 255.f;
startT.setWindowCrop(mTaskBackgroundSurface, taskBounds.width(), taskBounds.height())
.setShadowRadius(mTaskBackgroundSurface, shadowRadius)
- .setColor(mTaskBackgroundSurface, mTmpColor);
+ .setColor(mTaskBackgroundSurface, mTmpColor)
+ .setLayer(mTaskBackgroundSurface, -1)
+ .show(mTaskBackgroundSurface);
// Caption view
mCaptionWindowManager.setConfiguration(taskConfig);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 10788f9..c7c78d3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -20,6 +20,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static org.junit.Assert.assertFalse;
@@ -111,7 +112,7 @@
}
@Test
- public void testIsLaunchingAdjacently_notInSplitScreen() {
+ public void testShouldAddMultipleTaskFlag_notInSplitScreen() {
doReturn(false).when(mSplitScreenController).isSplitScreenVisible();
doReturn(true).when(mSplitScreenController).isValidToEnterSplitScreen(any());
@@ -120,7 +121,7 @@
ActivityManager.RunningTaskInfo focusTaskInfo =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
- assertTrue(mSplitScreenController.isLaunchingAdjacently(
+ assertTrue(mSplitScreenController.shouldAddMultipleTaskFlag(
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
// Verify launching different activity returns false.
@@ -128,28 +129,40 @@
focusTaskInfo =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, diffIntent);
doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
- assertFalse(mSplitScreenController.isLaunchingAdjacently(
+ assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
}
@Test
- public void testIsLaunchingAdjacently_inSplitScreen() {
+ public void testShouldAddMultipleTaskFlag_inSplitScreen() {
doReturn(true).when(mSplitScreenController).isSplitScreenVisible();
-
- // Verify launching the same activity returns true.
Intent startIntent = createStartIntent("startActivity");
- ActivityManager.RunningTaskInfo pairingTaskInfo =
+ ActivityManager.RunningTaskInfo sameTaskInfo =
createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, startIntent);
- doReturn(pairingTaskInfo).when(mSplitScreenController).getTaskInfo(anyInt());
- assertTrue(mSplitScreenController.isLaunchingAdjacently(
+ Intent diffIntent = createStartIntent("diffActivity");
+ ActivityManager.RunningTaskInfo differentTaskInfo =
+ createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, diffIntent);
+
+ // Verify launching the same activity return false.
+ doReturn(sameTaskInfo).when(mSplitScreenController)
+ .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+ assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
- // Verify launching different activity returns false.
- Intent diffIntent = createStartIntent("diffActivity");
- pairingTaskInfo =
- createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, diffIntent);
- doReturn(pairingTaskInfo).when(mSplitScreenController).getTaskInfo(anyInt());
- assertFalse(mSplitScreenController.isLaunchingAdjacently(
+ // Verify launching the same activity as adjacent returns true.
+ doReturn(differentTaskInfo).when(mSplitScreenController)
+ .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+ doReturn(sameTaskInfo).when(mSplitScreenController)
+ .getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ assertTrue(mSplitScreenController.shouldAddMultipleTaskFlag(
+ startIntent, SPLIT_POSITION_TOP_OR_LEFT));
+
+ // Verify launching different activity from adjacent returns false.
+ doReturn(differentTaskInfo).when(mSplitScreenController)
+ .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
+ doReturn(differentTaskInfo).when(mSplitScreenController)
+ .getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
+ assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 1e7d5fe..226843e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -204,6 +204,8 @@
verify(mMockSurfaceControlStartT)
.setColor(taskBackgroundSurface, new float[] {1.f, 1.f, 0.f});
verify(mMockSurfaceControlStartT).setShadowRadius(taskBackgroundSurface, 10);
+ verify(mMockSurfaceControlStartT).setLayer(taskBackgroundSurface, -1);
+ verify(mMockSurfaceControlStartT).show(taskBackgroundSurface);
verify(mMockSurfaceControlViewHostFactory)
.create(any(), eq(defaultDisplay), any(), anyBoolean());
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 322e1be..5795fd4 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1610,6 +1610,9 @@
<string name="dream_complication_title_cast_info">Cast Info</string>
<!-- Screensaver overlay which displays home controls. [CHAR LIMIT=20] -->
<string name="dream_complication_title_home_controls">Home Controls</string>
+ <!-- Screensaver overlay which displays smartspace. [CHAR LIMIT=20] -->
+ <string name="dream_complication_title_smartspace">Smartspace</string>
+
<!-- Title for a screen allowing the user to choose a profile picture. [CHAR LIMIT=NONE] -->
<string name="avatar_picker_title">Choose a profile picture</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index a46e232..2258617 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -92,7 +92,8 @@
COMPLICATION_TYPE_WEATHER,
COMPLICATION_TYPE_AIR_QUALITY,
COMPLICATION_TYPE_CAST_INFO,
- COMPLICATION_TYPE_HOME_CONTROLS
+ COMPLICATION_TYPE_HOME_CONTROLS,
+ COMPLICATION_TYPE_SMARTSPACE
})
@Retention(RetentionPolicy.SOURCE)
public @interface ComplicationType {}
@@ -103,6 +104,7 @@
public static final int COMPLICATION_TYPE_AIR_QUALITY = 4;
public static final int COMPLICATION_TYPE_CAST_INFO = 5;
public static final int COMPLICATION_TYPE_HOME_CONTROLS = 6;
+ public static final int COMPLICATION_TYPE_SMARTSPACE = 7;
private final Context mContext;
private final IDreamManager mDreamManager;
@@ -351,6 +353,9 @@
case COMPLICATION_TYPE_HOME_CONTROLS:
res = R.string.dream_complication_title_home_controls;
break;
+ case COMPLICATION_TYPE_SMARTSPACE:
+ res = R.string.dream_complication_title_smartspace;
+ break;
default:
return null;
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index 2f36ab9..8f9ced6 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -23,7 +23,6 @@
import android.graphics.Color
import android.graphics.Rect
import android.os.Looper
-import android.service.dreams.IDreamManager
import android.util.Log
import android.util.MathUtils
import android.view.GhostView
@@ -54,7 +53,7 @@
class DialogLaunchAnimator
@JvmOverloads
constructor(
- private val dreamManager: IDreamManager,
+ private val callback: Callback,
private val interactionJankMonitor: InteractionJankMonitor,
private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS),
private val isForTesting: Boolean = false
@@ -126,7 +125,7 @@
val animatedDialog =
AnimatedDialog(
launchAnimator,
- dreamManager,
+ callback,
interactionJankMonitor,
animateFrom,
onDialogDismissed = { openedDialogs.remove(it) },
@@ -194,8 +193,12 @@
val dialog = animatedDialog.dialog
- // Don't animate if the dialog is not showing.
- if (!dialog.isShowing) {
+ // Don't animate if the dialog is not showing or if we are locked and going to show the
+ // bouncer.
+ if (
+ !dialog.isShowing ||
+ (!callback.isUnlocked() && !callback.isShowingAlternateAuthOnUnlock())
+ ) {
return null
}
@@ -285,6 +288,23 @@
?.let { it.touchSurface = it.prepareForStackDismiss() }
dialog.dismiss()
}
+
+ interface Callback {
+ /** Whether the device is currently in dreaming (screensaver) mode. */
+ fun isDreaming(): Boolean
+
+ /**
+ * Whether the device is currently unlocked, i.e. if it is *not* on the keyguard or if the
+ * keyguard can be dismissed.
+ */
+ fun isUnlocked(): Boolean
+
+ /**
+ * Whether we are going to show alternate authentication (like UDFPS) instead of the
+ * traditional bouncer when unlocking the device.
+ */
+ fun isShowingAlternateAuthOnUnlock(): Boolean
+ }
}
/**
@@ -296,7 +316,7 @@
private class AnimatedDialog(
private val launchAnimator: LaunchAnimator,
- private val dreamManager: IDreamManager,
+ private val callback: DialogLaunchAnimator.Callback,
private val interactionJankMonitor: InteractionJankMonitor,
/** The view that triggered the dialog after being tapped. */
@@ -850,7 +870,7 @@
// If we are dreaming, the dialog was probably closed because of that so we don't animate
// into the touchSurface.
- if (dreamManager.isDreaming) {
+ if (callback.isDreaming()) {
return false
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f07d9ce..98946ac 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -214,7 +214,8 @@
* If no cancel signal has been received after this amount of time, set the biometric running
* state to stopped to allow Keyguard to retry authentication.
*/
- private static final int DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000;
+ @VisibleForTesting
+ protected static final int DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000;
private static final ComponentName FALLBACK_HOME_COMPONENT = new ComponentName(
"com.android.settings", "com.android.settings.FallbackHome");
@@ -332,10 +333,15 @@
private static final int HAL_ERROR_RETRY_TIMEOUT = 500; // ms
private static final int HAL_ERROR_RETRY_MAX = 20;
- private final Runnable mFpCancelNotReceived = this::onFingerprintCancelNotReceived;
+ @VisibleForTesting
+ protected final Runnable mFpCancelNotReceived = this::onFingerprintCancelNotReceived;
private final Runnable mFaceCancelNotReceived = this::onFaceCancelNotReceived;
+ @VisibleForTesting
+ protected Handler getHandler() {
+ return mHandler;
+ }
private final Handler mHandler;
private SparseBooleanArray mBiometricEnabledForUser = new SparseBooleanArray();
@@ -723,6 +729,11 @@
private void handleFingerprintAuthFailed() {
Assert.isMainThread();
+ if (mHandler.hasCallbacks(mFpCancelNotReceived)) {
+ Log.d(TAG, "handleFingerprintAuthFailed()"
+ + " triggered while waiting for cancellation, removing watchdog");
+ mHandler.removeCallbacks(mFpCancelNotReceived);
+ }
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -753,6 +764,11 @@
private void handleFingerprintAuthenticated(int authUserId, boolean isStrongBiometric) {
Trace.beginSection("KeyGuardUpdateMonitor#handlerFingerPrintAuthenticated");
+ if (mHandler.hasCallbacks(mFpCancelNotReceived)) {
+ Log.d(TAG, "handleFingerprintAuthenticated()"
+ + " triggered while waiting for cancellation, removing watchdog");
+ mHandler.removeCallbacks(mFpCancelNotReceived);
+ }
try {
final int userId;
try {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index e16ac08..c21e36a 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -97,6 +97,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.NonNull;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
@@ -132,7 +133,7 @@
private static final int FONT_SEARCH_STEP_PX = 4;
private final Context mContext;
- private final UiEventLogger mUiEventLogger;
+ private final ClipboardLogger mClipboardLogger;
private final BroadcastDispatcher mBroadcastDispatcher;
private final DisplayManager mDisplayManager;
private final DisplayMetrics mDisplayMetrics;
@@ -181,7 +182,7 @@
final Context displayContext = context.createDisplayContext(getDefaultDisplay());
mContext = displayContext.createWindowContext(TYPE_SCREENSHOT, null);
- mUiEventLogger = uiEventLogger;
+ mClipboardLogger = new ClipboardLogger(uiEventLogger);
mAccessibilityManager = AccessibilityManager.getInstance(mContext);
mTextClassifier = requireNonNull(context.getSystemService(TextClassificationManager.class))
@@ -231,7 +232,7 @@
@Override
public void onSwipeDismissInitiated(Animator animator) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SWIPE_DISMISSED);
mExitAnimator = animator;
}
@@ -249,7 +250,7 @@
});
mDismissButton.setOnClickListener(view -> {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISS_TAPPED);
animateOut();
});
@@ -285,7 +286,8 @@
int newDisplayId) {
if (mContext.getResources().getConfiguration().orientation
!= mOrientation) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
+ mClipboardLogger.logSessionComplete(
+ CLIPBOARD_OVERLAY_DISMISSED_OTHER);
hideImmediate();
}
}
@@ -300,7 +302,7 @@
});
mTimeoutHandler.setOnTimeoutRunnable(() -> {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_TIMED_OUT);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TIMED_OUT);
animateOut();
});
@@ -308,7 +310,7 @@
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
animateOut();
}
}
@@ -320,7 +322,7 @@
@Override
public void onReceive(Context context, Intent intent) {
if (SCREENSHOT_ACTION.equals(intent.getAction())) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_DISMISSED_OTHER);
animateOut();
}
}
@@ -390,7 +392,7 @@
mContext.getString(R.string.clipboard_send_nearby_description));
mRemoteCopyChip.setVisibility(View.VISIBLE);
mRemoteCopyChip.setOnClickListener((v) -> {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_REMOTE_COPY_TAPPED);
mContext.startActivity(remoteCopyIntent);
animateOut();
});
@@ -450,7 +452,7 @@
chip.setContentDescription(action.getTitle());
chip.setIcon(action.getIcon(), false);
chip.setPendingIntent(action.getActionIntent(), () -> {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_ACTION_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_ACTION_TAPPED);
animateOut();
});
chip.setAlpha(1);
@@ -486,7 +488,7 @@
touchRegion.op(tmpRect, Region.Op.UNION);
if (!touchRegion.contains(
(int) motionEvent.getRawX(), (int) motionEvent.getRawY())) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_TAP_OUTSIDE);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_TAP_OUTSIDE);
animateOut();
}
}
@@ -497,7 +499,7 @@
}
private void editImage(Uri uri) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_EDIT_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
String editorPackage = mContext.getString(R.string.config_screenshotEditor);
Intent editIntent = new Intent(Intent.ACTION_EDIT);
if (!TextUtils.isEmpty(editorPackage)) {
@@ -512,7 +514,7 @@
}
private void editText() {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_EDIT_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
Intent editIntent = new Intent(mContext, EditTextActivity.class);
editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
mContext.startActivity(editIntent);
@@ -520,13 +522,15 @@
}
private void shareContent(ClipData clip) {
- mUiEventLogger.log(CLIPBOARD_OVERLAY_SHARE_TAPPED);
+ mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SHARE_TAPPED);
Intent shareIntent = new Intent(Intent.ACTION_SEND);
- shareIntent.putExtra(Intent.EXTRA_TEXT, clip.getItemAt(0).getText().toString());
shareIntent.setDataAndType(
clip.getItemAt(0).getUri(), clip.getDescription().getMimeType(0));
- shareIntent.putExtra(Intent.EXTRA_STREAM, clip.getItemAt(0).getUri());
- shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ shareIntent.putExtra(Intent.EXTRA_TEXT, clip.getItemAt(0).getText().toString());
+ if (clip.getItemAt(0).getUri() != null) {
+ shareIntent.putExtra(Intent.EXTRA_STREAM, clip.getItemAt(0).getUri());
+ shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
Intent chooserIntent = Intent.createChooser(shareIntent, null)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
@@ -864,6 +868,7 @@
mRemoteCopyChip.setVisibility(View.GONE);
resetActionChips();
mTimeoutHandler.cancelTimeout();
+ mClipboardLogger.reset();
}
@MainThread
@@ -969,4 +974,24 @@
mWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
}
}
+
+ static class ClipboardLogger {
+ private final UiEventLogger mUiEventLogger;
+ private boolean mGuarded = false;
+
+ ClipboardLogger(UiEventLogger uiEventLogger) {
+ mUiEventLogger = uiEventLogger;
+ }
+
+ void logSessionComplete(@NonNull UiEventLogger.UiEventEnum event) {
+ if (!mGuarded) {
+ mGuarded = true;
+ mUiEventLogger.log(event);
+ }
+ }
+
+ void reset() {
+ mGuarded = false;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index de7bf28..55c1806 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -215,16 +215,15 @@
final boolean cameraBlocked = mSensorPrivacyController
.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA);
@DreamOverlayStatusBarView.StatusIconType int iconType = Resources.ID_NULL;
- if (micBlocked && cameraBlocked) {
- iconType = DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED;
- } else if (!micBlocked && cameraBlocked) {
- iconType = DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED;
- } else if (micBlocked && !cameraBlocked) {
- iconType = DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED;
- }
- if (iconType != Resources.ID_NULL) {
- showIcon(iconType, true);
- }
+ showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED,
+ !micBlocked && cameraBlocked);
+ showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED,
+ micBlocked && !cameraBlocked);
+ showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
+ micBlocked && cameraBlocked);
}
private String buildNotificationsContentDescription(int notificationCount) {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
index 5457144..29bb2f4 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java
@@ -163,7 +163,8 @@
COMPLICATION_TYPE_WEATHER,
COMPLICATION_TYPE_AIR_QUALITY,
COMPLICATION_TYPE_CAST_INFO,
- COMPLICATION_TYPE_HOME_CONTROLS
+ COMPLICATION_TYPE_HOME_CONTROLS,
+ COMPLICATION_TYPE_SMARTSPACE
})
@Retention(RetentionPolicy.SOURCE)
@interface ComplicationType {}
@@ -175,6 +176,7 @@
int COMPLICATION_TYPE_AIR_QUALITY = 1 << 3;
int COMPLICATION_TYPE_CAST_INFO = 1 << 4;
int COMPLICATION_TYPE_HOME_CONTROLS = 1 << 5;
+ int COMPLICATION_TYPE_SMARTSPACE = 1 << 6;
/**
* The {@link Host} interface specifies a way a {@link Complication} to communicate with its
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
index dcab90f..d5db63d 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
@@ -21,6 +21,7 @@
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_DATE;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_HOME_CONTROLS;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_NONE;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_SMARTSPACE;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER;
@@ -51,6 +52,8 @@
return COMPLICATION_TYPE_CAST_INFO;
case DreamBackend.COMPLICATION_TYPE_HOME_CONTROLS:
return COMPLICATION_TYPE_HOME_CONTROLS;
+ case DreamBackend.COMPLICATION_TYPE_SMARTSPACE:
+ return COMPLICATION_TYPE_SMARTSPACE;
default:
return COMPLICATION_TYPE_NONE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
index ac6edba..567bdbc 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java
@@ -52,6 +52,11 @@
return mViewHolderProvider.get();
}
+ @Override
+ public int getRequiredTypeAvailability() {
+ return COMPLICATION_TYPE_SMARTSPACE;
+ }
+
/**
* {@link CoreStartable} responsbile for registering {@link SmartSpaceComplication} with
* SystemUI.
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 0dc07ac..d9e75c4 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -207,6 +207,14 @@
public static final SysPropBooleanFlag HIDE_NAVBAR_WINDOW =
new SysPropBooleanFlag(1103, "persist.wm.debug.hide_navbar_window", false);
+ @Keep
+ public static final SysPropBooleanFlag WM_DESKTOP_WINDOWING =
+ new SysPropBooleanFlag(1104, "persist.wm.debug.desktop_mode", false);
+
+ @Keep
+ public static final SysPropBooleanFlag WM_CAPTION_ON_SHELL =
+ new SysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", false);
+
// 1200 - predictive back
@Keep
public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag(
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index dc23684d..6124e10 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -22,17 +22,11 @@
import com.android.systemui.util.collection.RingBuffer
import com.google.errorprone.annotations.CompileTimeConstant
import java.io.PrintWriter
-import java.text.SimpleDateFormat
-import java.util.Arrays.stream
-import java.util.Locale
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.BlockingQueue
import kotlin.concurrent.thread
import kotlin.math.max
-const val UNBOUNDED_STACK_TRACE = -1
-const val NESTED_TRACE_DEPTH = 10
-
/**
* A simple ring buffer of recyclable log messages
*
@@ -74,18 +68,12 @@
* @param maxSize The maximum number of messages to keep in memory at any one time. Buffers start
* out empty and grow up to [maxSize] as new messages are logged. Once the buffer's size reaches
* the maximum, it behaves like a ring buffer.
- * @param rootStackTraceDepth The number of stack trace elements to be logged for an exception when
- * the logBuffer is dumped. Defaulted to -1 [UNBOUNDED_STACK_TRACE] to print the entire stack trace.
- * @param nestedStackTraceDepth The number of stack trace elements to be logged for any nested
- * exceptions present in [Throwable.cause] or [Throwable.suppressedExceptions].
*/
class LogBuffer @JvmOverloads constructor(
private val name: String,
private val maxSize: Int,
private val logcatEchoTracker: LogcatEchoTracker,
private val systrace: Boolean = true,
- private val rootStackTraceDepth: Int = UNBOUNDED_STACK_TRACE,
- private val nestedStackTraceDepth: Int = NESTED_TRACE_DEPTH,
) {
private val buffer = RingBuffer(maxSize) { LogMessageImpl.create() }
@@ -236,7 +224,7 @@
val iterationStart = if (tailLength <= 0) { 0 } else { max(0, buffer.size - tailLength) }
for (i in iterationStart until buffer.size) {
- dumpMessage(buffer[i], pw)
+ buffer[i].dump(pw)
}
}
@@ -264,76 +252,6 @@
}
}
- private fun dumpMessage(
- message: LogMessage,
- pw: PrintWriter
- ) {
- val formattedTimestamp = DATE_FORMAT.format(message.timestamp)
- val shortLevel = message.level.shortString
- val messageToPrint = message.messagePrinter(message)
- val tag = message.tag
- printLikeLogcat(pw, formattedTimestamp, shortLevel, tag, messageToPrint)
- message.exception?.let { ex ->
- printException(
- pw,
- formattedTimestamp,
- shortLevel,
- ex,
- tag,
- stackTraceDepth = rootStackTraceDepth)
- }
- }
-
- private fun printException(
- pw: PrintWriter,
- timestamp: String,
- level: String,
- exception: Throwable,
- tag: String,
- exceptionMessagePrefix: String = "",
- stackTraceDepth: Int = UNBOUNDED_STACK_TRACE
- ) {
- val message = "$exceptionMessagePrefix$exception"
- printLikeLogcat(pw, timestamp, level, tag, message)
- var stacktraceStream = stream(exception.stackTrace)
- if (stackTraceDepth != UNBOUNDED_STACK_TRACE) {
- stacktraceStream = stacktraceStream.limit(stackTraceDepth.toLong())
- }
- stacktraceStream.forEach { line ->
- printLikeLogcat(pw, timestamp, level, tag, "\tat $line")
- }
- exception.cause?.let { cause ->
- printException(pw, timestamp, level, cause, tag, "Caused by: ", nestedStackTraceDepth)
- }
- exception.suppressedExceptions.forEach { suppressed ->
- printException(
- pw,
- timestamp,
- level,
- suppressed,
- tag,
- "Suppressed: ",
- nestedStackTraceDepth
- )
- }
- }
-
- private fun printLikeLogcat(
- pw: PrintWriter,
- formattedTimestamp: String,
- shortLogLevel: String,
- tag: String,
- message: String
- ) {
- pw.print(formattedTimestamp)
- pw.print(" ")
- pw.print(shortLogLevel)
- pw.print(" ")
- pw.print(tag)
- pw.print(": ")
- pw.println(message)
- }
-
private fun echo(message: LogMessage, toLogcat: Boolean, toSystrace: Boolean) {
if (toLogcat || toSystrace) {
val strMessage = message.messagePrinter(message)
@@ -370,5 +288,4 @@
typealias MessageInitializer = LogMessage.() -> Unit
private const val TAG = "LogBuffer"
-private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
private val FROZEN_MESSAGE = LogMessageImpl.create()
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt b/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
index 987aea8..dae2592 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogMessage.kt
@@ -16,6 +16,10 @@
package com.android.systemui.log
+import java.io.PrintWriter
+import java.text.SimpleDateFormat
+import java.util.Locale
+
/**
* Generic data class for storing messages logged to a [LogBuffer]
*
@@ -50,6 +54,17 @@
var bool2: Boolean
var bool3: Boolean
var bool4: Boolean
+
+ /**
+ * Function that dumps the [LogMessage] to the provided [writer].
+ */
+ fun dump(writer: PrintWriter) {
+ val formattedTimestamp = DATE_FORMAT.format(timestamp)
+ val shortLevel = level.shortString
+ val messageToPrint = messagePrinter(this)
+ printLikeLogcat(writer, formattedTimestamp, shortLevel, tag, messageToPrint)
+ exception?.printStackTrace(writer)
+ }
}
/**
@@ -61,3 +76,21 @@
* of the printer for each call, thwarting our attempts at avoiding any sort of allocation.
*/
typealias MessagePrinter = LogMessage.() -> String
+
+private fun printLikeLogcat(
+ pw: PrintWriter,
+ formattedTimestamp: String,
+ shortLogLevel: String,
+ tag: String,
+ message: String
+) {
+ pw.print(formattedTimestamp)
+ pw.print(" ")
+ pw.print(shortLogLevel)
+ pw.print(" ")
+ pw.print(tag)
+ pw.print(": ")
+ pw.println(message)
+}
+
+private val DATE_FORMAT = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index e9b6af4..e360d10 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -263,6 +263,7 @@
}
private void onGroupActionTriggered(boolean isChecked, MediaDevice device) {
+ disableSeekBar();
if (isChecked && isDeviceIncluded(mController.getSelectableMediaDevice(), device)) {
mController.addDeviceToPlayMedia(device);
} else if (!isChecked && isDeviceIncluded(mController.getDeselectableMediaDevice(),
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index bec6739..3b4ca48 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -273,6 +273,8 @@
void initSeekbar(MediaDevice device, boolean isCurrentSeekbarInvisible) {
if (!mController.isVolumeControlEnabled(device)) {
disableSeekBar();
+ } else {
+ enableSeekBar();
}
mSeekBar.setMaxVolume(device.getMaxVolume());
final int currentVolume = device.getCurrentVolume();
@@ -417,11 +419,16 @@
return drawable;
}
- private void disableSeekBar() {
+ protected void disableSeekBar() {
mSeekBar.setEnabled(false);
mSeekBar.setOnTouchListener((v, event) -> true);
}
+ private void enableSeekBar() {
+ mSeekBar.setEnabled(true);
+ mSeekBar.setOnTouchListener((v, event) -> false);
+ }
+
protected void setUpDeviceIcon(MediaDevice device) {
ThreadUtils.postOnBackgroundThread(() -> {
Icon icon = mController.getDeviceIconCompat(device).toIcon(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
index 5d7af52..6fe06e0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
@@ -194,6 +194,11 @@
}
private int getLoggingDeviceType(MediaDevice device, boolean isSourceDevice) {
+ if (device == null) {
+ return isSourceDevice
+ ? SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__SOURCE__UNKNOWN_TYPE
+ : SysUiStatsLog.MEDIA_OUTPUT_OP_SWITCH_REPORTED__TARGET__UNKNOWN_TYPE;
+ }
switch (device.getDeviceType()) {
case MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE:
return isSourceDevice
@@ -229,6 +234,9 @@
}
private int getInteractionDeviceType(MediaDevice device) {
+ if (device == null) {
+ return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE;
+ }
switch (device.getDeviceType()) {
case MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE:
return SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__BUILTIN_SPEAKER;
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 5f478ce..9ab83b8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -56,7 +56,7 @@
internal val logger: MediaTttLogger,
internal val windowManager: WindowManager,
private val viewUtil: ViewUtil,
- @Main private val mainExecutor: DelayableExecutor,
+ @Main internal val mainExecutor: DelayableExecutor,
private val accessibilityManager: AccessibilityManager,
private val configurationController: ConfigurationController,
private val powerManager: PowerManager,
@@ -205,13 +205,15 @@
*
* @param appPackageName the package name of the app playing the media. Will be used to fetch
* the app icon and app name if overrides aren't provided.
+ *
+ * @return the content description of the icon.
*/
internal fun setIcon(
currentChipView: ViewGroup,
appPackageName: String?,
appIconDrawableOverride: Drawable? = null,
appNameOverride: CharSequence? = null,
- ) {
+ ): CharSequence {
val appIconView = currentChipView.requireViewById<CachingIconView>(R.id.app_icon)
val iconInfo = getIconInfo(appPackageName)
@@ -224,6 +226,7 @@
appIconView.contentDescription = appNameOverride ?: iconInfo.iconName
appIconView.setImageDrawable(appIconDrawableOverride ?: iconInfo.icon)
+ return appIconView.contentDescription.toString()
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index 3ea11b8..b94b8bf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -122,13 +122,12 @@
val chipState = newChipInfo.state
// App icon
- setIcon(currentChipView, newChipInfo.routeInfo.packageName)
+ val iconName = setIcon(currentChipView, newChipInfo.routeInfo.packageName)
// Text
val otherDeviceName = newChipInfo.routeInfo.name.toString()
- currentChipView.requireViewById<TextView>(R.id.text).apply {
- text = chipState.getChipTextString(context, otherDeviceName)
- }
+ val chipText = chipState.getChipTextString(context, otherDeviceName)
+ currentChipView.requireViewById<TextView>(R.id.text).text = chipText
// Loading
currentChipView.requireViewById<View>(R.id.loading).visibility =
@@ -145,17 +144,29 @@
// Failure
currentChipView.requireViewById<View>(R.id.failure_icon).visibility =
chipState.isTransferFailure.visibleIfTrue()
+
+ // For accessibility
+ currentChipView.requireViewById<ViewGroup>(
+ R.id.media_ttt_sender_chip_inner
+ ).contentDescription = "$iconName $chipText"
}
override fun animateChipIn(chipView: ViewGroup) {
+ val chipInnerView = chipView.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner)
ViewHierarchyAnimator.animateAddition(
- chipView.requireViewById<ViewGroup>(R.id.media_ttt_sender_chip_inner),
+ chipInnerView,
ViewHierarchyAnimator.Hotspot.TOP,
Interpolators.EMPHASIZED_DECELERATE,
- duration = 500L,
+ duration = ANIMATION_DURATION,
includeMargins = true,
includeFadeIn = true,
)
+
+ // We can only request focus once the animation finishes.
+ mainExecutor.executeDelayed(
+ { chipInnerView.requestAccessibilityFocus() },
+ ANIMATION_DURATION
+ )
}
override fun removeChip(removalReason: String) {
@@ -186,3 +197,4 @@
}
const val SENDER_TAG = "MediaTapToTransferSender"
+private const val ANIMATION_DURATION = 500L
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index 2d7a809..3789cbb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -302,10 +302,6 @@
*/
@VisibleForTesting
void createNavigationBar(Display display, Bundle savedState, RegisterStatusBarResult result) {
- if (initializeTaskbarIfNecessary()) {
- return;
- }
-
if (display == null) {
return;
}
@@ -315,7 +311,7 @@
// We may show TaskBar on the default display for large screen device. Don't need to create
// navigation bar for this case.
- if (mIsTablet && isOnDefaultDisplay) {
+ if (isOnDefaultDisplay && initializeTaskbarIfNecessary()) {
return;
}
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 b44b4de..6424256 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -42,7 +42,6 @@
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.ViewController
-import com.android.wm.shell.back.BackAnimation
import java.io.PrintWriter
import javax.inject.Inject
import kotlin.math.abs
@@ -119,7 +118,7 @@
private val latencyTracker: LatencyTracker
) {
/** Construct a [BackPanelController]. */
- fun create(context: Context, backAnimation: BackAnimation?): BackPanelController {
+ fun create(context: Context): BackPanelController {
val backPanelController = BackPanelController(
context,
windowManager,
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 fc6dcd3..6ac3ead 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -574,10 +574,10 @@
private void resetEdgeBackPlugin() {
if (mIsNewBackAffordanceEnabled) {
setEdgeBackPlugin(
- mBackPanelControllerFactory.create(mContext, mBackAnimation));
+ mBackPanelControllerFactory.create(mContext));
} else {
setEdgeBackPlugin(
- new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker));
+ new NavigationBarEdgePanel(mContext, mLatencyTracker));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
index eba9d3f..122852f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationBarEdgePanel.java
@@ -43,7 +43,6 @@
import android.view.WindowManager;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;
-import android.window.BackEvent;
import androidx.core.graphics.ColorUtils;
import androidx.dynamicanimation.animation.DynamicAnimation;
@@ -59,7 +58,6 @@
import com.android.systemui.plugins.NavigationEdgeBackPlugin;
import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.wm.shell.back.BackAnimation;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -283,14 +281,11 @@
}
};
private BackCallback mBackCallback;
- private BackAnimation mBackAnimation;
- public NavigationBarEdgePanel(Context context,
- BackAnimation backAnimation, LatencyTracker latencyTracker) {
+ public NavigationBarEdgePanel(Context context, LatencyTracker latencyTracker) {
super(context);
mWindowManager = context.getSystemService(WindowManager.class);
- mBackAnimation = backAnimation;
mVibratorHelper = Dependency.get(VibratorHelper.class);
mDensity = context.getResources().getDisplayMetrics().density;
@@ -360,7 +355,6 @@
.getDimension(R.dimen.navigation_edge_action_drag_threshold);
mSwipeProgressThreshold = context.getResources()
.getDimension(R.dimen.navigation_edge_action_progress_threshold);
- initializeBackAnimation();
setVisibility(GONE);
@@ -388,17 +382,6 @@
mLatencyTracker = latencyTracker;
}
- public void setBackAnimation(BackAnimation backAnimation) {
- mBackAnimation = backAnimation;
- initializeBackAnimation();
- }
-
- private void initializeBackAnimation() {
- if (mBackAnimation != null) {
- mBackAnimation.setSwipeThresholds(mSwipeTriggerThreshold, mSwipeProgressThreshold);
- }
- }
-
@Override
public void onDestroy() {
cancelFailsafe();
@@ -484,12 +467,6 @@
@Override
public void onMotionEvent(MotionEvent event) {
- if (mBackAnimation != null) {
- mBackAnimation.onBackMotion(
- event.getX(), event.getY(),
- event.getActionMasked(),
- mIsLeftPanel ? BackEvent.EDGE_LEFT : BackEvent.EDGE_RIGHT);
- }
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
@@ -903,9 +880,6 @@
// Whenever the trigger back state changes the existing translation animation should be
// cancelled
mTranslationAnimation.cancel();
- if (mBackAnimation != null) {
- mBackAnimation.setTriggerBack(triggerBack);
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 68d35f9..824d3a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -34,6 +34,8 @@
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
+import com.android.systemui.statusbar.notification.collection.PipelineDumper;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
import com.android.systemui.util.time.SystemClock;
@@ -52,7 +54,8 @@
*/
@SysUISingleton
@SuppressLint("OverrideAbstract")
-public class NotificationListener extends NotificationListenerWithPlugins {
+public class NotificationListener extends NotificationListenerWithPlugins implements
+ PipelineDumpable {
private static final String TAG = "NotificationListener";
private static final boolean DEBUG = CentralSurfaces.DEBUG;
private static final long MAX_RANKING_DELAY_MILLIS = 500L;
@@ -255,6 +258,11 @@
}
}
+ @Override
+ public void dumpPipeline(@NonNull PipelineDumper d) {
+ d.dump("notificationHandlers", mNotificationHandlers);
+ }
+
private static Ranking getRankingOrTemporaryStandIn(RankingMap rankingMap, String key) {
Ranking ranking = new Ranking();
if (!rankingMap.getRanking(key, ranking)) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 48e3450..0951e82 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -19,7 +19,9 @@
import android.app.IActivityManager;
import android.content.Context;
import android.os.Handler;
+import android.os.RemoteException;
import android.service.dreams.IDreamManager;
+import android.util.Log;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.statusbar.IStatusBarService;
@@ -60,10 +62,12 @@
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarIconList;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallFlags;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.systemui.tracing.ProtoTracer;
@@ -274,7 +278,30 @@
@Provides
@SysUISingleton
static DialogLaunchAnimator provideDialogLaunchAnimator(IDreamManager dreamManager,
+ KeyguardStateController keyguardStateController,
+ Lazy<StatusBarKeyguardViewManager> statusBarKeyguardViewManager,
InteractionJankMonitor interactionJankMonitor) {
- return new DialogLaunchAnimator(dreamManager, interactionJankMonitor);
+ DialogLaunchAnimator.Callback callback = new DialogLaunchAnimator.Callback() {
+ @Override
+ public boolean isDreaming() {
+ try {
+ return dreamManager.isDreaming();
+ } catch (RemoteException e) {
+ Log.e("DialogLaunchAnimator.Callback", "dreamManager.isDreaming failed", e);
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isUnlocked() {
+ return keyguardStateController.isUnlocked();
+ }
+
+ @Override
+ public boolean isShowingAlternateAuthOnUnlock() {
+ return statusBarKeyguardViewManager.get().shouldShowAltAuth();
+ }
+ };
+ return new DialogLaunchAnimator(callback, interactionJankMonitor);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
index 351a4be..68bf69a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java
@@ -142,7 +142,7 @@
*/
@MainThread
@SysUISingleton
-public class NotifCollection implements Dumpable {
+public class NotifCollection implements Dumpable, PipelineDumpable {
private final IStatusBarService mStatusBarService;
private final SystemClock mClock;
private final NotifPipelineFlags mNotifPipelineFlags;
@@ -870,6 +870,14 @@
}
}
+ @Override
+ public void dumpPipeline(@NonNull PipelineDumper d) {
+ d.dump("notifCollectionListeners", mNotifCollectionListeners);
+ d.dump("lifetimeExtenders", mLifetimeExtenders);
+ d.dump("dismissInterceptors", mDismissInterceptors);
+ d.dump("buildListener", mBuildListener);
+ }
+
private final BatchableNotificationHandler mNotifHandler = new BatchableNotificationHandler() {
@Override
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineDumpable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineDumpable.kt
new file mode 100644
index 0000000..a1aec3f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineDumpable.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.notification.collection
+
+interface PipelineDumpable {
+ fun dumpPipeline(d: PipelineDumper)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineDumper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineDumper.kt
new file mode 100644
index 0000000..a10c745
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineDumper.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.statusbar.notification.collection
+
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.withIncreasedIndent
+import java.io.PrintWriter
+
+class PipelineDumper(pw: PrintWriter) {
+ private val ipw = pw.asIndenting()
+
+ fun print(a: Any?) = ipw.print(a)
+ fun println(a: Any?) = ipw.println(a)
+ fun withIncreasedIndent(b: () -> Unit) = ipw.withIncreasedIndent(b)
+ fun withIncreasedIndent(r: Runnable) = ipw.withIncreasedIndent(r)
+
+ fun dump(label: String, value: Any?) {
+ ipw.print("$label: ")
+ dump(value)
+ }
+
+ private fun dump(value: Any?) = when (value) {
+ null, is String, is Int -> println(value)
+ is Collection<*> -> dumpCollection(value)
+ else -> {
+ println(value.fullPipelineName)
+ withIncreasedIndent { (value as? PipelineDumpable)?.dumpPipeline(this) }
+ }
+ }
+
+ private fun dumpCollection(values: Collection<Any?>) {
+ println(values.size)
+ withIncreasedIndent { values.forEach { dump(it) } }
+ }
+}
+
+private val Any.bareClassName: String get() {
+ val className = javaClass.name
+ val packageName = javaClass.`package`.name
+ return className.substring(packageName.length + 1)
+}
+
+private val Any.barePipelineName: String? get() = when (this) {
+ is NotifLifetimeExtender -> name
+ is NotifDismissInterceptor -> name
+ is Pluggable<*> -> name
+ else -> null
+}
+
+private val Any.fullPipelineName: String get() =
+ barePipelineName?.let { "\"$it\" ($bareClassName)" } ?: bareClassName
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 075a0dc..420f21d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -87,7 +87,7 @@
*/
@MainThread
@SysUISingleton
-public class ShadeListBuilder implements Dumpable {
+public class ShadeListBuilder implements Dumpable, PipelineDumpable {
private final SystemClock mSystemClock;
private final ShadeListBuilderLogger mLogger;
private final NotificationInteractionTracker mInteractionTracker;
@@ -1396,6 +1396,21 @@
"\t\t"));
}
+ @Override
+ public void dumpPipeline(@NonNull PipelineDumper d) {
+ d.dump("choreographer", mChoreographer);
+ d.dump("notifPreGroupFilters", mNotifPreGroupFilters);
+ d.dump("onBeforeTransformGroupsListeners", mOnBeforeTransformGroupsListeners);
+ d.dump("notifPromoters", mNotifPromoters);
+ d.dump("onBeforeSortListeners", mOnBeforeSortListeners);
+ d.dump("notifSections", mNotifSections);
+ d.dump("notifComparators", mNotifComparators);
+ d.dump("onBeforeFinalizeFilterListeners", mOnBeforeFinalizeFilterListeners);
+ d.dump("notifFinalizeFilters", mNotifFinalizeFilters);
+ d.dump("onBeforeRenderListListeners", mOnBeforeRenderListListeners);
+ d.dump("onRenderListListener", mOnRenderListListener);
+ }
+
/** See {@link #setOnRenderListListener(OnRenderListListener)} */
public interface OnRenderListListener {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
index 050b4c1..98f2167 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescer.java
@@ -32,6 +32,8 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
+import com.android.systemui.statusbar.notification.collection.PipelineDumper;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
@@ -63,7 +65,7 @@
* passed along to the NotifCollection.
*/
@MainThread
-public class GroupCoalescer implements Dumpable {
+public class GroupCoalescer implements Dumpable, PipelineDumpable {
private final DelayableExecutor mMainExecutor;
private final SystemClock mClock;
private final GroupCoalescerLogger mLogger;
@@ -314,6 +316,11 @@
}
}
+ @Override
+ public void dumpPipeline(@NonNull PipelineDumper d) {
+ d.dump("handler", mHandler);
+ }
+
private final Comparator<CoalescedEvent> mEventComparator = (o1, o2) -> {
int cmp = Boolean.compare(
o2.getSbn().getNotification().isGroupSummary(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 891e25e..1399385 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -15,24 +15,22 @@
*/
package com.android.systemui.statusbar.notification.collection.coordinator
-import com.android.systemui.Dumpable
-import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable
+import com.android.systemui.statusbar.notification.collection.PipelineDumper
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
-import java.io.PrintWriter
import javax.inject.Inject
/**
* Handles the attachment of [Coordinator]s to the [NotifPipeline] so that the
* Coordinators can register their respective callbacks.
*/
-interface NotifCoordinators : Coordinator, Dumpable
+interface NotifCoordinators : Coordinator, PipelineDumpable
@CoordinatorScope
class NotifCoordinatorsImpl @Inject constructor(
- dumpManager: DumpManager,
notifPipelineFlags: NotifPipelineFlags,
dataStoreCoordinator: DataStoreCoordinator,
hideLocallyDismissedNotifsCoordinator: HideLocallyDismissedNotifsCoordinator,
@@ -66,8 +64,6 @@
* Creates all the coordinators.
*/
init {
- dumpManager.registerDumpable(TAG, this)
-
// TODO(b/208866714): formalize the system by which some coordinators may be required by the
// pipeline, such as this DataStoreCoordinator which cannot be removed, as it's a critical
// glue between the pipeline and parts of SystemUI which depend on pipeline output via the
@@ -121,15 +117,12 @@
pipeline.setSections(mOrderedSections)
}
- override fun dump(pw: PrintWriter, args: Array<String>) {
- pw.println()
- pw.println("$TAG:")
- for (c in mCoordinators) {
- pw.println("\t${c.javaClass}")
- }
- for (s in mOrderedSections) {
- pw.println("\t${s.name}")
- }
+ /*
+ * As part of the NotifPipeline dumpable, dumps the list of coordinators; sections are omitted
+ * as they are dumped in the RenderStageManager instead.
+ */
+ override fun dumpPipeline(d: PipelineDumper) = with(d) {
+ dump("coordinators", mCoordinators)
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
index 24ef580..a34d033 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
@@ -18,6 +18,8 @@
import android.util.Log;
+import androidx.annotation.NonNull;
+
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
@@ -25,12 +27,15 @@
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
+import com.android.systemui.statusbar.notification.collection.PipelineDumper;
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
import com.android.systemui.statusbar.notification.collection.render.RenderStageManager;
+import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager;
import com.android.systemui.statusbar.notification.collection.render.ShadeViewManagerFactory;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -42,7 +47,7 @@
* Initialization code for the new notification pipeline.
*/
@SysUISingleton
-public class NotifPipelineInitializer implements Dumpable {
+public class NotifPipelineInitializer implements Dumpable, PipelineDumpable {
private final NotifPipeline mPipelineWrapper;
private final GroupCoalescer mGroupCoalescer;
private final NotifCollection mNotifCollection;
@@ -53,6 +58,9 @@
private final DumpManager mDumpManager;
private final ShadeViewManagerFactory mShadeViewManagerFactory;
+ /* These are saved just for dumping. */
+ private ShadeViewManager mShadeViewManager;
+ private NotificationListener mNotificationService;
@Inject
public NotifPipelineInitializer(
@@ -83,9 +91,10 @@
NotificationRowBinderImpl rowBinder,
NotificationListContainer listContainer,
NotifStackController stackController) {
-
mDumpManager.registerDumpable("NotifPipeline", this);
+ mNotificationService = notificationService;
+
// Setup inflation
mNotifInflater.setRowBinder(rowBinder);
@@ -93,13 +102,12 @@
mNotifPluggableCoordinators.attach(mPipelineWrapper);
// Wire up pipeline
- mShadeViewManagerFactory
- .create(listContainer, stackController)
- .attach(mRenderStageManager);
+ mShadeViewManager = mShadeViewManagerFactory.create(listContainer, stackController);
+ mShadeViewManager.attach(mRenderStageManager);
mRenderStageManager.attach(mListBuilder);
mListBuilder.attach(mNotifCollection);
mNotifCollection.attach(mGroupCoalescer);
- mGroupCoalescer.attach(notificationService);
+ mGroupCoalescer.attach(mNotificationService);
Log.d(TAG, "Notif pipeline initialized."
+ " rendering=" + true);
@@ -107,8 +115,37 @@
@Override
public void dump(PrintWriter pw, String[] args) {
- mNotifPluggableCoordinators.dump(pw, args);
- mGroupCoalescer.dump(pw, args);
+ dumpPipeline(new PipelineDumper(pw));
+ }
+
+ @Override
+ public void dumpPipeline(@NonNull PipelineDumper d) {
+ d.println("STAGE 0: SETUP");
+ d.dump("notifPluggableCoordinators", mNotifPluggableCoordinators);
+ d.println("");
+
+ d.println("STAGE 1: LISTEN");
+ d.dump("notificationService", mNotificationService);
+ d.println("");
+
+ d.println("STAGE 2: BATCH EVENTS");
+ d.dump("groupCoalescer", mGroupCoalescer);
+ d.println("");
+
+ d.println("STAGE 3: COLLECT");
+ d.dump("notifCollection", mNotifCollection);
+ d.println("");
+
+ d.println("STAGE 4: BUILD LIST");
+ d.dump("listBuilder", mListBuilder);
+ d.println("");
+
+ d.println("STAGE 5: DISPATCH RENDER");
+ d.dump("renderStageManager", mRenderStageManager);
+ d.println("");
+
+ d.println("STAGE 6: UPDATE SHADE");
+ d.dump("shadeViewManager", mShadeViewManager);
}
private static final String TAG = "NotifPipeline";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
index ea66f3b..9765e8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection.listbuilder
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable
+import com.android.systemui.statusbar.notification.collection.PipelineDumper
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
import com.android.systemui.statusbar.notification.collection.render.NodeController
@@ -24,10 +26,18 @@
data class NotifSection(
val sectioner: NotifSectioner,
val index: Int
-) {
+) : PipelineDumpable {
@PriorityBucket
val bucket: Int = sectioner.bucket
val label: String = "$index:$bucket:${sectioner.name}"
val headerController: NodeController? = sectioner.headerNodeController
val comparator: NotifComparator? = sectioner.comparator
+
+ override fun dumpPipeline(d: PipelineDumper) = with(d) {
+ dump("index", index)
+ dump("bucket", bucket)
+ dump("sectioner", sectioner)
+ dump("headerController", headerController)
+ dump("comparator", comparator)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
index a9c3987..7a37846 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
@@ -20,6 +20,8 @@
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable
+import com.android.systemui.statusbar.notification.collection.PipelineDumper
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
@@ -33,7 +35,7 @@
* provided to [setViewRenderer].
*/
@SysUISingleton
-class RenderStageManager @Inject constructor() {
+class RenderStageManager @Inject constructor() : PipelineDumpable {
private val onAfterRenderListListeners = mutableListOf<OnAfterRenderListListener>()
private val onAfterRenderGroupListeners = mutableListOf<OnAfterRenderGroupListener>()
private val onAfterRenderEntryListeners = mutableListOf<OnAfterRenderEntryListener>()
@@ -75,6 +77,13 @@
onAfterRenderEntryListeners.add(listener)
}
+ override fun dumpPipeline(d: PipelineDumper) = with(d) {
+ dump("ViewRenderer", viewRenderer)
+ dump("OnAfterRenderListListeners", onAfterRenderListListeners)
+ dump("OnAfterRenderGroupListeners", onAfterRenderGroupListeners)
+ dump("OnAfterRenderEntryListeners", onAfterRenderEntryListeners)
+ }
+
private fun dispatchOnAfterRenderList(
viewRenderer: NotifViewRenderer,
entries: List<ListEntry>
@@ -139,4 +148,4 @@
}
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
index b76169f..2073e92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.notification.collection.render
import android.view.View
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable
+import com.android.systemui.statusbar.notification.collection.PipelineDumper
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
@@ -28,7 +30,7 @@
class RootNodeController(
private val listContainer: NotificationListContainer,
override val view: View
-) : NodeController {
+) : NodeController, PipelineDumpable {
override val nodeLabel: String = "<root>"
override fun getChildAt(index: Int): View? {
@@ -59,4 +61,8 @@
listContainer.setChildTransferInProgress(false)
}
}
+
+ override fun dumpPipeline(d: PipelineDumper) = with(d) {
+ dump("listContainer", listContainer)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index 51dc728..df8e87f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -22,6 +22,8 @@
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable
+import com.android.systemui.statusbar.notification.collection.PipelineDumper
import com.android.systemui.statusbar.notification.collection.provider.SectionHeaderVisibilityProvider
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.util.traceSection
@@ -43,12 +45,12 @@
nodeSpecBuilderLogger: NodeSpecBuilderLogger,
shadeViewDifferLogger: ShadeViewDifferLogger,
private val viewBarn: NotifViewBarn
-) {
+) : PipelineDumpable {
// We pass a shim view here because the listContainer may not actually have a view associated
// with it and the differ never actually cares about the root node's view.
private val rootController = RootNodeController(listContainer, View(context))
private val specBuilder = NodeSpecBuilder(mediaContainerController, featureManager,
- sectionHeaderVisibilityProvider, viewBarn, nodeSpecBuilderLogger)
+ sectionHeaderVisibilityProvider, viewBarn, nodeSpecBuilderLogger)
private val viewDiffer = ShadeViewDiffer(rootController, shadeViewDifferLogger)
/** Method for attaching this manager to the pipeline. */
@@ -56,6 +58,12 @@
renderStageManager.setViewRenderer(viewRenderer)
}
+ override fun dumpPipeline(d: PipelineDumper) = with(d) {
+ dump("rootController", rootController)
+ dump("specBuilder", specBuilder)
+ dump("viewDiffer", viewDiffer)
+ }
+
private val viewRenderer = object : NotifViewRenderer {
override fun onRenderList(notifList: List<ListEntry>) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index e5835a8..cc539b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -86,6 +86,8 @@
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
+import com.android.systemui.statusbar.notification.collection.PipelineDumper;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy.OnGroupChangeListener;
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
@@ -1560,7 +1562,8 @@
}
}
- private class NotificationListContainerImpl implements NotificationListContainer {
+ private class NotificationListContainerImpl implements NotificationListContainer,
+ PipelineDumpable {
@Override
public void setChildTransferInProgress(boolean childTransferInProgress) {
@@ -1706,6 +1709,12 @@
public void setWillExpand(boolean willExpand) {
mView.setWillExpand(willExpand);
}
+
+ @Override
+ public void dumpPipeline(@NonNull PipelineDumper d) {
+ d.dump("NotificationStackScrollLayoutController.this",
+ NotificationStackScrollLayoutController.this);
+ }
}
class TouchHandler implements Gefingerpoken {
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 3880ee3..d5c6f89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -471,7 +471,8 @@
showBouncer(scrimmed);
}
- private boolean shouldShowAltAuth() {
+ /** Whether we should show the alternate authentication instead of the traditional bouncer. */
+ public boolean shouldShowAltAuth() {
return mAlternateAuthInterceptor != null
&& mKeyguardUpdateManager.isUnlockingWithBiometricAllowed(true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index e22a896..4c76270 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -405,8 +405,8 @@
}
@Override
- public void onEntryUpdated(NotificationEntry entry) {
- BubblesManager.this.onEntryUpdated(entry);
+ public void onEntryUpdated(NotificationEntry entry, boolean fromSystem) {
+ BubblesManager.this.onEntryUpdated(entry, fromSystem);
}
@Override
@@ -444,9 +444,10 @@
}
}
- void onEntryUpdated(NotificationEntry entry) {
+ void onEntryUpdated(NotificationEntry entry, boolean fromSystem) {
+ boolean shouldBubble = mNotificationInterruptStateProvider.shouldBubbleUp(entry);
mBubbles.onEntryUpdated(notifToBubbleEntry(entry),
- mNotificationInterruptStateProvider.shouldBubbleUp(entry));
+ shouldBubble, fromSystem);
}
void onEntryRemoved(NotificationEntry entry) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 8d27f24..c677371 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -22,6 +22,7 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
+import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
import static com.google.common.truth.Truth.assertThat;
@@ -109,6 +110,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
+import org.mockito.internal.util.reflection.FieldSetter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -200,9 +202,10 @@
private ArgumentCaptor<CancellationSignal> mCancellationSignalCaptor;
// Direct executor
- private Executor mBackgroundExecutor = Runnable::run;
- private Executor mMainExecutor = Runnable::run;
+ private final Executor mBackgroundExecutor = Runnable::run;
+ private final Executor mMainExecutor = Runnable::run;
private TestableLooper mTestableLooper;
+ private Handler mHandler;
private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
private TestableContext mSpiedContext;
private MockitoSession mMockitoSession;
@@ -291,6 +294,13 @@
mBiometricEnabledOnKeyguardCallback = mBiometricEnabledCallbackArgCaptor.getValue();
biometricsEnabledForCurrentUser();
+ mHandler = spy(mKeyguardUpdateMonitor.getHandler());
+ try {
+ FieldSetter.setField(mKeyguardUpdateMonitor,
+ KeyguardUpdateMonitor.class.getDeclaredField("mHandler"), mHandler);
+ } catch (NoSuchFieldException e) {
+
+ }
verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue();
mKeyguardUpdateMonitor.registerCallback(mTestCallback);
@@ -330,7 +340,7 @@
when(mTelephonyManager.getActiveModemCount()).thenReturn(1);
when(mTelephonyManager.getSimState(anyInt())).thenReturn(state);
- when(mSubscriptionManager.getSubscriptionIds(anyInt())).thenReturn(new int[] { subId });
+ when(mSubscriptionManager.getSubscriptionIds(anyInt())).thenReturn(new int[]{subId});
KeyguardUpdateMonitor testKUM = new TestableKeyguardUpdateMonitor(mSpiedContext);
@@ -505,7 +515,7 @@
// Even SimState Loaded, still need ACTION_SERVICE_STATE turn on mTelephonyCapable
assertThat(mKeyguardUpdateMonitor.mTelephonyCapable).isFalse();
- Intent intentServiceState = new Intent(Intent.ACTION_SERVICE_STATE);
+ Intent intentServiceState = new Intent(Intent.ACTION_SERVICE_STATE);
intentSimState.putExtra(Intent.EXTRA_SIM_STATE
, Intent.SIM_STATE_LOADED);
mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
@@ -520,7 +530,7 @@
mTestableLooper.processAllMessages();
verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
- anyInt());
+ anyInt());
verify(mFingerprintManager, never()).detectFingerprint(any(), any(), anyInt());
}
@@ -791,7 +801,8 @@
public void testBiometricsCleared_whenUserSwitches() throws Exception {
final IRemoteCallback reply = new IRemoteCallback.Stub() {
@Override
- public void sendResult(Bundle data) {} // do nothing
+ public void sendResult(Bundle data) {
+ } // do nothing
};
final BiometricAuthenticated dummyAuthentication =
new BiometricAuthenticated(true /* authenticated */, true /* strong */);
@@ -809,7 +820,8 @@
public void testMultiUserJankMonitor_whenUserSwitches() throws Exception {
final IRemoteCallback reply = new IRemoteCallback.Stub() {
@Override
- public void sendResult(Bundle data) {} // do nothing
+ public void sendResult(Bundle data) {
+ } // do nothing
};
mKeyguardUpdateMonitor.handleUserSwitchComplete(10 /* user */);
verify(mInteractionJankMonitor).end(InteractionJankMonitor.CUJ_USER_SWITCH);
@@ -1499,6 +1511,34 @@
assertThat(cancelSignal.isCanceled()).isTrue();
}
+ @Test
+ public void testFingerprintCanAuth_whenCancellationNotReceivedAndAuthFailed() {
+ mKeyguardUpdateMonitor.dispatchStartedWakingUp();
+ mTestableLooper.processAllMessages();
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
+
+ verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
+ verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
+ anyInt());
+
+ mKeyguardUpdateMonitor.onFaceAuthenticated(0, false);
+ // Make sure keyguard is going away after face auth attempt, and that it calls
+ // updateBiometricStateListeningState.
+ mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(false);
+ mTestableLooper.processAllMessages();
+
+ verify(mHandler).postDelayed(mKeyguardUpdateMonitor.mFpCancelNotReceived,
+ DEFAULT_CANCEL_SIGNAL_TIMEOUT);
+
+ mKeyguardUpdateMonitor.onFingerprintAuthenticated(0, true);
+ mTestableLooper.processAllMessages();
+
+ verify(mHandler, times(1)).removeCallbacks(mKeyguardUpdateMonitor.mFpCancelNotReceived);
+ mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
+ mTestableLooper.processAllMessages();
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(anyBoolean())).isEqualTo(true);
+ }
+
private void fingerprintIsNotEnrolled() {
when(mFingerprintManager.hasEnrolledTemplates(mCurrentUserId)).thenReturn(false);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
index c48cbb1..0f11241 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -26,6 +26,7 @@
import junit.framework.Assert.assertNull
import junit.framework.Assert.assertTrue
import junit.framework.AssertionFailedError
+import kotlin.concurrent.thread
import org.junit.After
import org.junit.Before
import org.junit.Rule
@@ -34,19 +35,18 @@
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.Spy
import org.mockito.junit.MockitoJUnit
-import kotlin.concurrent.thread
@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
class ActivityLaunchAnimatorTest : SysuiTestCase() {
private val launchContainer = LinearLayout(mContext)
- private val testLaunchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
+ private val testLaunchAnimator = fakeLaunchAnimator()
@Mock lateinit var callback: ActivityLaunchAnimator.Callback
@Mock lateinit var listener: ActivityLaunchAnimator.Listener
@Spy private val controller = TestLaunchAnimatorController(launchContainer)
@@ -77,12 +77,13 @@
// We start in a new thread so that we can ensure that the callbacks are called in the main
// thread.
thread {
- animator.startIntentWithAnimation(
+ animator.startIntentWithAnimation(
controller = controller,
animate = animate,
intentStarter = intentStarter
- )
- }.join()
+ )
+ }
+ .join()
}
@Test
@@ -197,14 +198,25 @@
val bounds = Rect(10 /* left */, 20 /* top */, 30 /* right */, 40 /* bottom */)
val taskInfo = ActivityManager.RunningTaskInfo()
taskInfo.topActivity = ComponentName("com.android.systemui", "FakeActivity")
- taskInfo.topActivityInfo = ActivityInfo().apply {
- applicationInfo = ApplicationInfo()
- }
+ taskInfo.topActivityInfo = ActivityInfo().apply { applicationInfo = ApplicationInfo() }
return RemoteAnimationTarget(
- 0, RemoteAnimationTarget.MODE_OPENING, SurfaceControl(), false, Rect(), Rect(), 0,
- Point(), Rect(), bounds, WindowConfiguration(), false, SurfaceControl(), Rect(),
- taskInfo, false
+ 0,
+ RemoteAnimationTarget.MODE_OPENING,
+ SurfaceControl(),
+ false,
+ Rect(),
+ Rect(),
+ 0,
+ Point(),
+ Rect(),
+ bounds,
+ WindowConfiguration(),
+ false,
+ SurfaceControl(),
+ Rect(),
+ taskInfo,
+ false
)
}
}
@@ -213,17 +225,17 @@
* A simple implementation of [ActivityLaunchAnimator.Controller] which throws if it is called
* outside of the main thread.
*/
-private class TestLaunchAnimatorController(
- override var launchContainer: ViewGroup
-) : ActivityLaunchAnimator.Controller {
- override fun createAnimatorState() = LaunchAnimator.State(
+private class TestLaunchAnimatorController(override var launchContainer: ViewGroup) :
+ ActivityLaunchAnimator.Controller {
+ override fun createAnimatorState() =
+ LaunchAnimator.State(
top = 100,
bottom = 200,
left = 300,
right = 400,
topCornerRadius = 10f,
bottomCornerRadius = 20f
- )
+ )
private fun assertOnMainThread() {
if (Looper.myLooper() != Looper.getMainLooper()) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index 4218e09..7c1e384 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -5,7 +5,6 @@
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
-import android.service.dreams.IDreamManager
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.ViewUtils
@@ -38,19 +37,16 @@
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class DialogLaunchAnimatorTest : SysuiTestCase() {
- private val launchAnimator = LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
private val attachedViews = mutableSetOf<View>()
- @Mock lateinit var dreamManager: IDreamManager
@Mock lateinit var interactionJankMonitor: InteractionJankMonitor
@get:Rule val rule = MockitoJUnit.rule()
@Before
fun setUp() {
- dialogLaunchAnimator = DialogLaunchAnimator(
- dreamManager, interactionJankMonitor, launchAnimator, isForTesting = true
- )
+ dialogLaunchAnimator =
+ fakeDialogLaunchAnimator(interactionJankMonitor = interactionJankMonitor)
}
@After
@@ -153,6 +149,22 @@
}
@Test
+ fun testActivityLaunchWhenLockedWithoutAlternateAuth() {
+ val dialogLaunchAnimator =
+ fakeDialogLaunchAnimator(isUnlocked = false, isShowingAlternateAuthOnUnlock = false)
+ val dialog = createAndShowDialog(dialogLaunchAnimator)
+ assertNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView))
+ }
+
+ @Test
+ fun testActivityLaunchWhenLockedWithAlternateAuth() {
+ val dialogLaunchAnimator =
+ fakeDialogLaunchAnimator(isUnlocked = false, isShowingAlternateAuthOnUnlock = true)
+ val dialog = createAndShowDialog(dialogLaunchAnimator)
+ assertNotNull(dialogLaunchAnimator.createActivityLaunchController(dialog.contentView))
+ }
+
+ @Test
fun testDialogAnimationIsChangedByAnimator() {
// Important: the power menu animation relies on this behavior to know when to animate (see
// http://ag/16774605).
@@ -193,11 +205,13 @@
verify(interactionJankMonitor).end(InteractionJankMonitor.CUJ_USER_DIALOG_OPEN)
}
- private fun createAndShowDialog(): TestDialog {
+ private fun createAndShowDialog(
+ animator: DialogLaunchAnimator = dialogLaunchAnimator,
+ ): TestDialog {
val touchSurface = createTouchSurface()
return runOnMainThreadAndWaitForIdleSync {
val dialog = TestDialog(context)
- dialogLaunchAnimator.showFromView(dialog, touchSurface)
+ animator.showFromView(dialog, touchSurface)
dialog
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt
deleted file mode 100644
index dadf94e..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TestValues.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-package com.android.systemui.animation
-
-/**
- * A [LaunchAnimator.Timings] to be used in tests.
- *
- * Note that all timings except the total duration are non-zero to avoid divide-by-zero exceptions
- * when computing the progress of a sub-animation (the contents fade in/out).
- */
-val TEST_TIMINGS = LaunchAnimator.Timings(
- totalDuration = 0L,
- contentBeforeFadeOutDelay = 1L,
- contentBeforeFadeOutDuration = 1L,
- contentAfterFadeInDelay = 1L,
- contentAfterFadeInDuration = 1L
-)
-
-/** A [LaunchAnimator.Interpolators] to be used in tests. */
-val TEST_INTERPOLATORS = LaunchAnimator.Interpolators(
- positionInterpolator = Interpolators.STANDARD,
- positionXInterpolator = Interpolators.STANDARD,
- contentBeforeFadeOutInterpolator = Interpolators.STANDARD,
- contentAfterFadeInInterpolator = Interpolators.STANDARD
-)
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
index 2915f5a..e099c92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
@@ -20,6 +20,7 @@
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_CAST_INFO;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_DATE;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_HOME_CONTROLS;
+import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_SMARTSPACE;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER;
import static com.android.systemui.dreams.complication.ComplicationUtils.convertComplicationType;
@@ -60,6 +61,8 @@
.isEqualTo(COMPLICATION_TYPE_CAST_INFO);
assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_HOME_CONTROLS))
.isEqualTo(COMPLICATION_TYPE_HOME_CONTROLS);
+ assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_SMARTSPACE))
+ .isEqualTo(COMPLICATION_TYPE_SMARTSPACE);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
index 4abb973..56aff3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
@@ -24,16 +24,11 @@
@Before
fun setup() {
outputWriter = StringWriter()
- buffer = createBuffer(UNBOUNDED_STACK_TRACE, NESTED_TRACE_DEPTH)
+ buffer = createBuffer()
}
- private fun createBuffer(rootTraceDepth: Int, nestedTraceDepth: Int): LogBuffer {
- return LogBuffer("TestBuffer",
- 1,
- logcatEchoTracker,
- false,
- rootStackTraceDepth = rootTraceDepth,
- nestedStackTraceDepth = nestedTraceDepth)
+ private fun createBuffer(): LogBuffer {
+ return LogBuffer("TestBuffer", 1, logcatEchoTracker, false)
}
@Test
@@ -56,95 +51,83 @@
}
@Test
- fun dump_writesExceptionAndStacktraceLimitedToGivenDepth() {
- buffer = createBuffer(rootTraceDepth = 2, nestedTraceDepth = -1)
- // stack trace depth of 5
- val exception = createTestException("Exception message", "TestClass", 5)
+ fun dump_writesExceptionAndStacktrace() {
+ buffer = createBuffer()
+ val exception = createTestException("Exception message", "TestClass")
buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
val dumpedString = dumpBuffer()
- // logs are limited to depth 2
- assertThat(dumpedString).contains("E Tag: Extra message")
- assertThat(dumpedString).contains("E Tag: java.lang.RuntimeException: Exception message")
- assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:1)")
- assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:2)")
- assertThat(dumpedString)
- .doesNotContain("E Tag: \tat TestClass.TestMethod(TestClass.java:3)")
+ assertThat(dumpedString).contains("Extra message")
+ assertThat(dumpedString).contains("java.lang.RuntimeException: Exception message")
+ assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:1)")
+ assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:2)")
}
@Test
- fun dump_writesCauseAndStacktraceLimitedToGivenDepth() {
- buffer = createBuffer(rootTraceDepth = 0, nestedTraceDepth = 2)
+ fun dump_writesCauseAndStacktrace() {
+ buffer = createBuffer()
val exception = createTestException("Exception message",
"TestClass",
- 1,
- cause = createTestException("The real cause!", "TestClass", 5))
+ cause = createTestException("The real cause!", "TestClass"))
buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
val dumpedString = dumpBuffer()
- // logs are limited to depth 2
assertThat(dumpedString)
- .contains("E Tag: Caused by: java.lang.RuntimeException: The real cause!")
- assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:1)")
- assertThat(dumpedString).contains("E Tag: \tat TestClass.TestMethod(TestClass.java:2)")
- assertThat(dumpedString)
- .doesNotContain("E Tag: \tat TestClass.TestMethod(TestClass.java:3)")
+ .contains("Caused by: java.lang.RuntimeException: The real cause!")
+ assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:1)")
+ assertThat(dumpedString).contains("at TestClass.TestMethod(TestClass.java:2)")
}
@Test
- fun dump_writesSuppressedExceptionAndStacktraceLimitedToGivenDepth() {
- buffer = createBuffer(rootTraceDepth = 0, nestedTraceDepth = 2)
+ fun dump_writesSuppressedExceptionAndStacktrace() {
+ buffer = createBuffer()
val exception = RuntimeException("Root exception message")
exception.addSuppressed(
createTestException(
"First suppressed exception",
"FirstClass",
- 5,
- createTestException("Cause of suppressed exp", "ThirdClass", 5)
+ createTestException("Cause of suppressed exp", "ThirdClass")
))
exception.addSuppressed(
- createTestException("Second suppressed exception", "SecondClass", 5))
+ createTestException("Second suppressed exception", "SecondClass"))
buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
val dumpedStr = dumpBuffer()
- // logs are limited to depth 2
// first suppressed exception
assertThat(dumpedStr)
- .contains("E Tag: Suppressed: " +
+ .contains("Suppressed: " +
"java.lang.RuntimeException: First suppressed exception")
- assertThat(dumpedStr).contains("E Tag: \tat FirstClass.TestMethod(FirstClass.java:1)")
- assertThat(dumpedStr).contains("E Tag: \tat FirstClass.TestMethod(FirstClass.java:2)")
- assertThat(dumpedStr)
- .doesNotContain("E Tag: \tat FirstClass.TestMethod(FirstClass.java:3)")
+ assertThat(dumpedStr).contains("at FirstClass.TestMethod(FirstClass.java:1)")
+ assertThat(dumpedStr).contains("at FirstClass.TestMethod(FirstClass.java:2)")
assertThat(dumpedStr)
- .contains("E Tag: Caused by: java.lang.RuntimeException: Cause of suppressed exp")
- assertThat(dumpedStr).contains("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:1)")
- assertThat(dumpedStr).contains("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:2)")
- assertThat(dumpedStr)
- .doesNotContain("E Tag: \tat ThirdClass.TestMethod(ThirdClass.java:3)")
+ .contains("Caused by: java.lang.RuntimeException: Cause of suppressed exp")
+ assertThat(dumpedStr).contains("at ThirdClass.TestMethod(ThirdClass.java:1)")
+ assertThat(dumpedStr).contains("at ThirdClass.TestMethod(ThirdClass.java:2)")
// second suppressed exception
assertThat(dumpedStr)
- .contains("E Tag: Suppressed: " +
+ .contains("Suppressed: " +
"java.lang.RuntimeException: Second suppressed exception")
- assertThat(dumpedStr).contains("E Tag: \tat SecondClass.TestMethod(SecondClass.java:1)")
- assertThat(dumpedStr).contains("E Tag: \tat SecondClass.TestMethod(SecondClass.java:2)")
- assertThat(dumpedStr)
- .doesNotContain("E Tag: \tat SecondClass.TestMethod(SecondClass.java:3)")
+ assertThat(dumpedStr).contains("at SecondClass.TestMethod(SecondClass.java:1)")
+ assertThat(dumpedStr).contains("at SecondClass.TestMethod(SecondClass.java:2)")
}
private fun createTestException(
- message: String,
- errorClass: String,
- stackTraceLength: Int,
- cause: Throwable? = null
+ message: String,
+ errorClass: String,
+ cause: Throwable? = null,
): Exception {
val exception = RuntimeException(message, cause)
- exception.stackTrace = createStackTraceElements(errorClass, stackTraceLength)
+ exception.stackTrace = (1..5).map { lineNumber ->
+ StackTraceElement(errorClass,
+ "TestMethod",
+ "$errorClass.java",
+ lineNumber)
+ }.toTypedArray()
return exception
}
@@ -152,16 +135,4 @@
buffer.dump(PrintWriter(outputWriter), tailLength = 100)
return outputWriter.toString()
}
-
- private fun createStackTraceElements(
- errorClass: String,
- stackTraceLength: Int
- ): Array<StackTraceElement> {
- return (1..stackTraceLength).map { lineNumber ->
- StackTraceElement(errorClass,
- "TestMethod",
- "$errorClass.java",
- lineNumber)
- }.toTypedArray()
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 568e0cb..260bb87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -254,4 +254,30 @@
verify(mMediaOutputController).connectDevice(mMediaDevice2);
}
+
+ @Test
+ public void onItemClick_onGroupActionTriggered_verifySeekbarDisabled() {
+ when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices);
+ List<MediaDevice> selectableDevices = new ArrayList<>();
+ selectableDevices.add(mMediaDevice1);
+ when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(selectableDevices);
+ when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(true);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ mViewHolder.mContainerLayout.performClick();
+
+ assertThat(mViewHolder.mSeekBar.isEnabled()).isFalse();
+ }
+
+ @Test
+ public void onBindViewHolder_volumeControlChangeToEnabled_enableSeekbarAgain() {
+ when(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).thenReturn(false);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+ assertThat(mViewHolder.mSeekBar.isEnabled()).isFalse();
+
+ when(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).thenReturn(true);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mSeekBar.isEnabled()).isTrue();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index fee17c7..18acf3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -33,6 +33,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -621,7 +622,7 @@
assertFalse(mBubbleData.getBubbleInStackWithKey(mRow.getKey()).showDot());
// Send update
- mEntryListener.onEntryUpdated(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
// Nothing should have changed
// Notif is suppressed after expansion
@@ -789,7 +790,7 @@
@Test
public void testAddNotif_notBubble() {
mEntryListener.onEntryAdded(mNonBubbleNotifRow.getEntry());
- mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry());
+ mEntryListener.onEntryUpdated(mNonBubbleNotifRow.getEntry(), /* fromSystem= */ true);
assertThat(mBubbleController.hasBubbles()).isFalse();
}
@@ -827,7 +828,7 @@
NotificationListenerService.Ranking ranking = new RankingBuilder(
mRow.getRanking()).setCanBubble(false).build();
mRow.setRanking(ranking);
- mEntryListener.onEntryUpdated(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
assertFalse(mBubbleController.hasBubbles());
verify(mDeleteIntent, never()).send();
@@ -1432,6 +1433,100 @@
assertThat(mBubbleData.hasBubbleInStackWithKey(mBubbleEntry.getKey())).isFalse();
}
+ /**
+ * Verifies that if a bubble is in the overflow and a non-interruptive notification update
+ * comes in for it, it stays in the overflow but the entry is updated.
+ */
+ @Test
+ public void testNonInterruptiveUpdate_doesntBubbleFromOverflow() {
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+
+ // Dismiss the bubble so it's in the overflow
+ mBubbleController.removeBubble(
+ mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())).isTrue();
+
+ // Update the entry to not show in shade
+ setMetadataFlags(mRow,
+ Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, /* enableFlag= */ true);
+ mBubbleController.updateBubble(mBubbleEntry,
+ /* suppressFlyout= */ false, /* showInShade= */ true);
+
+ // Check that the update was applied - shouldn't be show in shade
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
+ // Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
+ verify(mBubbleController, times(1)).inflateAndAdd(
+ any(Bubble.class), anyBoolean(), anyBoolean());
+ }
+
+ /**
+ * Verifies that if a bubble is active, and a non-interruptive notification update comes in for
+ * it, it doesn't trigger a new inflate and add for that bubble.
+ */
+ @Test
+ public void testNonInterruptiveUpdate_doesntTriggerInflate() {
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+
+ // Update the entry to not show in shade
+ setMetadataFlags(mRow,
+ Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, /* enableFlag= */ true);
+ mBubbleController.updateBubble(mBubbleEntry,
+ /* suppressFlyout= */ false, /* showInShade= */ true);
+
+ // Check that the update was applied - shouldn't be show in shade
+ assertBubbleNotificationSuppressedFromShade(mBubbleEntry);
+ // Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
+ verify(mBubbleController, times(1)).inflateAndAdd(
+ any(Bubble.class), anyBoolean(), anyBoolean());
+ }
+
+ /**
+ * Verifies that if a bubble is in the overflow and a non-interruptive notification update
+ * comes in for it with FLAG_BUBBLE that the flag is removed.
+ */
+ @Test
+ public void testNonInterruptiveUpdate_doesntOverrideOverflowFlagBubble() {
+ mEntryListener.onEntryAdded(mRow);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ true);
+ assertBubbleNotificationNotSuppressedFromShade(mBubbleEntry);
+
+ // Dismiss the bubble so it's in the overflow
+ mBubbleController.removeBubble(
+ mRow.getKey(), Bubbles.DISMISS_USER_GESTURE);
+ assertThat(mBubbleData.hasOverflowBubbleWithKey(mRow.getKey())).isTrue();
+ // Once it's in the overflow it's not actively a bubble (doesn't have FLAG_BUBBLE)
+ Bubble b = mBubbleData.getOverflowBubbleWithKey(mBubbleEntry.getKey());
+ assertThat(b.isBubble()).isFalse();
+
+ // Send a non-notifying update that has FLAG_BUBBLE
+ mRow.getSbn().getNotification().flags = FLAG_BUBBLE;
+ assertThat(mRow.getSbn().getNotification().isBubbleNotification()).isTrue();
+ mBubbleController.updateBubble(mBubbleEntry,
+ /* suppressFlyout= */ false, /* showInShade= */ true);
+
+ // Verify that it still doesn't have FLAG_BUBBLE because it's in the overflow.
+ b = mBubbleData.getOverflowBubbleWithKey(mBubbleEntry.getKey());
+ assertThat(b.isBubble()).isFalse();
+ }
+
+ @Test
+ public void testNonSystemUpdatesIgnored() {
+ mEntryListener.onEntryAdded(mRow);
+ assertThat(mBubbleController.hasBubbles()).isTrue();
+
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
+ mEntryListener.onEntryUpdated(mRow, /* fromSystem= */ false);
+
+ // Check that it wasn't inflated (1 because it would've been inflated via onEntryAdded)
+ verify(mBubbleController, times(1)).inflateAndAdd(
+ any(Bubble.class), anyBoolean(), anyBoolean());
+ }
+
/** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index c52ea60f..c83189d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui;
+import static com.android.systemui.animation.FakeDialogLaunchAnimatorKt.fakeDialogLaunchAnimator;
+
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -34,6 +36,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.broadcast.FakeBroadcastDispatcher;
import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger;
@@ -119,6 +122,7 @@
// is missing (constructing the actual one would throw).
// TODO(b/219008720): Remove this.
mDependency.injectMockDependency(SystemUIDialogManager.class);
+ mDependency.injectTestDependency(DialogLaunchAnimator.class, fakeDialogLaunchAnimator());
}
@After
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.kt
new file mode 100644
index 0000000..990db77
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogLaunchAnimator.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.animation
+
+import com.android.internal.jank.InteractionJankMonitor
+import org.mockito.Mockito.mock
+
+/** A [DialogLaunchAnimator] to be used in tests. */
+@JvmOverloads
+fun fakeDialogLaunchAnimator(
+ isUnlocked: Boolean = true,
+ isShowingAlternateAuthOnUnlock: Boolean = false,
+ interactionJankMonitor: InteractionJankMonitor = mock(InteractionJankMonitor::class.java),
+): DialogLaunchAnimator {
+ return DialogLaunchAnimator(
+ FakeCallback(
+ isUnlocked = isUnlocked,
+ isShowingAlternateAuthOnUnlock = isShowingAlternateAuthOnUnlock,
+ ),
+ interactionJankMonitor,
+ fakeLaunchAnimator(),
+ isForTesting = true,
+ )
+}
+
+private class FakeCallback(
+ private val isDreaming: Boolean = false,
+ private val isUnlocked: Boolean = true,
+ private val isShowingAlternateAuthOnUnlock: Boolean = false,
+) : DialogLaunchAnimator.Callback {
+ override fun isDreaming(): Boolean = isDreaming
+ override fun isUnlocked(): Boolean = isUnlocked
+ override fun isShowingAlternateAuthOnUnlock() = isShowingAlternateAuthOnUnlock
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt
new file mode 100644
index 0000000..5b431e7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeLaunchAnimator.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.animation
+
+/** A [LaunchAnimator] to be used in tests. */
+fun fakeLaunchAnimator(): LaunchAnimator {
+ return LaunchAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
+}
+
+/**
+ * A [LaunchAnimator.Timings] to be used in tests.
+ *
+ * Note that all timings except the total duration are non-zero to avoid divide-by-zero exceptions
+ * when computing the progress of a sub-animation (the contents fade in/out).
+ */
+private val TEST_TIMINGS =
+ LaunchAnimator.Timings(
+ totalDuration = 0L,
+ contentBeforeFadeOutDelay = 1L,
+ contentBeforeFadeOutDuration = 1L,
+ contentAfterFadeInDelay = 1L,
+ contentAfterFadeInDuration = 1L
+ )
+
+/** A [LaunchAnimator.Interpolators] to be used in tests. */
+private val TEST_INTERPOLATORS =
+ LaunchAnimator.Interpolators(
+ positionInterpolator = Interpolators.STANDARD,
+ positionXInterpolator = Interpolators.STANDARD,
+ contentBeforeFadeOutInterpolator = Interpolators.STANDARD,
+ contentAfterFadeInInterpolator = Interpolators.STANDARD
+ )
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 5eec6e5..9a98f54 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -370,6 +370,10 @@
private static final float DEFAULT_LOW_BATTERY_LEVEL = 20F;
// Decide whether charging is required to turn on the feature
private static final boolean DEFAULT_CHARGING_REQUIRED = true;
+ // Minimum GC interval sleep time in ms
+ private static final int DEFAULT_MIN_GC_SLEEPTIME = 10000;
+ // Target dirty segment ratio to aim to
+ private static final int DEFAULT_TARGET_DIRTY_RATIO = 80;
private volatile int mLifetimePercentThreshold;
private volatile int mMinSegmentsThreshold;
@@ -377,6 +381,8 @@
private volatile float mSegmentReclaimWeight;
private volatile float mLowBatteryLevel;
private volatile boolean mChargingRequired;
+ private volatile int mMinGCSleepTime;
+ private volatile int mTargetDirtyRatio;
private volatile boolean mNeedGC;
private volatile boolean mPassedLifetimeThresh;
@@ -2712,6 +2718,10 @@
"low_battery_level", DEFAULT_LOW_BATTERY_LEVEL);
mChargingRequired = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
"charging_required", DEFAULT_CHARGING_REQUIRED);
+ mMinGCSleepTime = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "min_gc_sleeptime", DEFAULT_MIN_GC_SLEEPTIME);
+ mTargetDirtyRatio = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
+ "target_dirty_ratio", DEFAULT_TARGET_DIRTY_RATIO);
// If we use the smart idle maintenance, we need to turn off GC in the traditional idle
// maintenance to avoid the conflict
@@ -2829,6 +2839,14 @@
enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
try {
+ int latestWrite = mVold.getWriteAmount();
+ if (latestWrite == -1) {
+ Slog.w(TAG, "Failed to get storage write record");
+ return;
+ }
+
+ updateStorageWriteRecords(latestWrite);
+
// Block based checkpoint process runs fstrim. So, if checkpoint is in progress
// (first boot after OTA), We skip the smart idle maintenance
if (!needsCheckpoint() || !supportsBlockCheckpoint()) {
@@ -2836,13 +2854,6 @@
return;
}
- int latestWrite = mVold.getWriteAmount();
- if (latestWrite == -1) {
- Slog.w(TAG, "Failed to get storage write record");
- return;
- }
-
- updateStorageWriteRecords(latestWrite);
int avgWriteAmount = getAverageWriteAmount();
Slog.i(TAG, "Set smart idle maintenance: " + "latest write amount: " +
@@ -2850,9 +2861,12 @@
", min segment threshold: " + mMinSegmentsThreshold +
", dirty reclaim rate: " + mDirtyReclaimRate +
", segment reclaim weight: " + mSegmentReclaimWeight +
- ", period: " + sSmartIdleMaintPeriod);
+ ", period(min): " + sSmartIdleMaintPeriod +
+ ", min gc sleep time(ms): " + mMinGCSleepTime +
+ ", target dirty ratio: " + mTargetDirtyRatio);
mVold.setGCUrgentPace(avgWriteAmount, mMinSegmentsThreshold, mDirtyReclaimRate,
- mSegmentReclaimWeight, sSmartIdleMaintPeriod);
+ mSegmentReclaimWeight, sSmartIdleMaintPeriod,
+ mMinGCSleepTime, mTargetDirtyRatio);
} else {
Slog.i(TAG, "Skipping smart idle maintenance - block based checkpoint in progress");
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 0cdf7bf..339d5d4fe 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -43,7 +43,6 @@
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
-import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
import android.net.Uri;
import android.os.Binder;
@@ -164,17 +163,6 @@
return PackageManager.DELETE_FAILED_INTERNAL_ERROR;
}
- if (PackageManagerServiceUtils.isSystemApp(uninstalledPs)) {
- UserInfo userInfo = mUserManagerInternal.getUserInfo(userId);
- if (userInfo == null || (!userInfo.isAdmin() && !mUserManagerInternal.getUserInfo(
- mUserManagerInternal.getProfileParentId(userId)).isAdmin())) {
- Slog.w(TAG, "Not removing package " + packageName
- + " as only admin user (or their profile) may downgrade system apps");
- EventLog.writeEvent(0x534e4554, "170646036", -1, packageName);
- return PackageManager.DELETE_FAILED_USER_RESTRICTED;
- }
- }
-
disabledSystemPs = mPm.mSettings.getDisabledSystemPkgLPr(packageName);
// Static shared libs can be declared by any package, so let us not
// allow removing a package if it provides a lib others depend on.
diff --git a/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
new file mode 100644
index 0000000..1497684
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java
@@ -0,0 +1,131 @@
+/*
+ * 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.pm.pkg.component;
+
+
+import android.content.pm.parsing.result.ParseInput;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.os.Build;
+import android.util.ArraySet;
+
+import com.android.internal.R;
+import com.android.server.SystemConfig;
+import com.android.server.pm.pkg.parsing.ParsingPackage;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * Utility methods for handling the tag {@code <install-constraints/>}
+ *
+ * @hide
+ */
+public class InstallConstraintsTagParser {
+
+ private static final String TAG_FINGERPRINT_PREFIX = "fingerprint-prefix";
+
+ /**
+ * @hide
+ */
+ public static ParseResult<ParsingPackage> parseInstallConstraints(
+ ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ Set<String> allowlist = SystemConfig.getInstance().getInstallConstraintsAllowlist();
+ if (!allowlist.contains(pkg.getPackageName())) {
+ return input.skip("install-constraints cannot be used by this package");
+ }
+
+ ParseResult<Set<String>> prefixes = parseFingerprintPrefixes(input, res, parser);
+ if (prefixes.isSuccess()) {
+ if (validateFingerprintPrefixes(prefixes.getResult())) {
+ return input.success(pkg);
+ } else {
+ return input.skip(
+ "Install of this package is restricted on this device; device fingerprint"
+ + " does not start with one of the allowed prefixes");
+ }
+ }
+ return input.skip(prefixes.getErrorMessage());
+ }
+
+ private static ParseResult<Set<String>> parseFingerprintPrefixes(
+ ParseInput input, Resources res, XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ Set<String> prefixes = new ArraySet<>();
+ int type;
+ while (true) {
+ // move to the tag that contains the next prefix
+ type = parser.next();
+ if (type == XmlPullParser.END_TAG) {
+ if (prefixes.size() == 0) {
+ return input.error("install-constraints must contain at least one constraint");
+ }
+ return input.success(prefixes);
+ } else if (type == XmlPullParser.START_TAG) {
+ if (parser.getName().equals(TAG_FINGERPRINT_PREFIX)) {
+ ParseResult<String> parsedPrefix =
+ readFingerprintPrefixValue(input, res, parser);
+ if (parsedPrefix.isSuccess()) {
+ prefixes.add(parsedPrefix.getResult());
+ } else {
+ return input.error(parsedPrefix.getErrorMessage());
+ }
+ } else {
+ return input.error("Unexpected tag: " + parser.getName());
+ }
+
+ // consume the end tag of this attribute
+ type = parser.next();
+ if (type != XmlPullParser.END_TAG) {
+ return input.error("Expected end tag; instead got " + type);
+ }
+ }
+ }
+ }
+
+ private static ParseResult<String> readFingerprintPrefixValue(ParseInput input, Resources res,
+ XmlResourceParser parser) {
+ TypedArray sa = res.obtainAttributes(parser,
+ R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix);
+ try {
+ String value = sa.getString(
+ R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix_value);
+ if (value == null) {
+ return input.error("Failed to specify prefix value");
+ }
+ return input.success(value);
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ private static boolean validateFingerprintPrefixes(Set<String> prefixes) {
+ String fingerprint = Build.FINGERPRINT;
+ for (String prefix : prefixes) {
+ if (fingerprint.startsWith(prefix)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index 9bfb40f..7ce7f7e 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -95,6 +95,7 @@
import com.android.server.pm.permission.CompatibilityPermissionInfo;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
import com.android.server.pm.pkg.component.ComponentParseUtils;
+import com.android.server.pm.pkg.component.InstallConstraintsTagParser;
import com.android.server.pm.pkg.component.ParsedActivity;
import com.android.server.pm.pkg.component.ParsedActivityUtils;
import com.android.server.pm.pkg.component.ParsedApexSystemService;
@@ -169,9 +170,11 @@
public static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
public static final String TAG_APPLICATION = "application";
+ public static final String TAG_ATTRIBUTION = "attribution";
public static final String TAG_COMPATIBLE_SCREENS = "compatible-screens";
public static final String TAG_EAT_COMMENT = "eat-comment";
public static final String TAG_FEATURE_GROUP = "feature-group";
+ public static final String TAG_INSTALL_CONSTRAINTS = "install-constraints";
public static final String TAG_INSTRUMENTATION = "instrumentation";
public static final String TAG_KEY_SETS = "key-sets";
public static final String TAG_MANIFEST = "manifest";
@@ -179,15 +182,16 @@
public static final String TAG_OVERLAY = "overlay";
public static final String TAG_PACKAGE = "package";
public static final String TAG_PACKAGE_VERIFIER = "package-verifier";
- public static final String TAG_ATTRIBUTION = "attribution";
public static final String TAG_PERMISSION = "permission";
public static final String TAG_PERMISSION_GROUP = "permission-group";
public static final String TAG_PERMISSION_TREE = "permission-tree";
+ public static final String TAG_PROFILEABLE = "profileable";
public static final String TAG_PROTECTED_BROADCAST = "protected-broadcast";
public static final String TAG_QUERIES = "queries";
+ public static final String TAG_RECEIVER = "receiver";
public static final String TAG_RESTRICT_UPDATE = "restrict-update";
- public static final String TAG_SUPPORT_SCREENS = "supports-screens";
public static final String TAG_SUPPORTS_INPUT = "supports-input";
+ public static final String TAG_SUPPORT_SCREENS = "supports-screens";
public static final String TAG_USES_CONFIGURATION = "uses-configuration";
public static final String TAG_USES_FEATURE = "uses-feature";
public static final String TAG_USES_GL_TEXTURE = "uses-gl-texture";
@@ -196,8 +200,6 @@
public static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
public static final String TAG_USES_SDK = "uses-sdk";
public static final String TAG_USES_SPLIT = "uses-split";
- public static final String TAG_PROFILEABLE = "profileable";
- public static final String TAG_RECEIVER = "receiver";
public static final String METADATA_MAX_ASPECT_RATIO = "android.max_aspect";
public static final String METADATA_SUPPORTS_SIZE_CHANGES = "android.supports_size_changes";
@@ -1040,6 +1042,8 @@
return input.success(pkg);
case TAG_RESTRICT_UPDATE:
return parseRestrictUpdateHash(flags, input, pkg, res, parser);
+ case TAG_INSTALL_CONSTRAINTS:
+ return parseInstallConstraints(input, pkg, res, parser);
case TAG_QUERIES:
return parseQueries(input, pkg, res, parser);
default:
@@ -1729,6 +1733,12 @@
return input.success(pkg);
}
+ private static ParseResult<ParsingPackage> parseInstallConstraints(
+ ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws IOException, XmlPullParserException {
+ return InstallConstraintsTagParser.parseInstallConstraints(input, pkg, res, parser);
+ }
+
private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg,
Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException {
final int depth = parser.getDepth();
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 3b25f28..8d94324 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -586,7 +586,10 @@
}
// Cannot embed activity across TaskFragments for activity result.
- if (a.resultTo != null && a.resultTo.getTaskFragment() != this) {
+ // If the activity that started for result is finishing, it's likely that this start mode
+ // is used to place an activity in the same task. Since the finishing activity won't be
+ // able to get the results, so it's OK to embed in a different TaskFragment.
+ if (a.resultTo != null && !a.resultTo.finishing && a.resultTo.getTaskFragment() != this) {
return EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
deleted file mode 100644
index e30f3d2..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/pm/DeletePackageHelperTest.kt
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * 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.pm
-
-import android.content.pm.PackageManager
-import android.content.pm.UserInfo
-import android.os.Build
-import android.util.Log
-import com.android.server.testutils.any
-import com.android.server.testutils.spy
-import com.android.server.testutils.whenever
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.Mockito.doAnswer
-
-@RunWith(JUnit4::class)
-class DeletePackageHelperTest {
-
- @Rule
- @JvmField
- val rule = MockSystemRule()
-
- private lateinit var mPms: PackageManagerService
- private lateinit var mUserManagerInternal: UserManagerInternal
-
- @Before
- @Throws(Exception::class)
- fun setup() {
- Log.i("system.out", "setup", Exception())
- rule.system().stageNominalSystemState()
- rule.system().stageScanExistingPackage(
- "a.data.package", 1L, rule.system().dataAppDirectory)
-
- mUserManagerInternal = rule.mocks().injector.userManagerInternal
- whenever(mUserManagerInternal.getUserIds()).thenReturn(intArrayOf(0, 1))
-
- mPms = createPackageManagerService()
- doAnswer { false }.`when`(mPms).isPackageDeviceAdmin(any(), any())
- doAnswer { null }.`when`(mPms).freezePackageForDelete(any(), any(), any(), any())
- }
-
- private fun createPackageManagerService(): PackageManagerService {
- return spy(PackageManagerService(rule.mocks().injector,
- false /*coreOnly*/,
- false /*factoryTest*/,
- MockSystem.DEFAULT_VERSION_INFO.fingerprint,
- false /*isEngBuild*/,
- false /*isUserDebugBuild*/,
- Build.VERSION_CODES.CUR_DEVELOPMENT,
- Build.VERSION.INCREMENTAL))
- }
-
- @Test
- fun deleteSystemPackageFailsIfNotAdminAndNotProfile() {
- val ps = mPms.mSettings.getPackageLPr("a.data.package")
- whenever(PackageManagerServiceUtils.isSystemApp(ps)).thenReturn(true)
- whenever(mUserManagerInternal.getUserInfo(1)).thenReturn(UserInfo(1, "test", 0))
- whenever(mUserManagerInternal.getProfileParentId(1)).thenReturn(1)
-
- val dph = DeletePackageHelper(mPms)
- val result = dph.deletePackageX("a.data.package", 1L, 1,
- PackageManager.DELETE_SYSTEM_APP, false)
-
- assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_USER_RESTRICTED)
- }
-
- @Test
- fun deleteSystemPackageFailsIfProfileOfNonAdmin() {
- val userId = 1
- val parentId = 5
- val ps = mPms.mSettings.getPackageLPr("a.data.package")
- whenever(PackageManagerServiceUtils.isSystemApp(ps)).thenReturn(true)
- whenever(mUserManagerInternal.getUserInfo(userId)).thenReturn(
- UserInfo(userId, "test", UserInfo.FLAG_PROFILE))
- whenever(mUserManagerInternal.getProfileParentId(userId)).thenReturn(parentId)
- whenever(mUserManagerInternal.getUserInfo(parentId)).thenReturn(
- UserInfo(userId, "testparent", 0))
-
- val dph = DeletePackageHelper(mPms)
- val result = dph.deletePackageX("a.data.package", 1L, userId,
- PackageManager.DELETE_SYSTEM_APP, false)
-
- assertThat(result).isEqualTo(PackageManager.DELETE_FAILED_USER_RESTRICTED)
- }
-
- @Test
- fun deleteSystemPackageSucceedsIfAdmin() {
- val ps = mPms.mSettings.getPackageLPr("a.data.package")
- whenever(PackageManagerServiceUtils.isSystemApp(ps)).thenReturn(true)
- whenever(mUserManagerInternal.getUserInfo(1)).thenReturn(
- UserInfo(1, "test", UserInfo.FLAG_ADMIN))
-
- val dph = DeletePackageHelper(mPms)
- val result = dph.deletePackageX("a.data.package", 1L, 1,
- PackageManager.DELETE_SYSTEM_APP, false)
-
- assertThat(result).isEqualTo(PackageManager.DELETE_SUCCEEDED)
- }
-
- @Test
- fun deleteSystemPackageSucceedsIfProfileOfAdmin() {
- val userId = 1
- val parentId = 5
- val ps = mPms.mSettings.getPackageLPr("a.data.package")
- whenever(PackageManagerServiceUtils.isSystemApp(ps)).thenReturn(true)
- whenever(mUserManagerInternal.getUserInfo(userId)).thenReturn(
- UserInfo(userId, "test", UserInfo.FLAG_PROFILE))
- whenever(mUserManagerInternal.getProfileParentId(userId)).thenReturn(parentId)
- whenever(mUserManagerInternal.getUserInfo(parentId)).thenReturn(
- UserInfo(userId, "testparent", UserInfo.FLAG_ADMIN))
-
- val dph = DeletePackageHelper(mPms)
- val result = dph.deletePackageX("a.data.package", 1L, userId,
- PackageManager.DELETE_SYSTEM_APP, false)
-
- assertThat(result).isEqualTo(PackageManager.DELETE_SUCCEEDED)
- }
-}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index e9171c0c..92c7871 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -336,6 +336,59 @@
assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty();
}
+ /**
+ * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}.
+ */
+ @Test
+ public void readPermissions_installConstraints_successful() throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <install-constraints-allowed package=\"com.android.apex1\" />\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "install-constraints-allowlist.xml", contents);
+
+ readPermissions(folder, /* Grant all permission flags */ ~0);
+
+ assertThat(mSysConfig.getInstallConstraintsAllowlist())
+ .containsExactly("com.android.apex1");
+ }
+
+ /**
+ * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}.
+ */
+ @Test
+ public void readPermissions_installConstraints_noPackage() throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <install-constraints-allowed/>\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "install-constraints-allowlist.xml", contents);
+
+ readPermissions(folder, /* Grant all permission flags */ ~0);
+
+ assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty();
+ }
+
+ /**
+ * Tests that readPermissions works correctly for the tag {@code install-constraints-allowed}
+ * without {@link SystemConfig#ALLOW_VENDOR_APEX}.
+ */
+ @Test
+ public void readPermissions_installConstraints_noAppConfigs() throws IOException {
+ final String contents =
+ "<config>\n"
+ + " <install-constraints-allowed package=\"com.android.apex1\" />\n"
+ + "</config>";
+ final File folder = createTempSubfolder("folder");
+ createTempFile(folder, "install-constraints-allowlist.xml", contents);
+
+ readPermissions(folder, /* Grant all but ALLOW_APP_CONFIGS flag */ ~0x08);
+
+ assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty();
+ }
+
@Test
public void readApexPrivAppPermissions_addAllPermissions()
throws Exception {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
index 1096351..88eadfc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -32,6 +32,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityRecord.State.RESUMED;
+import static com.android.server.wm.TaskFragment.EMBEDDING_ALLOWED;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_MIN_DIMENSION_VIOLATION;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT;
import static com.android.server.wm.TaskFragment.EMBEDDING_DISALLOWED_UNTRUSTED_HOST;
@@ -468,6 +469,10 @@
newActivity.resultTo = activity;
assertEquals(EMBEDDING_DISALLOWED_NEW_TASK_FRAGMENT,
newTaskFragment.isAllowedToEmbedActivity(newActivity));
+
+ // Allow embedding if the resultTo activity is finishing.
+ activity.finishing = true;
+ assertEquals(EMBEDDING_ALLOWED, newTaskFragment.isAllowedToEmbedActivity(newActivity));
}
@Test
diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
index 41f9fae..6fc4b67 100644
--- a/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
+++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortHalInstance.java
@@ -31,15 +31,14 @@
public static UsbPortHal getInstance(UsbPortManager portManager, IndentingPrintWriter pw) {
logAndPrint(Log.DEBUG, null, "Querying USB HAL version");
- if (UsbPortHidl.isServicePresent(null)) {
- logAndPrint(Log.INFO, null, "USB HAL HIDL present");
- return new UsbPortHidl(portManager, pw);
- }
if (UsbPortAidl.isServicePresent(null)) {
logAndPrint(Log.INFO, null, "USB HAL AIDL present");
return new UsbPortAidl(portManager, pw);
}
-
+ if (UsbPortHidl.isServicePresent(null)) {
+ logAndPrint(Log.INFO, null, "USB HAL HIDL present");
+ return new UsbPortHidl(portManager, pw);
+ }
return null;
}
}
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index d432341..948b11b 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -477,6 +477,7 @@
manifest_action["compatible-screens"]["screen"];
manifest_action["supports-gl-texture"];
manifest_action["restrict-update"];
+ manifest_action["install-constraints"]["fingerprint-prefix"];
manifest_action["package-verifier"];
manifest_action["meta-data"] = meta_data_action;
manifest_action["uses-split"].Action(RequiredNameIsJavaPackage);