diff options
53 files changed, 1085 insertions, 449 deletions
diff --git a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl index a61bb3304c12..34d016adbc06 100644 --- a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl +++ b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl @@ -15,8 +15,8 @@ */ package android.hardware.camera2.extension; -import android.hardware.camera2.extension.Size; import android.hardware.camera2.extension.OutputConfigId; +import android.hardware.camera2.extension.Size; import android.view.Surface; /** @hide */ @@ -35,5 +35,5 @@ parcelable CameraOutputConfig OutputConfigId outputId; int surfaceGroupId; String physicalCameraId; - List<OutputConfigId> surfaceSharingOutputConfigs; + List<CameraOutputConfig> sharedSurfaceConfigs; } diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java index 5503e2834d98..c8dc2d0b0b91 100644 --- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java @@ -217,60 +217,30 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes CameraSessionConfig sessionConfig = mSessionProcessor.initSession(mCameraDevice.getId(), previewSurface, captureSurface); List<CameraOutputConfig> outputConfigs = sessionConfig.outputConfigs; - // map camera output ids to output configurations - HashMap<Integer, OutputConfiguration> cameraOutputs = new HashMap<>(); - for (CameraOutputConfig output : outputConfigs) { - OutputConfiguration cameraOutput = null; - switch(output.type) { - case CameraOutputConfig.TYPE_SURFACE: - if (output.surface == null) { - Log.w(TAG, "Unsupported client output id: " + output.outputId.id + - ", skipping!"); - continue; - } - cameraOutput = new OutputConfiguration(output.surfaceGroupId, - output.surface); - break; - case CameraOutputConfig.TYPE_IMAGEREADER: - if ((output.imageFormat == ImageFormat.UNKNOWN) || (output.size.width <= 0) || - (output.size.height <= 0)) { - Log.w(TAG, "Unsupported client output id: " + output.outputId.id + - ", skipping!"); - continue; - } - ImageReader reader = ImageReader.newInstance(output.size.width, - output.size.height, output.imageFormat, output.capacity); - mReaderMap.put(output.outputId.id, reader); - cameraOutput = new OutputConfiguration(output.surfaceGroupId, - reader.getSurface()); - break; - case CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER: - // Support for multi-resolution outputs to be added in future releases - default: - throw new IllegalArgumentException("Unsupported output config type: " + - output.type); - } - cameraOutput.setPhysicalCameraId(output.physicalCameraId); - cameraOutputs.put(output.outputId.id, cameraOutput); - } - ArrayList<OutputConfiguration> outputList = new ArrayList<>(); for (CameraOutputConfig output : outputConfigs) { - if (!cameraOutputs.containsKey(output.outputId.id)) { - // Shared surface already removed by a previous iteration + Surface outputSurface = initializeSurfrace(output); + if (outputSurface == null) { continue; } - OutputConfiguration outConfig = cameraOutputs.get(output.outputId.id); - if ((output.surfaceSharingOutputConfigs != null) && - !output.surfaceSharingOutputConfigs.isEmpty()) { - outConfig.enableSurfaceSharing(); - for (OutputConfigId outputId : output.surfaceSharingOutputConfigs) { - outConfig.addSurface(cameraOutputs.get(outputId.id).getSurface()); - cameraOutputs.remove(outputId.id); + OutputConfiguration cameraOutput = new OutputConfiguration(output.surfaceGroupId, + outputSurface); + + if ((output.sharedSurfaceConfigs != null) && !output.sharedSurfaceConfigs.isEmpty()) { + cameraOutput.enableSurfaceSharing(); + for (CameraOutputConfig sharedOutputConfig : output.sharedSurfaceConfigs) { + Surface sharedSurface = initializeSurfrace(sharedOutputConfig); + if (sharedSurface == null) { + continue; + } + cameraOutput.addSurface(sharedSurface); + mCameraConfigMap.put(sharedSurface, sharedOutputConfig); } } - outputList.add(outConfig); - mCameraConfigMap.put(outConfig.getSurface(), output); + + cameraOutput.setPhysicalCameraId(output.physicalCameraId); + outputList.add(cameraOutput); + mCameraConfigMap.put(cameraOutput.getSurface(), output); } SessionConfiguration sessionConfiguration = new SessionConfiguration( @@ -995,4 +965,32 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes CameraMetadataNative.update(ret.getNativeMetadata(), request.parameters); return ret; } + + private Surface initializeSurfrace(CameraOutputConfig output) { + switch(output.type) { + case CameraOutputConfig.TYPE_SURFACE: + if (output.surface == null) { + Log.w(TAG, "Unsupported client output id: " + output.outputId.id + + ", skipping!"); + return null; + } + return output.surface; + case CameraOutputConfig.TYPE_IMAGEREADER: + if ((output.imageFormat == ImageFormat.UNKNOWN) || (output.size.width <= 0) || + (output.size.height <= 0)) { + Log.w(TAG, "Unsupported client output id: " + output.outputId.id + + ", skipping!"); + return null; + } + ImageReader reader = ImageReader.newInstance(output.size.width, + output.size.height, output.imageFormat, output.capacity); + mReaderMap.put(output.outputId.id, reader); + return reader.getSurface(); + case CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER: + // Support for multi-resolution outputs to be added in future releases + default: + throw new IllegalArgumentException("Unsupported output config type: " + + output.type); + } + } } diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 731ea9207283..eadcac91dcd7 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -108,6 +108,17 @@ public final class DisplayManager { public static final String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION"; + /** + * Display category: All displays, including disabled displays. + * <p> + * This returns all displays, including currently disabled and inaccessible displays. + * + * @see #getDisplays(String) + * @hide + */ + public static final String DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED = + "android.hardware.display.category.ALL_INCLUDING_DISABLED"; + /** @hide **/ @IntDef(prefix = "VIRTUAL_DISPLAY_FLAG_", flag = true, value = { VIRTUAL_DISPLAY_FLAG_PUBLIC, @@ -552,7 +563,8 @@ public final class DisplayManager { final int[] displayIds = mGlobal.getDisplayIds(); synchronized (mLock) { try { - if (category == null) { + if (category == null + || DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) { addAllDisplaysLocked(mTempDisplays, displayIds); } else if (category.equals(DISPLAY_CATEGORY_PRESENTATION)) { addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI); diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index dcedb3083f76..7353bbccf77c 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -506,6 +506,14 @@ public final class AutofillManager { public static final String DEVICE_CONFIG_AUTOFILL_DIALOG_HINTS = "autofill_dialog_hints"; + /** + * Sets a value of delay time to show up the inline tooltip view. + * + * @hide + */ + public static final String DEVICE_CONFIG_AUTOFILL_TOOLTIP_SHOW_UP_DELAY = + "autofill_inline_tooltip_first_show_delay"; + private static final String DIALOG_HINTS_DELIMITER = ":"; /** @hide */ diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 468293862cd3..61c844ac8170 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -1002,6 +1002,7 @@ public class ChooserActivity extends ResolverActivity implements mMaxTargetsPerRow = getResources().getInteger(R.integer.config_chooser_max_targets_per_row); adjustPreviewWidth(newConfig.orientation, null); updateStickyContentPreview(); + updateTabPadding(); } private boolean shouldDisplayLandscape(int orientation) { @@ -1024,6 +1025,20 @@ public class ChooserActivity extends ResolverActivity implements updateLayoutWidth(R.id.content_preview_file_layout, width, parent); } + private void updateTabPadding() { + if (shouldShowTabs()) { + View tabs = findViewById(R.id.tabs); + float iconSize = getResources().getDimension(R.dimen.chooser_icon_size); + // The entire width consists of icons or padding. Divide the item padding in half to get + // paddingHorizontal. + float padding = (tabs.getWidth() - mMaxTargetsPerRow * iconSize) + / mMaxTargetsPerRow / 2; + // Subtract the margin the buttons already have. + padding -= getResources().getDimension(R.dimen.resolver_profile_tab_margin); + tabs.setPadding((int) padding, 0, (int) padding, 0); + } + } + private void updateLayoutWidth(int layoutResourceId, int width, View parent) { View view = parent.findViewById(layoutResourceId); if (view != null && view.getLayoutParams() != null) { @@ -2480,6 +2495,8 @@ public class ChooserActivity extends ResolverActivity implements recyclerView.setAdapter(gridAdapter); ((GridLayoutManager) recyclerView.getLayoutManager()).setSpanCount( mMaxTargetsPerRow); + + updateTabPadding(); } UserHandle currentUserHandle = mChooserMultiProfilePagerAdapter.getCurrentUserHandle(); diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java index d35d5ca6fca7..e6cc62472f5c 100644 --- a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java +++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java @@ -86,7 +86,11 @@ public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAd final LayoutInflater inflater = LayoutInflater.from(getContext()); final ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.chooser_list_per_profile, null, false); - return new ChooserProfileDescriptor(rootView, adapter); + ChooserProfileDescriptor profileDescriptor = + new ChooserProfileDescriptor(rootView, adapter); + profileDescriptor.recyclerView.setAccessibilityDelegateCompat( + new ChooserRecyclerViewAccessibilityDelegate(profileDescriptor.recyclerView)); + return profileDescriptor; } RecyclerView getListViewForIndex(int index) { diff --git a/core/java/com/android/internal/app/ChooserRecyclerViewAccessibilityDelegate.java b/core/java/com/android/internal/app/ChooserRecyclerViewAccessibilityDelegate.java new file mode 100644 index 000000000000..66c9838bf71b --- /dev/null +++ b/core/java/com/android/internal/app/ChooserRecyclerViewAccessibilityDelegate.java @@ -0,0 +1,92 @@ +/* + * 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.internal.app; + +import android.annotation.NonNull; +import android.graphics.Rect; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; + +import com.android.internal.widget.RecyclerView; +import com.android.internal.widget.RecyclerViewAccessibilityDelegate; + +class ChooserRecyclerViewAccessibilityDelegate extends RecyclerViewAccessibilityDelegate { + private final Rect mTempRect = new Rect(); + private final int[] mConsumed = new int[2]; + + ChooserRecyclerViewAccessibilityDelegate(RecyclerView recyclerView) { + super(recyclerView); + } + + @Override + public boolean onRequestSendAccessibilityEvent( + @NonNull ViewGroup host, + @NonNull View view, + @NonNull AccessibilityEvent event) { + boolean result = super.onRequestSendAccessibilityEvent(host, view, event); + if (result && event.getEventType() == AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) { + ensureViewOnScreenVisibility((RecyclerView) host, view); + } + return result; + } + + /** + * Bring the view that received accessibility focus on the screen. + * The method's logic is based on a model where RecyclerView is a child of another scrollable + * component (ResolverDrawerLayout) and can be partially scrolled off the screen. In that case, + * RecyclerView's children that are positioned fully within RecyclerView bounds but scrolled + * out of the screen by the outer component, when selected by the accessibility navigation will + * remain off the screen (as neither components detect such specific case). + * If the view that receiving accessibility focus is scrolled of the screen, perform the nested + * scrolling to make in visible. + */ + private void ensureViewOnScreenVisibility(RecyclerView recyclerView, View view) { + View child = recyclerView.findContainingItemView(view); + if (child == null) { + return; + } + recyclerView.getBoundsOnScreen(mTempRect, true); + int recyclerOnScreenTop = mTempRect.top; + int recyclerOnScreenBottom = mTempRect.bottom; + child.getBoundsOnScreen(mTempRect); + int dy = 0; + // if needed, do the page-length scroll instead of just a row-length scroll as + // ResolverDrawerLayout snaps to the compact view and the row-length scroll can be snapped + // back right away. + if (mTempRect.top < recyclerOnScreenTop) { + // snap to the bottom + dy = mTempRect.bottom - recyclerOnScreenBottom; + } else if (mTempRect.bottom > recyclerOnScreenBottom) { + // snap to the top + dy = mTempRect.top - recyclerOnScreenTop; + } + nestedVerticalScrollBy(recyclerView, dy); + } + + private void nestedVerticalScrollBy(RecyclerView recyclerView, int dy) { + if (dy == 0) { + return; + } + recyclerView.startNestedScroll(View.SCROLL_AXIS_VERTICAL); + if (recyclerView.dispatchNestedPreScroll(0, dy, mConsumed, null)) { + dy -= mConsumed[1]; + } + recyclerView.scrollBy(0, dy); + recyclerView.stopNestedScroll(); + } +} diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java index 5fe111148c91..ff188dc6f2b9 100644 --- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java +++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java @@ -27,6 +27,7 @@ import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Filter; import android.widget.Filterable; +import android.widget.LinearLayout; import android.widget.TextView; import com.android.internal.R; @@ -104,6 +105,13 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { @Override public int getItemViewType(int position) { if (!showHeaders()) { + LocaleStore.LocaleInfo item = (LocaleStore.LocaleInfo) getItem(position); + if (item.isSystemLocale()) { + return TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER; + } + if (item.isAppCurrentLocale()) { + return TYPE_CURRENT_LOCALE; + } return TYPE_LOCALE; } else { if (position == 0) { @@ -193,15 +201,11 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { } int itemType = getItemViewType(position); + View itemView = getNewViewIfNeeded(convertView, parent, itemType, position); switch (itemType) { case TYPE_HEADER_SUGGESTED: // intentional fallthrough case TYPE_HEADER_ALL_OTHERS: - // Covers both null, and "reusing" a wrong kind of view - if (!(convertView instanceof TextView)) { - convertView = mInflater.inflate(R.layout.language_picker_section_header, - parent, false); - } - TextView textView = (TextView) convertView; + TextView textView = (TextView) itemView; if (itemType == TYPE_HEADER_SUGGESTED) { setTextTo(textView, R.string.language_picker_section_suggested); } else { @@ -215,38 +219,77 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { mDisplayLocale != null ? mDisplayLocale : Locale.getDefault()); break; case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER: - if (!(convertView instanceof ViewGroup)) { - TextView title; - if (((LocaleStore.LocaleInfo)getItem(position)).isAppCurrentLocale()) { - convertView = mInflater.inflate( - R.layout.app_language_picker_current_locale_item, parent, false); - title = convertView.findViewById(R.id.language_picker_item); - addStateDescriptionIntoCurrentLocaleItem(convertView); - } else { - convertView = mInflater.inflate( + TextView title; + if (((LocaleStore.LocaleInfo)getItem(position)).isAppCurrentLocale()) { + title = itemView.findViewById(R.id.language_picker_item); + } else { + title = itemView.findViewById(R.id.locale); + } + title.setText(R.string.system_locale_title); + break; + case TYPE_CURRENT_LOCALE: + updateTextView(itemView, + itemView.findViewById(R.id.language_picker_item), position); + break; + default: + updateTextView(itemView, itemView.findViewById(R.id.locale), position); + break; + } + return itemView; + } + + /** Check if the old view can be reused, otherwise create a new one. */ + private View getNewViewIfNeeded( + View convertView, ViewGroup parent, int itemType, int position) { + View updatedView = convertView; + boolean shouldReuseView; + switch (itemType) { + case TYPE_HEADER_SUGGESTED: // intentional fallthrough + case TYPE_HEADER_ALL_OTHERS: + shouldReuseView = convertView instanceof TextView + && convertView.findViewById(R.id.language_picker_header) != null; + if (!shouldReuseView) { + updatedView = mInflater.inflate( + R.layout.language_picker_section_header, parent, false); + } + break; + case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER: + if (((LocaleStore.LocaleInfo) getItem(position)).isAppCurrentLocale()) { + shouldReuseView = convertView instanceof LinearLayout + && convertView.findViewById(R.id.language_picker_item) != null; + if (!shouldReuseView) { + updatedView = mInflater.inflate( + R.layout.app_language_picker_current_locale_item, + parent, false); + addStateDescriptionIntoCurrentLocaleItem(updatedView); + } + } else { + shouldReuseView = convertView instanceof TextView + && convertView.findViewById(R.id.locale) != null; + if (!shouldReuseView) { + updatedView = mInflater.inflate( R.layout.language_picker_item, parent, false); - title = convertView.findViewById(R.id.locale); } - title.setText(R.string.system_locale_title); } break; case TYPE_CURRENT_LOCALE: - if (!(convertView instanceof ViewGroup)) { - convertView = mInflater.inflate( + shouldReuseView = convertView instanceof LinearLayout + && convertView.findViewById(R.id.language_picker_item) != null; + if (!shouldReuseView) { + updatedView = mInflater.inflate( R.layout.app_language_picker_current_locale_item, parent, false); - addStateDescriptionIntoCurrentLocaleItem(convertView); + addStateDescriptionIntoCurrentLocaleItem(updatedView); } - updateTextView( - convertView, convertView.findViewById(R.id.language_picker_item), position); break; default: - // Covers both null, and "reusing" a wrong kind of view - if (!(convertView instanceof ViewGroup)) { - convertView = mInflater.inflate(R.layout.language_picker_item, parent, false); + shouldReuseView = convertView instanceof TextView + && convertView.findViewById(R.id.locale) != null; + if (!shouldReuseView) { + updatedView = mInflater.inflate(R.layout.language_picker_item, parent, false); } - updateTextView(convertView, convertView.findViewById(R.id.locale), position); + break; } - return convertView; + return updatedView; } private boolean showHeaders() { diff --git a/core/java/com/android/internal/view/inline/InlineTooltipUi.java b/core/java/com/android/internal/view/inline/InlineTooltipUi.java index 25fa678d0507..3eae89e350a0 100644 --- a/core/java/com/android/internal/view/inline/InlineTooltipUi.java +++ b/core/java/com/android/internal/view/inline/InlineTooltipUi.java @@ -15,24 +15,30 @@ */ package com.android.internal.view.inline; +import static android.view.autofill.AutofillManager.DEVICE_CONFIG_AUTOFILL_TOOLTIP_SHOW_UP_DELAY; import static android.view.autofill.Helper.sVerbose; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.content.ContextWrapper; +import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.provider.DeviceConfig; +import android.provider.Settings; import android.transition.Transition; import android.util.Slog; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; +import android.view.ViewParent; import android.view.WindowManager; import android.widget.LinearLayout; import android.widget.PopupWindow; import android.widget.inline.InlineContentView; import java.io.PrintWriter; +import java.lang.ref.WeakReference; /** * UI container for the inline suggestion tooltip. @@ -40,6 +46,8 @@ import java.io.PrintWriter; public final class InlineTooltipUi extends PopupWindow implements AutoCloseable { private static final String TAG = "InlineTooltipUi"; + private static final int FIRST_TIME_SHOW_DEFAULT_DELAY_MS = 250; + private final WindowManager mWm; private final ViewGroup mContentContainer; @@ -47,6 +55,16 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable private WindowManager.LayoutParams mWindowLayoutParams; + private DelayShowRunnable mDelayShowTooltip; + + private boolean mHasEverDetached; + + private boolean mDelayShowAtStart = true; + private boolean mDelaying = false; + private int mShowDelayConfigMs; + + private final Rect mTmpRect = new Rect(); + private final View.OnAttachStateChangeListener mAnchorOnAttachStateChangeListener = new View.OnAttachStateChangeListener() { @Override @@ -56,6 +74,7 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable @Override public void onViewDetachedFromWindow(View v) { + mHasEverDetached = true; dismiss(); } }; @@ -66,6 +85,13 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable @Override public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { + if (mHasEverDetached) { + // If the tooltip is ever detached, skip adjusting the position, + // because it only accepts to attach once and does not show again + // after detaching. + return; + } + if (mHeight != bottom - top) { mHeight = bottom - top; adjustPosition(); @@ -77,6 +103,13 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable mContentContainer = new LinearLayout(new ContextWrapper(context)); mWm = context.getSystemService(WindowManager.class); + // That's a default delay time, and it will scale via the value of + // Settings.Global.ANIMATOR_DURATION_SCALE + mShowDelayConfigMs = DeviceConfig.getInt( + DeviceConfig.NAMESPACE_AUTOFILL, + DEVICE_CONFIG_AUTOFILL_TOOLTIP_SHOW_UP_DELAY, + FIRST_TIME_SHOW_DEFAULT_DELAY_MS); + setTouchModal(false); setOutsideTouchable(true); setInputMethodMode(INPUT_METHOD_NOT_NEEDED); @@ -95,7 +128,7 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable @Override public void close() { - hide(); + dismiss(); } @Override @@ -117,14 +150,57 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable * The effective {@code update} method that should be called by its clients. */ public void update(View anchor) { + if (anchor == null) { + final View oldAnchor = getAnchor(); + if (oldAnchor != null) { + removeDelayShowTooltip(oldAnchor); + } + return; + } + + if (mDelayShowAtStart) { + // To avoid showing when the anchor is doing the fade in animation. That will + // cause the tooltip to show in the wrong position and jump at the start. + mDelayShowAtStart = false; + mDelaying = true; + + if (mDelayShowTooltip == null) { + mDelayShowTooltip = new DelayShowRunnable(anchor); + } + + int delayTimeMs = mShowDelayConfigMs; + try { + final float scale = Settings.Global.getFloat( + anchor.getContext().getContentResolver(), + Settings.Global.ANIMATOR_DURATION_SCALE); + delayTimeMs *= scale; + } catch (Settings.SettingNotFoundException e) { + // do nothing + } + anchor.postDelayed(mDelayShowTooltip, delayTimeMs); + } else if (!mDelaying) { + // Note: If we are going to reuse the tooltip, we need to take care the delay in + // the case that update for the new anchor. + updateInner(anchor); + } + } + + private void removeDelayShowTooltip(View anchor) { + if (mDelayShowTooltip != null) { + anchor.removeCallbacks(mDelayShowTooltip); + mDelayShowTooltip = null; + } + } + + private void updateInner(View anchor) { + if (mHasEverDetached) { + return; + } // set to the application type with the highest z-order setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL); - // The first time to show up, the height of tooltip is zero, - // so set the offset Y to 2 * anchor height. - final int achoredHeight = mContentContainer.getHeight(); - final int offsetY = (achoredHeight == 0) - ? -anchor.getHeight() << 1 : -anchor.getHeight() - achoredHeight; + final int offsetY = -anchor.getHeight() - getPreferHeight(anchor); + if (!isShowing()) { setWidth(WindowManager.LayoutParams.WRAP_CONTENT); setHeight(WindowManager.LayoutParams.WRAP_CONTENT); @@ -135,6 +211,34 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable } } + private int getPreferHeight(View anchor) { + // The first time to show up, the height of tooltip is zero, so make its height + // the same as anchor. + final int achoredHeight = mContentContainer.getHeight(); + return (achoredHeight == 0) ? anchor.getHeight() : achoredHeight; + } + + @Override + protected boolean findDropDownPosition(View anchor, WindowManager.LayoutParams outParams, + int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) { + boolean isAbove = super.findDropDownPosition(anchor, outParams, xOffset, yOffset, width, + height, gravity, allowScroll); + // Make the tooltips y fo position is above or under the parent of the anchor, + // otherwise suggestions doesn't clickable. + ViewParent parent = anchor.getParent(); + if (parent instanceof View) { + final Rect r = mTmpRect; + ((View) parent).getGlobalVisibleRect(r); + if (isAbove) { + outParams.y = r.top - getPreferHeight(anchor); + } else { + outParams.y = r.bottom + 1; + } + } + + return isAbove; + } + @Override protected void update(View anchor, WindowManager.LayoutParams params) { // update content view for the anchor is scrolling @@ -175,7 +279,9 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable final View anchor = getAnchor(); if (anchor != null) { anchor.removeOnAttachStateChangeListener(mAnchorOnAttachStateChangeListener); + removeDelayShowTooltip(anchor); } + mHasEverDetached = true; super.detachFromAnchor(); } @@ -185,7 +291,6 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable return; } - setShowing(false); setTransitioningToDismiss(true); hide(); @@ -193,6 +298,7 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable if (getOnDismissListener() != null) { getOnDismissListener().onDismiss(); } + super.dismiss(); } private void adjustPosition() { @@ -202,15 +308,15 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable } private void show(WindowManager.LayoutParams params) { - if (sVerbose) { - Slog.v(TAG, "show()"); - } mWindowLayoutParams = params; try { params.packageName = "android"; params.setTitle("Autofill Inline Tooltip"); // Title is set for debugging purposes if (!mShowing) { + if (sVerbose) { + Slog.v(TAG, "show()"); + } params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; params.privateFlags |= @@ -232,11 +338,11 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable } private void hide() { - if (sVerbose) { - Slog.v(TAG, "hide()"); - } try { if (mShowing) { + if (sVerbose) { + Slog.v(TAG, "hide()"); + } mContentContainer.removeOnLayoutChangeListener(mAnchoredOnLayoutChangeListener); mWm.removeView(mContentContainer); mShowing = false; @@ -336,4 +442,26 @@ public final class InlineTooltipUi extends PopupWindow implements AutoCloseable } } } + + private class DelayShowRunnable implements Runnable { + WeakReference<View> mAnchor; + + DelayShowRunnable(View anchor) { + mAnchor = new WeakReference<>(anchor); + } + + @Override + public void run() { + mDelaying = false; + final View anchor = mAnchor.get(); + if (anchor != null) { + updateInner(anchor); + } + } + + public void setAnchor(View anchor) { + mAnchor.clear(); + mAnchor = new WeakReference<>(anchor); + } + } } diff --git a/core/java/com/android/internal/widget/MessagingImageMessage.java b/core/java/com/android/internal/widget/MessagingImageMessage.java index f7955c3f72da..8e7fe18b222b 100644 --- a/core/java/com/android/internal/widget/MessagingImageMessage.java +++ b/core/java/com/android/internal/widget/MessagingImageMessage.java @@ -226,6 +226,13 @@ public class MessagingImageMessage extends ImageView implements MessagingMessage @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + if (mDrawable == null) { + Log.e(TAG, "onMeasure() after recycle()!"); + setMeasuredDimension(0, 0); + return; + } + if (mIsIsolated) { // When isolated we have a fixed size, let's use that sizing. setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml index 6e3a11af27a7..b102b4bb2124 100644 --- a/core/res/res/layout/chooser_grid.xml +++ b/core/res/res/layout/chooser_grid.xml @@ -41,7 +41,7 @@ android:src="@drawable/ic_drag_handle" android:layout_marginTop="@dimen/chooser_edge_margin_thin" android:layout_marginBottom="@dimen/chooser_edge_margin_thin" - android:tint="@color/lighter_gray" + android:tint="?attr/colorSurfaceVariant" android:layout_centerHorizontal="true" android:layout_alignParentTop="true" /> diff --git a/core/res/res/layout/language_picker_section_header.xml b/core/res/res/layout/language_picker_section_header.xml index 4fa4d9b043c6..58042f9a42f8 100644 --- a/core/res/res/layout/language_picker_section_header.xml +++ b/core/res/res/layout/language_picker_section_header.xml @@ -24,4 +24,5 @@ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:textColor="?android:attr/colorAccent" android:textStyle="bold" + android:id="@+id/language_picker_header" tools:text="@string/language_picker_section_all"/> diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml index 8480ec37a79e..6a200d05c2d7 100644 --- a/core/res/res/layout/resolver_list.xml +++ b/core/res/res/layout/resolver_list.xml @@ -90,11 +90,13 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> + <!-- horizontal padding = 8dp content padding - 4dp margin that tab buttons have. --> <TabWidget android:id="@android:id/tabs" android:layout_width="match_parent" android:layout_height="wrap_content" android:tabStripEnabled="false" + android:paddingHorizontal="4dp" android:visibility="gone" /> <FrameLayout android:id="@android:id/tabcontent" diff --git a/core/res/res/layout/resolver_profile_tab_button.xml b/core/res/res/layout/resolver_profile_tab_button.xml index 936c8e23b87a..fd168e6414f1 100644 --- a/core/res/res/layout/resolver_profile_tab_button.xml +++ b/core/res/res/layout/resolver_profile_tab_button.xml @@ -21,7 +21,7 @@ android:layout_height="36dp" android:layout_weight="1" android:layout_marginVertical="6dp" - android:layout_marginHorizontal="4dp" + android:layout_marginHorizontal="@dimen/resolver_profile_tab_margin" android:background="@drawable/resolver_profile_tab_bg" android:textColor="@color/resolver_profile_tab_text" android:textSize="@dimen/resolver_tab_text_size" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 16f14c9de606..0844dfd1112a 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3000,6 +3000,10 @@ </string-array> + <!-- Whether to show a notification informing users about notification permission settings + upon upgrade to T from a pre-T version --> + <bool name="config_notificationReviewPermissions">false</bool> + <!-- Default Gravity setting for the system Toast view. Equivalent to: Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM --> <integer name="config_toastDefaultGravity">0x00000051</integer> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 43ca4f098ce7..bb36ededc581 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -962,6 +962,7 @@ <dimen name="resolver_title_padding_bottom">0dp</dimen> <dimen name="resolver_empty_state_container_padding_top">48dp</dimen> <dimen name="resolver_empty_state_container_padding_bottom">8dp</dimen> + <dimen name="resolver_profile_tab_margin">4dp</dimen> <dimen name="chooser_action_button_icon_size">18dp</dimen> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e111ee184be1..96e8de0d2d64 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2266,6 +2266,7 @@ <java-symbol type="integer" name="config_screenshotChordKeyTimeout" /> <java-symbol type="integer" name="config_maxResolverActivityColumns" /> <java-symbol type="array" name="config_notificationSignalExtractors" /> + <java-symbol type="bool" name="config_notificationReviewPermissions" /> <java-symbol type="layout" name="notification_material_action" /> <java-symbol type="layout" name="notification_material_action_list" /> @@ -4315,6 +4316,7 @@ <java-symbol type="dimen" name="resolver_title_padding_bottom" /> <java-symbol type="dimen" name="resolver_empty_state_container_padding_top" /> <java-symbol type="dimen" name="resolver_empty_state_container_padding_bottom" /> + <java-symbol type="dimen" name="resolver_profile_tab_margin" /> <java-symbol type="string" name="config_deviceSpecificDisplayAreaPolicyProvider" /> @@ -4789,6 +4791,7 @@ <java-symbol type="layout" name="app_language_picker_current_locale_item" /> <java-symbol type="id" name="system_locale_subtitle" /> <java-symbol type="id" name="language_picker_item" /> + <java-symbol type="id" name="language_picker_header" /> <java-symbol type="dimen" name="status_bar_height_default" /> </resources> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java index de30dbbe7e46..484294ab295b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java @@ -160,6 +160,15 @@ public class SplitDecorManager extends WindowlessWindowManager { mBounds.set(newBounds); } + final boolean show = + newBounds.width() > mBounds.width() || newBounds.height() > mBounds.height(); + final boolean animate = show != mShown; + if (animate && mFadeAnimator != null && mFadeAnimator.isRunning()) { + // If we need to animate and animator still running, cancel it before we ensure both + // background and icon surfaces are non null for next animation. + mFadeAnimator.cancel(); + } + if (mBackgroundLeash == null) { mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash, RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession); @@ -183,11 +192,7 @@ public class SplitDecorManager extends WindowlessWindowManager { newBounds.width() / 2 - mIconSize / 2, newBounds.height() / 2 - mIconSize / 2); - boolean show = newBounds.width() > mBounds.width() || newBounds.height() > mBounds.height(); - if (show != mShown) { - if (mFadeAnimator != null && mFadeAnimator.isRunning()) { - mFadeAnimator.cancel(); - } + if (animate) { startFadeAnimation(show, false /* isResized */); mShown = show; } diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java index 345f8a3c15d6..3714c15af173 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java @@ -635,12 +635,13 @@ public class ClipboardOverlayController { } private Intent getRemoteCopyIntent(ClipData clipData) { + Intent nearbyIntent = new Intent(REMOTE_COPY_ACTION); + String remoteCopyPackage = mContext.getString(R.string.config_remoteCopyPackage); - if (TextUtils.isEmpty(remoteCopyPackage)) { - return null; + if (!TextUtils.isEmpty(remoteCopyPackage)) { + nearbyIntent.setComponent(ComponentName.unflattenFromString(remoteCopyPackage)); } - Intent nearbyIntent = new Intent(REMOTE_COPY_ACTION); - nearbyIntent.setComponent(ComponentName.unflattenFromString(remoteCopyPackage)); + nearbyIntent.setClipData(clipData); nearbyIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt index de2b5c9a4739..8bfb8aae5c91 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt @@ -199,14 +199,6 @@ class MediaHost constructor( } } - override var squishFraction: Float = 1.0f - set(value) { - if (!value.equals(field)) { - field = value - changedListener?.invoke() - } - } - override var showsOnlyActiveMedia: Boolean = false set(value) { if (!value.equals(field)) { @@ -257,7 +249,6 @@ class MediaHost constructor( override fun copy(): MediaHostState { val mediaHostState = MediaHostStateHolder() mediaHostState.expansion = expansion - mediaHostState.squishFraction = squishFraction mediaHostState.showsOnlyActiveMedia = showsOnlyActiveMedia mediaHostState.measurementInput = measurementInput?.copy() mediaHostState.visible = visible @@ -276,9 +267,6 @@ class MediaHost constructor( if (expansion != other.expansion) { return false } - if (squishFraction != other.squishFraction) { - return false - } if (showsOnlyActiveMedia != other.showsOnlyActiveMedia) { return false } @@ -297,7 +285,6 @@ class MediaHost constructor( override fun hashCode(): Int { var result = measurementInput?.hashCode() ?: 0 result = 31 * result + expansion.hashCode() - result = 31 * result + squishFraction.hashCode() result = 31 * result + falsingProtectionNeeded.hashCode() result = 31 * result + showsOnlyActiveMedia.hashCode() result = 31 * result + if (visible) 1 else 2 @@ -338,11 +325,6 @@ interface MediaHostState { var expansion: Float /** - * Fraction of the height animation. - */ - var squishFraction: Float - - /** * Is this host only showing active media or is it showing all of them including resumption? */ var showsOnlyActiveMedia: Boolean diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt index ae62355e6768..deb5cbafccc4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt @@ -18,7 +18,6 @@ package com.android.systemui.media import android.content.Context import android.content.res.Configuration -import androidx.annotation.VisibleForTesting import androidx.constraintlayout.widget.ConstraintSet import com.android.systemui.R import com.android.systemui.statusbar.policy.ConfigurationController @@ -277,79 +276,53 @@ class MediaViewController @Inject constructor( } /** - * Apply squishFraction to a copy of viewState such that the cached version is untouched. - */ - @VisibleForTesting - internal fun squishViewState( - viewState: TransitionViewState, - squishFraction: Float - ): TransitionViewState { - val squishedViewState = viewState.copy() - squishedViewState.height = (squishedViewState.height * squishFraction).toInt() - val albumArtViewState = squishedViewState.widgetStates.get(R.id.album_art) - if (albumArtViewState != null) { - albumArtViewState.height = squishedViewState.height - } - return squishedViewState - } - - /** * Obtain a new viewState for a given media state. This usually returns a cached state, but if * it's not available, it will recreate one by measuring, which may be expensive. */ - @VisibleForTesting - public fun obtainViewState(state: MediaHostState?): TransitionViewState? { + private fun obtainViewState(state: MediaHostState?): TransitionViewState? { if (state == null || state.measurementInput == null) { return null } // Only a subset of the state is relevant to get a valid viewState. Let's get the cachekey var cacheKey = getKey(state, isGutsVisible, tmpKey) val viewState = viewStates[cacheKey] - if (viewState != null) { // we already have cached this measurement, let's continue - if (state.squishFraction < 1f) { - return squishViewState(viewState, state.squishFraction) - } return viewState } // Copy the key since this might call recursively into it and we're using tmpKey cacheKey = cacheKey.copy() val result: TransitionViewState? - if (transitionLayout == null) { - return null - } - // Not cached. Let's create a new measurement - if (state.expansion == 0.0f || state.expansion == 1.0f) { - result = transitionLayout!!.calculateViewState( - state.measurementInput!!, - constraintSetForExpansion(state.expansion), - TransitionViewState()) - // We don't want to cache interpolated or null states as this could quickly fill up - // our cache. We only cache the start and the end states since the interpolation - // is cheap - setGutsViewState(result) - viewStates[cacheKey] = result - logger.logMediaSize("measured new viewState", result.width, result.height) + if (transitionLayout != null) { + // Let's create a new measurement + if (state.expansion == 0.0f || state.expansion == 1.0f) { + result = transitionLayout!!.calculateViewState( + state.measurementInput!!, + constraintSetForExpansion(state.expansion), + TransitionViewState()) + + setGutsViewState(result) + // We don't want to cache interpolated or null states as this could quickly fill up + // our cache. We only cache the start and the end states since the interpolation + // is cheap + viewStates[cacheKey] = result + } else { + // This is an interpolated state + val startState = state.copy().also { it.expansion = 0.0f } + + // Given that we have a measurement and a view, let's get (guaranteed) viewstates + // from the start and end state and interpolate them + val startViewState = obtainViewState(startState) as TransitionViewState + val endState = state.copy().also { it.expansion = 1.0f } + val endViewState = obtainViewState(endState) as TransitionViewState + result = layoutController.getInterpolatedState( + startViewState, + endViewState, + state.expansion) + } } else { - // This is an interpolated state - val startState = state.copy().also { it.expansion = 0.0f } - - // Given that we have a measurement and a view, let's get (guaranteed) viewstates - // from the start and end state and interpolate them - val startViewState = obtainViewState(startState) as TransitionViewState - val endState = state.copy().also { it.expansion = 1.0f } - - val endViewState = obtainViewState(endState) as TransitionViewState - result = layoutController.getInterpolatedState( - startViewState, - endViewState, - state.expansion) - logger.logMediaSize("interpolated viewState", result.width, result.height) - } - if (state.squishFraction < 1f) { - return squishViewState(result, state.squishFraction) + result = null } return result } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 6b579e8bb118..d03a2e55d628 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -624,7 +624,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca if (mQSAnimator != null) { mQSAnimator.setPosition(expansion); } - mQqsMediaHost.setSquishFraction(mSquishinessFraction); updateMediaPositions(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 4d325e154a4f..ba57d57d0fd3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -191,7 +191,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private final boolean mDebugRemoveAnimation; private int mContentHeight; - private int mIntrinsicContentHeight; + private float mIntrinsicContentHeight; private int mCollapsedSize; private int mPaddingBetweenElements; private int mMaxTopPadding; @@ -802,7 +802,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "mAmbientState.getStackY() + mContentHeight = " + y); - y = (int) mAmbientState.getStackY() + mIntrinsicContentHeight; + y = (int) (mAmbientState.getStackY() + mIntrinsicContentHeight); drawDebugInfo(canvas, y, Color.YELLOW, /* label= */ "mAmbientState.getStackY() + mIntrinsicContentHeight = " + y); @@ -1371,7 +1371,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable */ @ShadeViewRefactor(RefactorComponent.COORDINATOR) public void setExpandedHeight(float height) { + final float shadeBottom = getHeight() - getEmptyBottomMargin(); final boolean skipHeightUpdate = shouldSkipHeightUpdate(); + if (!skipHeightUpdate) { + final float expansionFraction = MathUtils.saturate(height / shadeBottom); + mAmbientState.setExpansionFraction(expansionFraction); + } updateStackPosition(); if (!skipHeightUpdate) { @@ -1473,7 +1478,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable */ @ShadeViewRefactor(RefactorComponent.COORDINATOR) public int getIntrinsicContentHeight() { - return mIntrinsicContentHeight; + return (int) mIntrinsicContentHeight; } @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) @@ -2287,7 +2292,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private void updateContentHeight() { final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mMinimumPaddings; final int shelfIntrinsicHeight = mShelf != null ? mShelf.getIntrinsicHeight() : 0; - final int height = + final float height = (int) scrimTopPadding + (int) mNotificationStackSizeCalculator.computeHeight( /* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications, shelfIntrinsicHeight); @@ -2295,7 +2300,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable // The topPadding can be bigger than the regular padding when qs is expanded, in that // state the maxPanelHeight and the contentHeight should be bigger - mContentHeight = height + Math.max(mIntrinsicPadding, mTopPadding) + mBottomPadding; + mContentHeight = (int) (height + Math.max(mIntrinsicPadding, mTopPadding) + mBottomPadding); updateScrollability(); clampScrollPosition(); updateStackPosition(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt index d05c3385e982..6287857e7be9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt @@ -37,6 +37,7 @@ import kotlin.properties.Delegates.notNull private const val TAG = "NotifStackSizeCalc" private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG) +private val SPEW = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE) /** Calculates number of notifications to display and the height of the notification stack. */ @SysUISingleton @@ -87,9 +88,10 @@ constructor( // Could be < 0 if the space available is less than the shelf size. Returns 0 in this case. maxNotifications = max(0, maxNotifications) log { + val sequence = if (SPEW) " stackHeightSequence=${stackHeightSequence.toList()}" else "" "computeMaxKeyguardNotifications(" + "availableSpace=$totalAvailableSpace" + - " shelfHeight=$shelfIntrinsicHeight) -> $maxNotifications" + " shelfHeight=$shelfIntrinsicHeight) -> $maxNotifications$sequence" } return maxNotifications } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java index 799fee5e865d..4013254c6592 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java @@ -53,9 +53,9 @@ public class StackScrollAlgorithm { private static final Boolean DEBUG = false; private final ViewGroup mHostView; - private int mPaddingBetweenElements; - private int mGapHeight; - private int mGapHeightOnLockscreen; + private float mPaddingBetweenElements; + private float mGapHeight; + private float mGapHeightOnLockscreen; private int mCollapsedSize; private StackScrollAlgorithmState mTempAlgorithmState = new StackScrollAlgorithmState(); @@ -127,13 +127,13 @@ public class StackScrollAlgorithm { return getExpansionFractionWithoutShelf(mTempAlgorithmState, ambientState); } - private void log(String s) { + public static void log(String s) { if (DEBUG) { android.util.Log.i(TAG, s); } } - public void logView(View view, String s) { + public static void logView(View view, String s) { String viewString = ""; if (view instanceof ExpandableNotificationRow) { ExpandableNotificationRow row = ((ExpandableNotificationRow) view); @@ -535,30 +535,23 @@ public class StackScrollAlgorithm { // more notifications than we should during this special transitional states. boolean bypassPulseNotExpanding = ambientState.isBypassEnabled() && ambientState.isOnKeyguard() && !ambientState.isPulseExpanding(); - final int stackBottom = - !ambientState.isShadeExpanded() || ambientState.isDozing() - || bypassPulseNotExpanding + final float stackBottom = !ambientState.isShadeExpanded() + || ambientState.getDozeAmount() == 1f + || bypassPulseNotExpanding ? ambientState.getInnerHeight() - : (int) ambientState.getStackHeight(); - final int shelfStart = stackBottom + : ambientState.getStackHeight(); + final float shelfStart = stackBottom - ambientState.getShelf().getIntrinsicHeight() - mPaddingBetweenElements; - viewState.yTranslation = Math.min(viewState.yTranslation, shelfStart); - if (viewState.yTranslation >= shelfStart) { - viewState.hidden = !view.isExpandAnimationRunning() - && !view.hasExpandingChild(); - viewState.inShelf = true; - // Notifications in the shelf cannot be visible HUNs. - viewState.headsUpIsVisible = false; - } + updateViewWithShelf(view, viewState, shelfStart); } } // Clip height of view right before shelf. viewState.height = (int) (getMaxAllowedChildHeight(view) * expansionFraction); } - algorithmState.mCurrentYPosition += viewState.height - + expansionFraction * mPaddingBetweenElements; + algorithmState.mCurrentYPosition += + expansionFraction * (getMaxAllowedChildHeight(view) + mPaddingBetweenElements); algorithmState.mCurrentExpandedYPosition += view.getIntrinsicHeight() + mPaddingBetweenElements; @@ -566,6 +559,18 @@ public class StackScrollAlgorithm { viewState.yTranslation += ambientState.getStackY(); } + @VisibleForTesting + void updateViewWithShelf(ExpandableView view, ExpandableViewState viewState, float shelfStart) { + viewState.yTranslation = Math.min(viewState.yTranslation, shelfStart); + if (viewState.yTranslation >= shelfStart) { + viewState.hidden = !view.isExpandAnimationRunning() + && !view.hasExpandingChild(); + viewState.inShelf = true; + // Notifications in the shelf cannot be visible HUNs. + viewState.headsUpIsVisible = false; + } + } + /** * Get the gap height needed for before a view * @@ -849,13 +854,13 @@ public class StackScrollAlgorithm { * Y position of the current view during updating children * with expansion factor applied. */ - private int mCurrentYPosition; + private float mCurrentYPosition; /** * Y position of the current view during updating children * without applying the expansion factor. */ - private int mCurrentExpandedYPosition; + private float mCurrentExpandedYPosition; } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 5c12671726f4..8203987cd7d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -107,7 +107,6 @@ import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; import android.widget.DateTimeView; -import android.window.SplashScreen; import androidx.annotation.NonNull; import androidx.lifecycle.Lifecycle; @@ -3508,11 +3507,6 @@ public class CentralSurfacesImpl extends CoreStartable implements @Override public void onTrackingStopped(boolean expand) { - if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) { - if (!expand && !mKeyguardStateController.canDismissLockScreen()) { - mStatusBarKeyguardViewManager.showBouncer(false /* scrimmed */); - } - } } // TODO: Figure out way to remove these. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 4a912572b7ed..9afdfd651130 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -192,6 +192,7 @@ import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.unfold.SysUIUnfoldComponent; +import com.android.systemui.util.Compile; import com.android.systemui.util.LargeScreenUtils; import com.android.systemui.util.ListenerSet; import com.android.systemui.util.Utils; @@ -216,7 +217,9 @@ import javax.inject.Provider; @CentralSurfacesComponent.CentralSurfacesScope public class NotificationPanelViewController extends PanelViewController { - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + private static final boolean DEBUG_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG); + private static final boolean SPEW_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE); + private static final boolean DEBUG_DRAWABLE = false; /** * The parallax amount of the quick settings translation when dragging down the panel @@ -816,7 +819,7 @@ public class NotificationPanelViewController extends PanelViewController { mSettingsChangeObserver = new SettingsChangeObserver(handler); mSplitShadeEnabled = LargeScreenUtils.shouldUseSplitNotificationShade(mResources); - mView.setWillNotDraw(!DEBUG); + mView.setWillNotDraw(!DEBUG_DRAWABLE); mLargeScreenShadeHeaderController = largeScreenShadeHeaderController; mLayoutInflater = layoutInflater; mFeatureFlags = featureFlags; @@ -890,7 +893,7 @@ public class NotificationPanelViewController extends PanelViewController { mView.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener()); - if (DEBUG) { + if (DEBUG_DRAWABLE) { mView.getOverlay().add(new DebugDrawable()); } @@ -1189,7 +1192,7 @@ public class NotificationPanelViewController extends PanelViewController { } private void reInflateViews() { - if (DEBUG) Log.d(TAG, "reInflateViews"); + if (DEBUG_LOGCAT) Log.d(TAG, "reInflateViews"); // Re-inflate the status view group. KeyguardStatusView keyguardStatusView = mNotificationContainerParent.findViewById(R.id.keyguard_status_view); @@ -1293,6 +1296,8 @@ public class NotificationPanelViewController extends PanelViewController { private void updateMaxDisplayedNotifications(boolean recompute) { if (recompute) { mMaxAllowedKeyguardNotifications = Math.max(computeMaxKeyguardNotifications(), 1); + } else { + if (SPEW_LOGCAT) Log.d(TAG, "Skipping computeMaxKeyguardNotifications() by request"); } if (mKeyguardShowing && !mKeyguardBypassController.getBypassEnabled()) { @@ -1545,6 +1550,19 @@ public class NotificationPanelViewController extends PanelViewController { mNotificationStackScrollLayoutController.getHeight() - staticTopPadding - bottomPadding; + + if (SPEW_LOGCAT) { + Log.d(TAG, "getSpaceForLockscreenNotifications()" + + " availableSpace=" + availableSpace + + " NSSL.height=" + mNotificationStackScrollLayoutController.getHeight() + + " NSSL.top=" + mNotificationStackScrollLayoutController.getTop() + + " staticTopPadding=" + staticTopPadding + + " bottomPadding=" + bottomPadding + + " lockIconPadding=" + lockIconPadding + + " mIndicationBottomPadding=" + mIndicationBottomPadding + + " mAmbientIndicationBottomPadding=" + mAmbientIndicationBottomPadding + ); + } return availableSpace; } @@ -1553,7 +1571,12 @@ public class NotificationPanelViewController extends PanelViewController { */ @VisibleForTesting int computeMaxKeyguardNotifications() { - if (mAmbientState.getFractionToShade() > 0 || mAmbientState.getDozeAmount() > 0) { + if (mAmbientState.getFractionToShade() > 0) { + if (SPEW_LOGCAT) { + Log.v(TAG, "Internally skipping computeMaxKeyguardNotifications()" + + " fractionToShade=" + mAmbientState.getFractionToShade() + ); + } return mMaxAllowedKeyguardNotifications; } @@ -1743,7 +1766,7 @@ public class NotificationPanelViewController extends PanelViewController { } private boolean onQsIntercept(MotionEvent event) { - if (DEBUG) Log.d(TAG, "onQsIntercept"); + if (DEBUG_LOGCAT) Log.d(TAG, "onQsIntercept"); int pointerIndex = event.findPointerIndex(mTrackingPointer); if (pointerIndex < 0) { pointerIndex = 0; @@ -1798,7 +1821,7 @@ public class NotificationPanelViewController extends PanelViewController { if ((h > getTouchSlop(event) || (h < -getTouchSlop(event) && mQsExpanded)) && Math.abs(h) > Math.abs(x - mInitialTouchX) && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) { - if (DEBUG) Log.d(TAG, "onQsIntercept - start tracking expansion"); + if (DEBUG_LOGCAT) Log.d(TAG, "onQsIntercept - start tracking expansion"); mView.getParent().requestDisallowInterceptTouchEvent(true); mQsTracking = true; traceQsJank(true /* startTracing */, false /* wasCancelled */); @@ -2075,7 +2098,7 @@ public class NotificationPanelViewController extends PanelViewController { private void handleQsDown(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN && shouldQuickSettingsIntercept( event.getX(), event.getY(), -1)) { - if (DEBUG) Log.d(TAG, "handleQsDown"); + if (DEBUG_LOGCAT) Log.d(TAG, "handleQsDown"); mFalsingCollector.onQsDown(); mQsTracking = true; onQsExpansionStarted(); @@ -2189,7 +2212,7 @@ public class NotificationPanelViewController extends PanelViewController { break; case MotionEvent.ACTION_MOVE: - if (DEBUG) Log.d(TAG, "onQSTouch move"); + if (DEBUG_LOGCAT) Log.d(TAG, "onQSTouch move"); setQsExpansion(h + mInitialHeightOnTouch); if (h >= getFalsingThreshold()) { mQsTouchAboveFalsingThreshold = true; @@ -2341,7 +2364,7 @@ public class NotificationPanelViewController extends PanelViewController { mCentralSurfaces.executeRunnableDismissingKeyguard(null, null /* cancelAction */, false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */); } - if (DEBUG) { + if (DEBUG_DRAWABLE) { mView.invalidate(); } } @@ -2998,7 +3021,7 @@ public class NotificationPanelViewController extends PanelViewController { // This is a circular dependency and should be avoided, otherwise we'll have // a stack overflow. if (mStackScrollerMeasuringPass > 2) { - if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting."); + if (DEBUG_LOGCAT) Log.d(TAG, "Unstable notification panel height. Aborting."); } else { positionClockAndNotifications(); } @@ -3032,7 +3055,7 @@ public class NotificationPanelViewController extends PanelViewController { updateNotificationTranslucency(); updatePanelExpanded(); updateGestureExclusionRect(); - if (DEBUG) { + if (DEBUG_DRAWABLE) { mView.invalidate(); } } @@ -3057,7 +3080,9 @@ public class NotificationPanelViewController extends PanelViewController { } private int calculatePanelHeightShade() { - final int maxHeight = mNotificationStackScrollLayoutController.getHeight(); + int emptyBottomMargin = mNotificationStackScrollLayoutController.getEmptyBottomMargin(); + int maxHeight = mNotificationStackScrollLayoutController.getHeight() - emptyBottomMargin; + if (mBarState == KEYGUARD) { int minKeyguardPanelBottom = mClockPositionAlgorithm.getLockscreenStatusViewHeight() + mNotificationStackScrollLayoutController.getIntrinsicContentHeight(); @@ -3675,7 +3700,7 @@ public class NotificationPanelViewController extends PanelViewController { public void onQsPanelScrollChanged(int scrollY) { mLargeScreenShadeHeaderController.setQsScrollY(scrollY); if (scrollY > 0 && !mQsFullyExpanded) { - if (DEBUG) Log.d(TAG, "Scrolling while not expanded. Forcing expand"); + if (DEBUG_LOGCAT) Log.d(TAG, "Scrolling while not expanded. Forcing expand"); // If we are scrolling QS, we should be fully expanded. expandWithQs(); } @@ -4070,7 +4095,7 @@ public class NotificationPanelViewController extends PanelViewController { } public void setHeaderDebugInfo(String text) { - if (DEBUG) mHeaderDebugInfo = text; + if (DEBUG_DRAWABLE) mHeaderDebugInfo = text; } public void onThemeChanged() { @@ -4112,7 +4137,7 @@ public class NotificationPanelViewController extends PanelViewController { } if (!isFullyCollapsed() && onQsIntercept(event)) { - if (DEBUG) Log.d(TAG, "onQsIntercept true"); + if (DEBUG_LOGCAT) Log.d(TAG, "onQsIntercept true"); return true; } return super.onInterceptTouchEvent(event); @@ -4183,7 +4208,7 @@ public class NotificationPanelViewController extends PanelViewController { handled |= mHeadsUpTouchHelper.onTouchEvent(event); if (!mHeadsUpTouchHelper.isTrackingHeadsUp() && handleQsTouch(event)) { - if (DEBUG) Log.d(TAG, "handleQsTouch true"); + if (DEBUG_LOGCAT) Log.d(TAG, "handleQsTouch true"); return true; } if (event.getActionMasked() == MotionEvent.ACTION_DOWN && isFullyCollapsed()) { @@ -4623,7 +4648,7 @@ public class NotificationPanelViewController extends PanelViewController { private class ConfigurationListener implements ConfigurationController.ConfigurationListener { @Override public void onThemeChanged() { - if (DEBUG) Log.d(TAG, "onThemeChanged"); + if (DEBUG_LOGCAT) Log.d(TAG, "onThemeChanged"); mThemeResId = mView.getContext().getThemeResId(); reInflateViews(); } @@ -4631,7 +4656,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onSmallestScreenWidthChanged() { Trace.beginSection("onSmallestScreenWidthChanged"); - if (DEBUG) Log.d(TAG, "onSmallestScreenWidthChanged"); + if (DEBUG_LOGCAT) Log.d(TAG, "onSmallestScreenWidthChanged"); // Can affect multi-user switcher visibility as it depends on screen size by default: // it is enabled only for devices with large screens (see config_keyguardUserSwitcher) @@ -4648,7 +4673,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onDensityOrFontScaleChanged() { - if (DEBUG) Log.d(TAG, "onDensityOrFontScaleChanged"); + if (DEBUG_LOGCAT) Log.d(TAG, "onDensityOrFontScaleChanged"); reInflateViews(); } } @@ -4661,7 +4686,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onChange(boolean selfChange) { - if (DEBUG) Log.d(TAG, "onSettingsChanged"); + if (DEBUG_LOGCAT) Log.d(TAG, "onSettingsChanged"); // Can affect multi-user switcher visibility reInflateViews(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 7ab944dc013e..82ca842d48c9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -806,7 +806,6 @@ public abstract class PanelViewController { mExpansionDragDownAmountPx = h; mExpandedFraction = Math.min(1f, maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight); - mAmbientState.setExpansionFraction(mExpandedFraction); onHeightUpdated(mExpandedHeight); updatePanelExpansionAndVisibility(); }); 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 124647b81d4a..61e123a8e42a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -480,12 +480,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mBouncer == null) { return; } + mBouncer.hide(destroyView); if (mShowing) { // If we were showing the bouncer and then aborting, we need to also clear out any // potential actions unless we actually unlocked. cancelPostAuthActions(); } - mBouncer.hide(destroyView); cancelPendingWakeupAction(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt deleted file mode 100644 index 18178097d5e3..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaViewControllerTest.kt +++ /dev/null @@ -1,86 +0,0 @@ -package com.android.systemui.media - -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper -import android.view.View -import androidx.test.filters.SmallTest -import com.android.systemui.R -import com.android.systemui.SysuiTestCase -import com.android.systemui.util.animation.MeasurementInput -import com.android.systemui.util.animation.TransitionLayout -import com.android.systemui.util.animation.TransitionViewState -import com.android.systemui.util.animation.WidgetState -import junit.framework.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.times -import org.mockito.Mockito.verify -import org.mockito.Mockito.verifyNoMoreInteractions -import org.mockito.MockitoAnnotations -import org.mockito.Mockito.`when` as whenever - -/** - * Tests for {@link MediaViewController}. - */ -@SmallTest -@RunWith(AndroidTestingRunner::class) -@TestableLooper.RunWithLooper -class MediaViewControllerTest : SysuiTestCase() { - @Mock - private lateinit var logger: MediaViewLogger - - private val configurationController = - com.android.systemui.statusbar.phone.ConfigurationControllerImpl(context) - private val mediaHostStatesManager = MediaHostStatesManager() - private lateinit var mediaViewController: MediaViewController - private val mediaHostStateHolder = MediaHost.MediaHostStateHolder() - private var transitionLayout = TransitionLayout(context, /* attrs */ null, /* defStyleAttr */ 0) - @Mock private lateinit var mockViewState: TransitionViewState - @Mock private lateinit var mockCopiedState: TransitionViewState - @Mock private lateinit var mockWidgetState: WidgetState - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - mediaViewController = MediaViewController( - context, - configurationController, - mediaHostStatesManager, - logger - ) - mediaViewController.attach(transitionLayout, MediaViewController.TYPE.PLAYER) - } - - @Test - fun testObtainViewState_applySquishFraction_toTransitionViewState_height() { - transitionLayout.measureState = TransitionViewState().apply { - this.height = 100 - } - mediaHostStateHolder.expansion = 1f - val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY) - val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY) - mediaHostStateHolder.measurementInput = - MeasurementInput(widthMeasureSpec, heightMeasureSpec) - - // Test no squish - mediaHostStateHolder.squishFraction = 1f - assertTrue(mediaViewController.obtainViewState(mediaHostStateHolder)!!.height == 100) - - // Test half squish - mediaHostStateHolder.squishFraction = 0.5f - assertTrue(mediaViewController.obtainViewState(mediaHostStateHolder)!!.height == 50) - } - - @Test - fun testSquish_DoesNotMutateViewState() { - whenever(mockViewState.copy()).thenReturn(mockCopiedState) - whenever(mockCopiedState.widgetStates) - .thenReturn(mutableMapOf(R.id.album_art to mockWidgetState)) - - mediaViewController.squishViewState(mockViewState, 0.5f) - verify(mockViewState, times(1)).copy() - verifyNoMoreInteractions(mockViewState) - } -}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt index 87ca1aa5eeb7..668f7526891a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt @@ -7,10 +7,13 @@ import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.EmptyShadeView import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow +import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.BypassController import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.SectionProvider import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.google.common.truth.Truth.assertThat +import junit.framework.Assert.assertFalse +import junit.framework.Assert.assertTrue import org.junit.Before import org.junit.Test import org.mockito.Mockito.mock @@ -110,4 +113,52 @@ class StackScrollAlgorithmTest : SysuiTestCase() { /* fractionToShade= */ 0f, /* onKeyguard= */ false) assertThat(gap).isEqualTo(bigGap) } + + @Test + fun updateViewWithShelf_viewAboveShelf_viewShown() { + val viewStart = 0f + val shelfStart = 1f + + val expandableView = mock(ExpandableView::class.java) + whenever(expandableView.isExpandAnimationRunning).thenReturn(false) + whenever(expandableView.hasExpandingChild()).thenReturn(false) + + val expandableViewState = ExpandableViewState() + expandableViewState.yTranslation = viewStart + + stackScrollAlgorithm.updateViewWithShelf(expandableView, expandableViewState, shelfStart); + assertFalse(expandableViewState.hidden) + } + + @Test + fun updateViewWithShelf_viewBelowShelf_viewHidden() { + val shelfStart = 0f + val viewStart = 1f + + val expandableView = mock(ExpandableView::class.java) + whenever(expandableView.isExpandAnimationRunning).thenReturn(false) + whenever(expandableView.hasExpandingChild()).thenReturn(false) + + val expandableViewState = ExpandableViewState() + expandableViewState.yTranslation = viewStart + + stackScrollAlgorithm.updateViewWithShelf(expandableView, expandableViewState, shelfStart); + assertTrue(expandableViewState.hidden) + } + + @Test + fun updateViewWithShelf_viewBelowShelfButIsExpanding_viewShown() { + val shelfStart = 0f + val viewStart = 1f + + val expandableView = mock(ExpandableView::class.java) + whenever(expandableView.isExpandAnimationRunning).thenReturn(true) + whenever(expandableView.hasExpandingChild()).thenReturn(true) + + val expandableViewState = ExpandableViewState() + expandableViewState.yTranslation = viewStart + + stackScrollAlgorithm.updateViewWithShelf(expandableView, expandableViewState, shelfStart); + assertFalse(expandableViewState.hidden) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java index fa9161a19cb1..f599e3b12c57 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java @@ -578,19 +578,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { } @Test - public void computeMaxKeyguardNotifications_dozeAmountNotZero_returnsExistingMax() { - when(mAmbientState.getDozeAmount()).thenReturn(0.5f); - mNotificationPanelViewController.setMaxDisplayedNotifications(-1); - - // computeMaxKeyguardNotifications sets maxAllowed to 0 at minimum if it updates the value - assertThat(mNotificationPanelViewController.computeMaxKeyguardNotifications()) - .isEqualTo(-1); - } - - @Test public void computeMaxKeyguardNotifications_noTransition_updatesMax() { when(mAmbientState.getFractionToShade()).thenReturn(0f); - when(mAmbientState.getDozeAmount()).thenReturn(0f); mNotificationPanelViewController.setMaxDisplayedNotifications(-1); // computeMaxKeyguardNotifications sets maxAllowed to 0 at minimum if it updates the value diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 38e018b42985..4f2abf263a9b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -312,6 +312,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mStatusBarKeyguardViewManager.dismissWithAction( action, cancelAction, true /* afterKeyguardGone */); + when(mBouncer.isShowing()).thenReturn(false); mStatusBarKeyguardViewManager.hideBouncer(true); mStatusBarKeyguardViewManager.hide(0, 30); verify(action, never()).onDismiss(); @@ -319,6 +320,20 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test + public void testHidingBouncer_cancelsGoneRunnable() { + OnDismissAction action = mock(OnDismissAction.class); + Runnable cancelAction = mock(Runnable.class); + mStatusBarKeyguardViewManager.dismissWithAction( + action, cancelAction, true /* afterKeyguardGone */); + + when(mBouncer.isShowing()).thenReturn(false); + mStatusBarKeyguardViewManager.hideBouncer(true); + + verify(action, never()).onDismiss(); + verify(cancelAction).run(); + } + + @Test public void testHiding_doesntCancelWhenShowing() { OnDismissAction action = mock(OnDismissAction.class); Runnable cancelAction = mock(Runnable.class); diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java index e27b7a659ae6..09a05bb6b40e 100644 --- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java +++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java @@ -1135,41 +1135,13 @@ public class CameraExtensionsProxyService extends Service { CameraSessionConfig ret = new CameraSessionConfig(); ret.outputConfigs = new ArrayList<>(); for (Camera2OutputConfigImpl output : outputConfigs) { - CameraOutputConfig entry = new CameraOutputConfig(); - entry.outputId = new OutputConfigId(); - entry.outputId.id = output.getId(); - entry.physicalCameraId = output.getPhysicalCameraId(); - entry.surfaceGroupId = output.getSurfaceGroupId(); - if (output instanceof SurfaceOutputConfigImpl) { - SurfaceOutputConfigImpl surfaceConfig = (SurfaceOutputConfigImpl) output; - entry.type = CameraOutputConfig.TYPE_SURFACE; - entry.surface = surfaceConfig.getSurface(); - } else if (output instanceof ImageReaderOutputConfigImpl) { - ImageReaderOutputConfigImpl imageReaderOutputConfig = - (ImageReaderOutputConfigImpl) output; - entry.type = CameraOutputConfig.TYPE_IMAGEREADER; - entry.size = new android.hardware.camera2.extension.Size(); - entry.size.width = imageReaderOutputConfig.getSize().getWidth(); - entry.size.height = imageReaderOutputConfig.getSize().getHeight(); - entry.imageFormat = imageReaderOutputConfig.getImageFormat(); - entry.capacity = imageReaderOutputConfig.getMaxImages(); - } else if (output instanceof MultiResolutionImageReaderOutputConfigImpl) { - MultiResolutionImageReaderOutputConfigImpl multiResReaderConfig = - (MultiResolutionImageReaderOutputConfigImpl) output; - entry.type = CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER; - entry.imageFormat = multiResReaderConfig.getImageFormat(); - entry.capacity = multiResReaderConfig.getMaxImages(); - } else { - throw new IllegalStateException("Unknown output config type!"); - } + CameraOutputConfig entry = getCameraOutputConfig(output); List<Camera2OutputConfigImpl> sharedOutputs = output.getSurfaceSharingOutputConfigs(); if ((sharedOutputs != null) && (!sharedOutputs.isEmpty())) { - entry.surfaceSharingOutputConfigs = new ArrayList<>(); + entry.sharedSurfaceConfigs = new ArrayList<>(); for (Camera2OutputConfigImpl sharedOutput : sharedOutputs) { - OutputConfigId outputId = new OutputConfigId(); - outputId.id = sharedOutput.getId(); - entry.surfaceSharingOutputConfigs.add(outputId); + entry.sharedSurfaceConfigs.add(getCameraOutputConfig(sharedOutput)); } } ret.outputConfigs.add(entry); @@ -1854,4 +1826,36 @@ public class CameraExtensionsProxyService extends Service { } } } + + private static CameraOutputConfig getCameraOutputConfig(Camera2OutputConfigImpl output) { + CameraOutputConfig ret = new CameraOutputConfig(); + ret.outputId = new OutputConfigId(); + ret.outputId.id = output.getId(); + ret.physicalCameraId = output.getPhysicalCameraId(); + ret.surfaceGroupId = output.getSurfaceGroupId(); + if (output instanceof SurfaceOutputConfigImpl) { + SurfaceOutputConfigImpl surfaceConfig = (SurfaceOutputConfigImpl) output; + ret.type = CameraOutputConfig.TYPE_SURFACE; + ret.surface = surfaceConfig.getSurface(); + } else if (output instanceof ImageReaderOutputConfigImpl) { + ImageReaderOutputConfigImpl imageReaderOutputConfig = + (ImageReaderOutputConfigImpl) output; + ret.type = CameraOutputConfig.TYPE_IMAGEREADER; + ret.size = new android.hardware.camera2.extension.Size(); + ret.size.width = imageReaderOutputConfig.getSize().getWidth(); + ret.size.height = imageReaderOutputConfig.getSize().getHeight(); + ret.imageFormat = imageReaderOutputConfig.getImageFormat(); + ret.capacity = imageReaderOutputConfig.getMaxImages(); + } else if (output instanceof MultiResolutionImageReaderOutputConfigImpl) { + MultiResolutionImageReaderOutputConfigImpl multiResReaderConfig = + (MultiResolutionImageReaderOutputConfigImpl) output; + ret.type = CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER; + ret.imageFormat = multiResReaderConfig.getImageFormat(); + ret.capacity = multiResReaderConfig.getMaxImages(); + } else { + throw new IllegalStateException("Unknown output config type!"); + } + + return ret; + } } diff --git a/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java b/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java index 9e8b2228195e..26a295ef9253 100644 --- a/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java +++ b/services/autofill/java/com/android/server/autofill/ui/CustomScrollView.java @@ -56,39 +56,41 @@ public class CustomScrollView extends ScrollView { private int mWidth = -1; private int mHeight = -1; - private int mMaxPortraitBodyHeightPercent = 20; - private int mMaxLandscapeBodyHeightPercent = 20; + private int mMaxPortraitBodyHeightPercent; + private int mMaxLandscapeBodyHeightPercent; + private int mAttrBasedMaxHeightPercent; public CustomScrollView(Context context) { super(context); - setMaxBodyHeightPercent(); + setMaxBodyHeightPercent(context); } public CustomScrollView(Context context, AttributeSet attrs) { super(context, attrs); - setMaxBodyHeightPercent(); + setMaxBodyHeightPercent(context); } public CustomScrollView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - setMaxBodyHeightPercent(); + setMaxBodyHeightPercent(context); } public CustomScrollView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - setMaxBodyHeightPercent(); + setMaxBodyHeightPercent(context); } - private void setMaxBodyHeightPercent() { + private void setMaxBodyHeightPercent(Context context) { + mAttrBasedMaxHeightPercent = getAttrBasedMaxHeightPercent(context); mMaxPortraitBodyHeightPercent = DeviceConfig.getInt( DeviceConfig.NAMESPACE_AUTOFILL, DEVICE_CONFIG_SAVE_DIALOG_PORTRAIT_BODY_HEIGHT_MAX_PERCENT, - mMaxPortraitBodyHeightPercent); + mAttrBasedMaxHeightPercent); mMaxLandscapeBodyHeightPercent = DeviceConfig.getInt( DeviceConfig.NAMESPACE_AUTOFILL, DEVICE_CONFIG_SAVE_DIALOG_LANDSCAPE_BODY_HEIGHT_MAX_PERCENT, - mMaxLandscapeBodyHeightPercent); + mAttrBasedMaxHeightPercent); } @Override @@ -117,29 +119,27 @@ public class CustomScrollView extends ScrollView { final int contentHeight = content.getMeasuredHeight(); int displayHeight = point.y; - int configBasedMaxHeight = (getResources().getConfiguration().orientation + int maxHeight = (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) ? (int) (mMaxLandscapeBodyHeightPercent * displayHeight / 100) : (int) (mMaxPortraitBodyHeightPercent * displayHeight / 100); - mHeight = configBasedMaxHeight > 0 - ? Math.min(contentHeight, configBasedMaxHeight) - : Math.min(contentHeight, getAttrBasedMaxHeight(context, displayHeight)); + mHeight = Math.min(contentHeight, maxHeight); if (sDebug) { Slog.d(TAG, "calculateDimensions():" + " mMaxPortraitBodyHeightPercent=" + mMaxPortraitBodyHeightPercent + ", mMaxLandscapeBodyHeightPercent=" + mMaxLandscapeBodyHeightPercent - + ", configBasedMaxHeight=" + configBasedMaxHeight - + ", attrBasedMaxHeight=" + getAttrBasedMaxHeight(context, displayHeight) + + ", mAttrBasedMaxHeightPercent=" + mAttrBasedMaxHeightPercent + + ", maxHeight=" + maxHeight + ", contentHeight=" + contentHeight + ", w=" + mWidth + ", h=" + mHeight); } } - private int getAttrBasedMaxHeight(Context context, int displayHeight) { + private int getAttrBasedMaxHeightPercent(Context context) { final TypedValue maxHeightAttrTypedValue = new TypedValue(); context.getTheme().resolveAttribute(R.attr.autofillSaveCustomSubtitleMaxHeight, maxHeightAttrTypedValue, true); - return (int) maxHeightAttrTypedValue.getFraction(displayHeight, displayHeight); + return (int) maxHeightAttrTypedValue.getFraction(100, 100); } } diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java index 2a83a3c431ec..6eb8e2619cb5 100644 --- a/services/companion/java/com/android/server/companion/CompanionApplicationController.java +++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java @@ -104,10 +104,10 @@ class CompanionApplicationController { } void bindCompanionApplication(@UserIdInt int userId, @NonNull String packageName, - boolean bindImportant) { + boolean isSelfManaged) { if (DEBUG) { Log.i(TAG, "bind() u" + userId + "/" + packageName - + " important=" + bindImportant); + + " isSelfManaged=" + isSelfManaged); } final List<ComponentName> companionServices = @@ -130,7 +130,7 @@ class CompanionApplicationController { serviceConnectors = CollectionUtils.map(companionServices, componentName -> CompanionDeviceServiceConnector.newInstance(mContext, userId, - componentName, bindImportant)); + componentName, isSelfManaged)); mBoundCompanionApplications.setValueForPackage(userId, packageName, serviceConnectors); } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java index a6bd48056e12..9bb1fb9ac9f5 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java @@ -17,7 +17,7 @@ package com.android.server.companion; import static android.content.Context.BIND_ALMOST_PERCEPTIBLE; -import static android.content.Context.BIND_IMPORTANT; +import static android.content.Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE; import static android.os.Process.THREAD_PRIORITY_DEFAULT; import android.annotation.NonNull; @@ -61,21 +61,22 @@ class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDe /** * Create a CompanionDeviceServiceConnector instance. * - * When bindImportant is false, the binding flag will be BIND_ALMOST_PERCEPTIBLE + * For self-managed apps, the binding flag will be BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE + * (oom_score_adj = VISIBLE_APP_ADJ = 100). + * + * For non self-managed apps, the binding flag will be BIND_ALMOST_PERCEPTIBLE * (oom_score_adj = PERCEPTIBLE_MEDIUM_APP = 225). The target service will be treated * as important as a perceptible app (IMPORTANCE_VISIBLE = 200), and will be unbound when * the app is removed from task manager. - * When bindImportant is true, the binding flag will be BIND_IMPORTANT - * (oom_score_adj = PERCEPTIBLE_MEDIUM_APP = -700). The target service will - * have the highest priority to avoid being killed (IMPORTANCE_FOREGROUND = 100). * * One time permission's importance level to keep session alive is * IMPORTANCE_FOREGROUND_SERVICE = 125. In order to kill the one time permission session, the * service importance level should be higher than 125. */ static CompanionDeviceServiceConnector newInstance(@NonNull Context context, - @UserIdInt int userId, @NonNull ComponentName componentName, boolean bindImportant) { - final int bindingFlags = bindImportant ? BIND_IMPORTANT : BIND_ALMOST_PERCEPTIBLE; + @UserIdInt int userId, @NonNull ComponentName componentName, boolean isSelfManaged) { + final int bindingFlags = isSelfManaged ? BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE + : BIND_ALMOST_PERCEPTIBLE; return new CompanionDeviceServiceConnector(context, userId, componentName, bindingFlags); } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 089a92402de5..254a3226e6de 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -125,6 +125,7 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.internal.net.VpnProfile; +import com.android.modules.utils.build.SdkLevel; import com.android.net.module.util.NetdUtils; import com.android.net.module.util.NetworkStackConstants; import com.android.server.DeviceIdleInternal; @@ -213,7 +214,9 @@ public class Vpn { private final Context mUserIdContext; @VisibleForTesting final Dependencies mDeps; private final NetworkInfo mNetworkInfo; + @GuardedBy("this") private int mLegacyState; + @GuardedBy("this") @VisibleForTesting protected String mPackage; private int mOwnerUID; private boolean mIsPackageTargetingAtLeastQ; @@ -251,6 +254,7 @@ public class Vpn { * Whether to keep the connection active after rebooting, or upgrading or reinstalling. This * only applies to {@link VpnService} connections. */ + @GuardedBy("this") @VisibleForTesting protected boolean mAlwaysOn = false; /** @@ -258,6 +262,7 @@ public class Vpn { * apps can still bypass by choosing explicit networks. Has no effect if {@link mAlwaysOn} is * not set. Applies to all types of VPNs. */ + @GuardedBy("this") @VisibleForTesting protected boolean mLockdown = false; /** @@ -610,7 +615,7 @@ public class Vpn { } /** Returns the package name that is currently prepared. */ - public String getPackage() { + public synchronized String getPackage() { return mPackage; } @@ -691,6 +696,36 @@ public class Vpn { return true; } + private boolean sendEventToVpnManagerApp(@NonNull String category, int errorClass, + int errorCode, @NonNull final String packageName, @Nullable final String sessionKey, + @NonNull final VpnProfileState profileState, @Nullable final Network underlyingNetwork, + @Nullable final NetworkCapabilities nc, @Nullable final LinkProperties lp) { + final Intent intent = new Intent(VpnManager.ACTION_VPN_MANAGER_EVENT); + intent.setPackage(packageName); + intent.addCategory(category); + intent.putExtra(VpnManager.EXTRA_VPN_PROFILE_STATE, profileState); + intent.putExtra(VpnManager.EXTRA_SESSION_KEY, sessionKey); + intent.putExtra(VpnManager.EXTRA_UNDERLYING_NETWORK, underlyingNetwork); + intent.putExtra(VpnManager.EXTRA_UNDERLYING_NETWORK_CAPABILITIES, nc); + intent.putExtra(VpnManager.EXTRA_UNDERLYING_LINK_PROPERTIES, lp); + intent.putExtra(VpnManager.EXTRA_TIMESTAMP_MILLIS, System.currentTimeMillis()); + if (!VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER.equals(category) + || !VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED.equals(category)) { + intent.putExtra(VpnManager.EXTRA_ERROR_CLASS, errorClass); + intent.putExtra(VpnManager.EXTRA_ERROR_CODE, errorCode); + } + try { + return mUserIdContext.startService(intent) != null; + } catch (RuntimeException e) { + Log.e(TAG, "Service of VpnManager app " + intent + " failed to start", e); + return false; + } + } + + private boolean isVpnApp(String packageName) { + return packageName != null && !VpnConfig.LEGACY_VPN.equals(packageName); + } + /** * Configures an always-on VPN connection through a specific application. This connection is * automatically granted and persisted after a reboot. @@ -713,9 +748,40 @@ public class Vpn { boolean lockdown, @Nullable List<String> lockdownAllowlist) { enforceControlPermissionOrInternalCaller(); + // Store mPackage since it might be reset or might be replaced with the other VPN app. + final String oldPackage = mPackage; + final boolean isPackageChanged = !Objects.equals(packageName, oldPackage); + // TODO: Remove "SdkLevel.isAtLeastT()" check once VpnManagerService is decoupled from + // ConnectivityServiceTest. + // Only notify VPN apps that were already always-on, and only if the always-on provider + // changed, or the lockdown mode changed. + final boolean shouldNotifyOldPkg = isVpnApp(oldPackage) && mAlwaysOn + && (lockdown != mLockdown || isPackageChanged); + // Also notify the new package if there was a provider change. + final boolean shouldNotifyNewPkg = isVpnApp(packageName) && isPackageChanged; if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownAllowlist)) { saveAlwaysOnPackage(); + // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from + // ConnectivityServiceTest. + if (shouldNotifyOldPkg && SdkLevel.isAtLeastT()) { + // If both of shouldNotifyOldPkg & isPackageChanged are true, which means the + // always-on of old package is disabled or the old package is replaced with the new + // package. In this case, VpnProfileState should be disconnected. + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, + -1 /* errorClass */, -1 /* errorCode*/, oldPackage, + null /* sessionKey */, isPackageChanged ? makeDisconnectedVpnProfileState() + : makeVpnProfileStateLocked(), + null /* underlyingNetwork */, null /* nc */, null /* lp */); + } + // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from + // ConnectivityServiceTest. + if (shouldNotifyNewPkg && SdkLevel.isAtLeastT()) { + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, + -1 /* errorClass */, -1 /* errorCode*/, packageName, + getSessionKeyLocked(), makeVpnProfileStateLocked(), + null /* underlyingNetwork */, null /* nc */, null /* lp */); + } return true; } return false; @@ -1012,6 +1078,7 @@ public class Vpn { return true; } + @GuardedBy("this") private boolean isCurrentPreparedPackage(String packageName) { // We can't just check that packageName matches mPackage, because if the app was uninstalled // and reinstalled it will no longer be prepared. Similarly if there is a shared UID, the @@ -1049,6 +1116,17 @@ public class Vpn { if (!VpnConfig.LEGACY_VPN.equals(mPackage)) { mAppOpsManager.finishOp( AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER, mOwnerUID, mPackage, null); + // The underlying network, NetworkCapabilities and LinkProperties are not + // necessary to send to VPN app since the purpose of this event is to notify + // VPN app that VPN is deactivated by the user. + // TODO(b/230548427): Remove SDK check once VPN related stuff are decoupled from + // ConnectivityServiceTest. + if (SdkLevel.isAtLeastT()) { + sendEventToVpnManagerApp(VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER, + -1 /* errorClass */, -1 /* errorCode*/, mPackage, + getSessionKeyLocked(), makeVpnProfileStateLocked(), + null /* underlyingNetwork */, null /* nc */, null /* lp */); + } } // cleanupVpnStateLocked() is called from mVpnRunner.exit() mVpnRunner.exit(); @@ -1305,6 +1383,7 @@ public class Vpn { return true; } + @GuardedBy("this") private void agentConnect() { LinkProperties lp = makeLinkProperties(); @@ -2005,6 +2084,7 @@ public class Vpn { return isIkev2VpnRunner() ? VpnManager.TYPE_VPN_PLATFORM : VpnManager.TYPE_VPN_LEGACY; } + @GuardedBy("this") private void updateAlwaysOnNotification(DetailedState networkState) { final boolean visible = (mAlwaysOn && networkState != DetailedState.CONNECTED); @@ -3593,11 +3673,19 @@ public class Vpn { } } - private VpnProfileState makeVpnProfileState() { + @GuardedBy("this") + @NonNull + private VpnProfileState makeVpnProfileStateLocked() { return new VpnProfileState(getStateFromLegacyState(mLegacyState), isIkev2VpnRunner() ? getSessionKeyLocked() : null, mAlwaysOn, mLockdown); } + @NonNull + private VpnProfileState makeDisconnectedVpnProfileState() { + return new VpnProfileState(VpnProfileState.STATE_DISCONNECTED, null /* sessionKey */, + false /* alwaysOn */, false /* lockdown */); + } + /** * Retrieve the VpnProfileState for the profile provisioned by the given package. * @@ -3609,7 +3697,7 @@ public class Vpn { @NonNull String packageName) { requireNonNull(packageName, "No package name provided"); enforceNotRestrictedUser(); - return isCurrentIkev2VpnLocked(packageName) ? makeVpnProfileState() : null; + return isCurrentIkev2VpnLocked(packageName) ? makeVpnProfileStateLocked() : null; } /** diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index aa30c0897d60..a25ac210f9c8 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -152,6 +152,9 @@ import javax.xml.datatype.DatatypeConfigurationException; * <screenBrightnessRampSlowDecrease>0.03</screenBrightnessRampSlowDecrease> * <screenBrightnessRampSlowIncrease>0.04</screenBrightnessRampSlowIncrease> * + * <screenBrightnessRampIncreaseMaxMillis>2000</screenBrightnessRampIncreaseMaxMillis> + * <screenBrightnessRampDecreaseMaxMillis>3000</screenBrightnessRampDecreaseMaxMillis> + * * <lightSensor> * <type>android.sensor.light</type> * <name>1234 Ambient Light Sensor</name> @@ -259,6 +262,8 @@ public class DisplayDeviceConfig { private float mBrightnessRampFastIncrease = Float.NaN; private float mBrightnessRampSlowDecrease = Float.NaN; private float mBrightnessRampSlowIncrease = Float.NaN; + private long mBrightnessRampDecreaseMaxMillis = 0; + private long mBrightnessRampIncreaseMaxMillis = 0; private int mAmbientHorizonLong = AMBIENT_LIGHT_LONG_HORIZON_MILLIS; private int mAmbientHorizonShort = AMBIENT_LIGHT_SHORT_HORIZON_MILLIS; private float mScreenBrighteningMinThreshold = 0.0f; // Retain behaviour as though there is @@ -534,6 +539,14 @@ public class DisplayDeviceConfig { return mBrightnessRampSlowIncrease; } + public long getBrightnessRampDecreaseMaxMillis() { + return mBrightnessRampDecreaseMaxMillis; + } + + public long getBrightnessRampIncreaseMaxMillis() { + return mBrightnessRampIncreaseMaxMillis; + } + public int getAmbientHorizonLong() { return mAmbientHorizonLong; } @@ -628,6 +641,8 @@ public class DisplayDeviceConfig { + ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease + ", mBrightnessRampSlowDecrease=" + mBrightnessRampSlowDecrease + ", mBrightnessRampSlowIncrease=" + mBrightnessRampSlowIncrease + + ", mBrightnessRampDecreaseMaxMillis=" + mBrightnessRampDecreaseMaxMillis + + ", mBrightnessRampIncreaseMaxMillis=" + mBrightnessRampIncreaseMaxMillis + ", mAmbientHorizonLong=" + mAmbientHorizonLong + ", mAmbientHorizonShort=" + mAmbientHorizonShort + ", mScreenDarkeningMinThreshold=" + mScreenDarkeningMinThreshold @@ -725,6 +740,8 @@ public class DisplayDeviceConfig { mBrightnessRampFastIncrease = PowerManager.BRIGHTNESS_MAX; mBrightnessRampSlowDecrease = PowerManager.BRIGHTNESS_MAX; mBrightnessRampSlowIncrease = PowerManager.BRIGHTNESS_MAX; + mBrightnessRampDecreaseMaxMillis = 0; + mBrightnessRampIncreaseMaxMillis = 0; setSimpleMappingStrategyValues(); loadAmbientLightSensorFromConfigXml(); setProxSensorUnspecified(); @@ -1115,6 +1132,15 @@ public class DisplayDeviceConfig { } loadBrightnessRampsFromConfigXml(); } + + final BigInteger increaseMax = config.getScreenBrightnessRampIncreaseMaxMillis(); + if (increaseMax != null) { + mBrightnessRampIncreaseMaxMillis = increaseMax.intValue(); + } + final BigInteger decreaseMax = config.getScreenBrightnessRampDecreaseMaxMillis(); + if (decreaseMax != null) { + mBrightnessRampDecreaseMaxMillis = decreaseMax.intValue(); + } } private void loadBrightnessRampsFromConfigXml() { diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java index 7dce2380407e..a4f49549c7eb 100644 --- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java +++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java @@ -76,6 +76,8 @@ class DisplayManagerShellCommand extends ShellCommand { return setUserDisabledHdrTypes(); case "get-user-disabled-hdr-types": return getUserDisabledHdrTypes(); + case "get-displays": + return getDisplays(); case "dock": return setDockedAndIdle(); case "undock": @@ -133,6 +135,9 @@ class DisplayManagerShellCommand extends ShellCommand { pw.println(" Sets the user disabled HDR types as TYPES"); pw.println(" get-user-disabled-hdr-types"); pw.println(" Returns the user disabled HDR types"); + pw.println(" get-displays [CATEGORY]"); + pw.println(" Returns the current displays. Can specify string category among"); + pw.println(" DisplayManager.DISPLAY_CATEGORY_*; must use the actual string value."); pw.println(" dock"); pw.println(" Sets brightness to docked + idle screen brightness mode"); pw.println(" undock"); @@ -141,6 +146,18 @@ class DisplayManagerShellCommand extends ShellCommand { Intent.printIntentArgsHelp(pw , ""); } + private int getDisplays() { + String category = getNextArg(); + DisplayManager dm = mService.getContext().getSystemService(DisplayManager.class); + Display[] displays = dm.getDisplays(category); + PrintWriter out = getOutPrintWriter(); + out.println("Displays:"); + for (int i = 0; i < displays.length; i++) { + out.println(" " + displays[i]); + } + return 0; + } + private int setBrightness() { String brightnessText = getNextArg(); if (brightnessText == null) { diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 80ff8349a153..d13a9a3ec27e 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -255,6 +255,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // to reach the final state. private final boolean mBrightnessBucketsInDozeConfig; + // Maximum time a ramp animation can take. + private long mBrightnessRampIncreaseMaxTimeMillis; + private long mBrightnessRampDecreaseMaxTimeMillis; + // The pending power request. // Initially null until the first call to requestPowerState. @GuardedBy("mLock") @@ -507,7 +511,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final Resources resources = context.getResources(); - // DOZE AND DIM SETTINGS mScreenBrightnessDozeConfig = clampAbsoluteBrightness( pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE)); @@ -641,7 +644,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mIsRbcActive = mCdsi.isReduceBrightColorsActivated(); mAutomaticBrightnessController.recalculateSplines(mIsRbcActive, adjustedNits); - // If rbc is turned on, off or there is a change in strength, we want to reset the short // term model. Since the nits range at which brightness now operates has changed due to // RBC/strength change, any short term model based on the previous range should be @@ -837,6 +839,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call loadNitsRange(mContext.getResources()); setUpAutoBrightness(mContext.getResources(), mHandler); reloadReduceBrightColours(); + if (mScreenBrightnessRampAnimator != null) { + mScreenBrightnessRampAnimator.setAnimationTimeLimits( + mBrightnessRampIncreaseMaxTimeMillis, + mBrightnessRampDecreaseMaxTimeMillis); + } mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId, mDisplayDeviceConfig.getHighBrightnessModeData(), new HighBrightnessModeController.HdrBrightnessDeviceConfig() { @@ -883,6 +890,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mScreenBrightnessRampAnimator = new DualRampAnimator<>(mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT, DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT); + mScreenBrightnessRampAnimator.setAnimationTimeLimits( + mBrightnessRampIncreaseMaxTimeMillis, + mBrightnessRampDecreaseMaxTimeMillis); mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener); noteScreenState(mPowerState.getScreenState()); @@ -1007,6 +1017,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessRampRateFastIncrease = mDisplayDeviceConfig.getBrightnessRampFastIncrease(); mBrightnessRampRateSlowDecrease = mDisplayDeviceConfig.getBrightnessRampSlowDecrease(); mBrightnessRampRateSlowIncrease = mDisplayDeviceConfig.getBrightnessRampSlowIncrease(); + mBrightnessRampDecreaseMaxTimeMillis = + mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis(); + mBrightnessRampIncreaseMaxTimeMillis = + mDisplayDeviceConfig.getBrightnessRampIncreaseMaxMillis(); } private void loadNitsRange(Resources resources) { diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java index 2567e4306293..690ec3fb5aaf 100644 --- a/services/core/java/com/android/server/display/RampAnimator.java +++ b/services/core/java/com/android/server/display/RampAnimator.java @@ -34,6 +34,8 @@ class RampAnimator<T> { private float mCurrentValue; private float mTargetValue; private float mRate; + private float mAnimationIncreaseMaxTimeSecs; + private float mAnimationDecreaseMaxTimeSecs; private boolean mAnimating; private float mAnimatedValue; // higher precision copy of mCurrentValue @@ -43,13 +45,24 @@ class RampAnimator<T> { private Listener mListener; - public RampAnimator(T object, FloatProperty<T> property) { + RampAnimator(T object, FloatProperty<T> property) { mObject = object; mProperty = property; mChoreographer = Choreographer.getInstance(); } /** + * Sets the maximum time that a brightness animation can take. + */ + public void setAnimationTimeLimits(long animationRampIncreaseMaxTimeMillis, + long animationRampDecreaseMaxTimeMillis) { + mAnimationIncreaseMaxTimeSecs = (animationRampIncreaseMaxTimeMillis > 0) + ? (animationRampIncreaseMaxTimeMillis / 1000.0f) : 0.0f; + mAnimationDecreaseMaxTimeSecs = (animationRampDecreaseMaxTimeMillis > 0) + ? (animationRampDecreaseMaxTimeMillis / 1000.0f) : 0.0f; + } + + /** * Starts animating towards the specified value. * * If this is the first time the property is being set or if the rate is 0, @@ -83,6 +96,15 @@ class RampAnimator<T> { return false; } + // Adjust the rate so that we do not exceed our maximum animation time. + if (target > mCurrentValue && mAnimationIncreaseMaxTimeSecs > 0.0f + && ((target - mCurrentValue) / rate) > mAnimationIncreaseMaxTimeSecs) { + rate = (target - mCurrentValue) / mAnimationIncreaseMaxTimeSecs; + } else if (target < mCurrentValue && mAnimationDecreaseMaxTimeSecs > 0.0f + && ((mCurrentValue - target) / rate) > mAnimationDecreaseMaxTimeSecs) { + rate = (mCurrentValue - target) / mAnimationDecreaseMaxTimeSecs; + } + // Adjust the rate based on the closest target. // If a faster rate is specified, then use the new rate so that we converge // more rapidly based on the new request. @@ -209,6 +231,17 @@ class RampAnimator<T> { } /** + * Sets the maximum time that a brightness animation can take. + */ + public void setAnimationTimeLimits(long animationRampIncreaseMaxTimeMillis, + long animationRampDecreaseMaxTimeMillis) { + mFirst.setAnimationTimeLimits(animationRampIncreaseMaxTimeMillis, + animationRampDecreaseMaxTimeMillis); + mSecond.setAnimationTimeLimits(animationRampIncreaseMaxTimeMillis, + animationRampDecreaseMaxTimeMillis); + } + + /** * Starts animating towards the specified values. * * If this is the first time the property is being set or if the rate is 0, diff --git a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java index caaf80073de1..f7089417a580 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecNetwork.java @@ -413,7 +413,7 @@ public class HdmiCecNetwork { */ boolean isConnectedToArcPort(int physicalAddress) { int portId = physicalAddressToPortId(physicalAddress); - if (portId != Constants.INVALID_PORT_ID) { + if (portId != Constants.INVALID_PORT_ID && portId != Constants.CEC_SWITCH_HOME) { return mPortInfoMap.get(portId).isArcSupported(); } return false; diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 9e5da450c8a5..d536a463f7b4 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -40,6 +40,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; import android.media.AudioSystem; @@ -85,6 +86,7 @@ import android.view.ViewConfiguration; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.server.LocalManagerRegistry; +import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.Watchdog; import com.android.server.Watchdog.Monitor; @@ -540,14 +542,19 @@ public class MediaSessionService extends SystemService implements Monitor { if (TextUtils.isEmpty(packageName)) { throw new IllegalArgumentException("packageName may not be empty"); } - String[] packages = mContext.getPackageManager().getPackagesForUid(uid); - final int packageCount = packages.length; - for (int i = 0; i < packageCount; i++) { - if (packageName.equals(packages[i])) { - return; - } + if (uid == Process.ROOT_UID || uid == Process.SHELL_UID) { + // If the caller is shell, then trust the packageName given and allow it + // to proceed. + return; + } + final PackageManagerInternal packageManagerInternal = + LocalServices.getService(PackageManagerInternal.class); + final int actualUid = packageManagerInternal.getPackageUid( + packageName, 0 /* flags */, UserHandle.getUserId(uid)); + if (!UserHandle.isSameApp(uid, actualUid)) { + throw new IllegalArgumentException("packageName does not belong to the calling uid; " + + "pkg=" + packageName + ", uid=" + uid); } - throw new IllegalArgumentException("packageName is not owned by the calling process"); } void tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage, diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 6dc1f372b602..3856633df28f 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -657,6 +657,9 @@ public class NotificationManagerService extends SystemService { private int mWarnRemoteViewsSizeBytes; private int mStripRemoteViewsSizeBytes; + @VisibleForTesting + protected boolean mShowReviewPermissionsNotification; + private MetricsLogger mMetricsLogger; private NotificationChannelLogger mNotificationChannelLogger; private TriPredicate<String, Integer, String> mAllowedManagedServicePackages; @@ -2280,7 +2283,8 @@ public class NotificationManagerService extends SystemService { mPermissionHelper, mNotificationChannelLogger, mAppOps, - new SysUiStatsEvent.BuilderFactory()); + new SysUiStatsEvent.BuilderFactory(), + mShowReviewPermissionsNotification); mPreferencesHelper.updateFixedImportance(mUm.getUsers()); mRankingHelper = new RankingHelper(getContext(), mRankingHandler, @@ -2469,6 +2473,9 @@ public class NotificationManagerService extends SystemService { WorkerHandler handler = new WorkerHandler(Looper.myLooper()); + mShowReviewPermissionsNotification = getContext().getResources().getBoolean( + R.bool.config_notificationReviewPermissions); + init(handler, new RankingHandlerWorker(mRankingThread.getLooper()), AppGlobals.getPackageManager(), getContext().getPackageManager(), getLocalService(LightsManager.class), @@ -6320,6 +6327,11 @@ public class NotificationManagerService extends SystemService { @Override public void sendReviewPermissionsNotification() { + if (!mShowReviewPermissionsNotification) { + // don't show if this notification is turned off + return; + } + // This method is meant to be called from the JobService upon running the job for this // notification having been rescheduled; so without checking any other state, it will // send the notification. @@ -11648,6 +11660,11 @@ public class NotificationManagerService extends SystemService { } protected void maybeShowInitialReviewPermissionsNotification() { + if (!mShowReviewPermissionsNotification) { + // if this notification is disabled by settings do not ever show it + return; + } + int currentState = Settings.Global.getInt(getContext().getContentResolver(), Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE, REVIEW_NOTIF_STATE_UNKNOWN); diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 97133a56779d..477b8da61e0f 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -86,7 +86,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -191,6 +190,7 @@ public class PreferencesHelper implements RankingConfig { private boolean mIsMediaNotificationFilteringEnabled = DEFAULT_MEDIA_NOTIFICATION_FILTERING; private boolean mAreChannelsBypassingDnd; private boolean mHideSilentStatusBarIcons = DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS; + private boolean mShowReviewPermissionsNotification; private boolean mAllowInvalidShortcuts = false; @@ -198,7 +198,8 @@ public class PreferencesHelper implements RankingConfig { ZenModeHelper zenHelper, PermissionHelper permHelper, NotificationChannelLogger notificationChannelLogger, AppOpsManager appOpsManager, - SysUiStatsEvent.BuilderFactory statsEventBuilderFactory) { + SysUiStatsEvent.BuilderFactory statsEventBuilderFactory, + boolean showReviewPermissionsNotification) { mContext = context; mZenModeHelper = zenHelper; mRankingHandler = rankingHandler; @@ -207,6 +208,7 @@ public class PreferencesHelper implements RankingConfig { mNotificationChannelLogger = notificationChannelLogger; mAppOps = appOpsManager; mStatsEventBuilderFactory = statsEventBuilderFactory; + mShowReviewPermissionsNotification = showReviewPermissionsNotification; XML_VERSION = 4; @@ -226,7 +228,8 @@ public class PreferencesHelper implements RankingConfig { final int xmlVersion = parser.getAttributeInt(null, ATT_VERSION, -1); boolean upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE; boolean migrateToPermission = (xmlVersion < XML_VERSION_NOTIF_PERMISSION); - if (xmlVersion < XML_VERSION_REVIEW_PERMISSIONS_NOTIFICATION) { + if (mShowReviewPermissionsNotification + && (xmlVersion < XML_VERSION_REVIEW_PERMISSIONS_NOTIFICATION)) { // make a note that we should show the notification at some point. // it shouldn't be possible for the user to already have seen it, as the XML version // would be newer then. diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index 0e9a04f1fdde..09044e72f60b 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -57,6 +57,16 @@ <xs:element type="nonNegativeDecimal" name="screenBrightnessRampSlowIncrease"> <xs:annotation name="final"/> </xs:element> + <!-- Maximum time in milliseconds that a brightness increase animation + can take. --> + <xs:element type="xs:nonNegativeInteger" name="screenBrightnessRampIncreaseMaxMillis"> + <xs:annotation name="final"/> + </xs:element> + <!-- Maximum time in milliseconds that a brightness decrease animation + can take. --> + <xs:element type="xs:nonNegativeInteger" name="screenBrightnessRampDecreaseMaxMillis"> + <xs:annotation name="final"/> + </xs:element> <xs:element type="sensorDetails" name="lightSensor"> <xs:annotation name="final"/> </xs:element> diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index 075edd77af1d..e8b13ca6356e 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -48,8 +48,10 @@ package com.android.server.display.config { method public com.android.server.display.config.DisplayQuirks getQuirks(); method @NonNull public final java.math.BigDecimal getScreenBrightnessDefault(); method @NonNull public final com.android.server.display.config.NitsMap getScreenBrightnessMap(); + method public final java.math.BigInteger getScreenBrightnessRampDecreaseMaxMillis(); method public final java.math.BigDecimal getScreenBrightnessRampFastDecrease(); method public final java.math.BigDecimal getScreenBrightnessRampFastIncrease(); + method public final java.math.BigInteger getScreenBrightnessRampIncreaseMaxMillis(); method public final java.math.BigDecimal getScreenBrightnessRampSlowDecrease(); method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease(); method @NonNull public final com.android.server.display.config.ThermalThrottling getThermalThrottling(); @@ -64,8 +66,10 @@ package com.android.server.display.config { method public void setQuirks(com.android.server.display.config.DisplayQuirks); method public final void setScreenBrightnessDefault(@NonNull java.math.BigDecimal); method public final void setScreenBrightnessMap(@NonNull com.android.server.display.config.NitsMap); + method public final void setScreenBrightnessRampDecreaseMaxMillis(java.math.BigInteger); method public final void setScreenBrightnessRampFastDecrease(java.math.BigDecimal); method public final void setScreenBrightnessRampFastIncrease(java.math.BigDecimal); + method public final void setScreenBrightnessRampIncreaseMaxMillis(java.math.BigInteger); method public final void setScreenBrightnessRampSlowDecrease(java.math.BigDecimal); method public final void setScreenBrightnessRampSlowIncrease(java.math.BigDecimal); method public final void setThermalThrottling(@NonNull com.android.server.display.config.ThermalThrottling); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index a7dc8518daea..779673e98c6f 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -19,7 +19,6 @@ package com.android.server.notification; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; import static android.app.ActivityTaskManager.INVALID_TASK_ID; -import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.Notification.FLAG_AUTO_CANCEL; import static android.app.Notification.FLAG_BUBBLE; import static android.app.Notification.FLAG_CAN_COLORIZE; @@ -9572,7 +9571,21 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testMaybeShowReviewPermissionsNotification_flagOff() { + mService.setShowReviewPermissionsNotification(false); + reset(mMockNm); + + // If state is SHOULD_SHOW, it would show, but not if the flag is off! + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE, + NotificationManagerService.REVIEW_NOTIF_STATE_SHOULD_SHOW); + mService.maybeShowInitialReviewPermissionsNotification(); + verify(mMockNm, never()).notify(anyString(), anyInt(), any(Notification.class)); + } + + @Test public void testMaybeShowReviewPermissionsNotification_unknown() { + mService.setShowReviewPermissionsNotification(true); reset(mMockNm); // Set up various possible states of the settings int and confirm whether or not the @@ -9588,6 +9601,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testMaybeShowReviewPermissionsNotification_shouldShow() { + mService.setShowReviewPermissionsNotification(true); reset(mMockNm); // If state is SHOULD_SHOW, it ... should show @@ -9602,6 +9616,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testMaybeShowReviewPermissionsNotification_alreadyShown() { + mService.setShowReviewPermissionsNotification(true); reset(mMockNm); // If state is either USER_INTERACTED or DISMISSED, we should not show this on boot @@ -9620,6 +9635,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testMaybeShowReviewPermissionsNotification_reshown() { + mService.setShowReviewPermissionsNotification(true); reset(mMockNm); // If we have re-shown the notification and the user did not subsequently interacted with @@ -9635,6 +9651,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testRescheduledReviewPermissionsNotification() { + mService.setShowReviewPermissionsNotification(true); reset(mMockNm); // when rescheduled, the notification goes through the NotificationManagerInternal service @@ -9653,4 +9670,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE, NotificationManagerService.REVIEW_NOTIF_STATE_UNKNOWN)); } + + @Test + public void testRescheduledReviewPermissionsNotification_flagOff() { + mService.setShowReviewPermissionsNotification(false); + reset(mMockNm); + + // no notification should be sent if the flag is off + mInternalService.sendReviewPermissionsNotification(); + verify(mMockNm, never()).notify(anyString(), anyInt(), any(Notification.class)); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 8d50ceaf74e9..598a22bbde39 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -292,7 +292,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory(); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); + mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); resetZenModeHelper(); mAudioAttributes = new AudioAttributes.Builder() @@ -621,7 +621,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_oldXml_migrates() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); + mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true); String xml = "<ranking version=\"2\">\n" + "<package name=\"" + PKG_N_MR1 + "\" uid=\"" + UID_N_MR1 @@ -691,7 +691,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_oldXml_backup_migratesWhenPkgInstalled() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); + mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); when(mPm.getPackageUidAsUser("pkg1", USER_SYSTEM)).thenReturn(UNKNOWN_UID); when(mPm.getPackageUidAsUser("pkg2", USER_SYSTEM)).thenReturn(UNKNOWN_UID); @@ -769,7 +769,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_newXml_noMigration_showPermissionNotification() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); + mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true); String xml = "<ranking version=\"3\">\n" + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n" @@ -824,9 +824,66 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test + public void testReadXml_newXml_permissionNotificationOff() throws Exception { + mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, + mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + + String xml = "<ranking version=\"3\">\n" + + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n" + + "<channel id=\"idn\" name=\"name\" importance=\"2\"/>\n" + + "<channel id=\"miscellaneous\" name=\"Uncategorized\" />\n" + + "</package>\n" + + "<package name=\"" + PKG_O + "\" >\n" + + "<channel id=\"ido\" name=\"name2\" importance=\"2\" show_badge=\"true\"/>\n" + + "</package>\n" + + "<package name=\"" + PKG_P + "\" >\n" + + "<channel id=\"idp\" name=\"name3\" importance=\"4\" locked=\"2\" />\n" + + "</package>\n" + + "</ranking>\n"; + NotificationChannel idn = new NotificationChannel("idn", "name", IMPORTANCE_LOW); + idn.setSound(null, new AudioAttributes.Builder() + .setUsage(USAGE_NOTIFICATION) + .setContentType(CONTENT_TYPE_SONIFICATION) + .setFlags(0) + .build()); + idn.setShowBadge(false); + NotificationChannel ido = new NotificationChannel("ido", "name2", IMPORTANCE_LOW); + ido.setShowBadge(true); + ido.setSound(null, new AudioAttributes.Builder() + .setUsage(USAGE_NOTIFICATION) + .setContentType(CONTENT_TYPE_SONIFICATION) + .setFlags(0) + .build()); + NotificationChannel idp = new NotificationChannel("idp", "name3", IMPORTANCE_HIGH); + idp.lockFields(2); + idp.setSound(null, new AudioAttributes.Builder() + .setUsage(USAGE_NOTIFICATION) + .setContentType(CONTENT_TYPE_SONIFICATION) + .setFlags(0) + .build()); + + loadByteArrayXml(xml.getBytes(), true, USER_SYSTEM); + + assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1)); + + assertEquals(idn, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, idn.getId(), false)); + compareChannels(ido, mHelper.getNotificationChannel(PKG_O, UID_O, ido.getId(), false)); + compareChannels(idp, mHelper.getNotificationChannel(PKG_P, UID_P, idp.getId(), false)); + + verify(mPermissionHelper, never()).setNotificationPermission(any()); + + // while this is the same case as above, if the permission helper is set to not show the + // review permissions notification it should not write anything to the settings int + assertEquals(NotificationManagerService.REVIEW_NOTIF_STATE_UNKNOWN, + Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE, + NotificationManagerService.REVIEW_NOTIF_STATE_UNKNOWN)); + } + + @Test public void testReadXml_newXml_noMigration_noPermissionNotification() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); + mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, true); String xml = "<ranking version=\"4\">\n" + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n" @@ -882,7 +939,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_oldXml_migration_NoUid() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); + mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID); String xml = "<ranking version=\"2\">\n" @@ -915,7 +972,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_newXml_noMigration_NoUid() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); + mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); when(mPm.getPackageUidAsUser("something", USER_SYSTEM)).thenReturn(UNKNOWN_UID); String xml = "<ranking version=\"3\">\n" @@ -947,7 +1004,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testChannelXmlForNonBackup_postMigration() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); + mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); appPermissions.put(new Pair(1, "first"), new Pair(true, false)); @@ -1027,7 +1084,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testChannelXmlForBackup_postMigration() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); + mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); appPermissions.put(new Pair(1, "first"), new Pair(true, false)); @@ -1113,7 +1170,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testChannelXmlForBackup_postMigration_noExternal() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); + mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); appPermissions.put(new Pair(UID_P, PKG_P), new Pair(true, false)); @@ -1192,7 +1249,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testChannelXmlForBackup_postMigration_noLocalSettings() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, - mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); + mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); appPermissions.put(new Pair(1, "first"), new Pair(true, false)); @@ -2117,7 +2174,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); // create notification channel that can bypass dnd, but app is blocked @@ -2145,7 +2202,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); // create notification channel that can bypass dnd, but app is blocked // expected result: areChannelsBypassingDnd = false @@ -2168,7 +2225,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); // create notification channel that can bypass dnd, but app is blocked // expected result: areChannelsBypassingDnd = false @@ -2220,7 +2277,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); assertFalse(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, times(1)).setNotificationPolicy(any()); resetZenModeHelper(); @@ -2233,7 +2290,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); assertFalse(mHelper.areChannelsBypassingDnd()); verify(mMockZenModeHelper, never()).setNotificationPolicy(any()); resetZenModeHelper(); @@ -3229,7 +3286,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { + "</ranking>\n"; mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); loadByteArrayXml(preQXml.getBytes(), true, USER_SYSTEM); assertEquals(PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS, @@ -3243,7 +3300,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(!PreferencesHelper.DEFAULT_HIDE_SILENT_STATUS_BAR_ICONS, @@ -3341,7 +3398,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); @@ -3354,7 +3411,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals("other", mHelper.getNotificationDelegate(PKG_O, UID_O)); @@ -3368,7 +3425,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); @@ -3382,7 +3439,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); // appears disabled @@ -3402,7 +3459,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); // appears disabled @@ -3422,7 +3479,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(BUBBLE_PREFERENCE_NONE, mHelper.getBubblePreference(PKG_O, UID_O)); @@ -3478,7 +3535,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(BUBBLE_PREFERENCE_SELECTED, mHelper.getBubblePreference(PKG_O, UID_O)); @@ -3516,7 +3573,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); loadStreamXml(baos, false, UserHandle.USER_ALL); assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), BUBBLE_PREFERENCE_NONE); @@ -4150,7 +4207,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testPlaceholderConversationId_shortcutRequired() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); final String xml = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" @@ -4170,7 +4227,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testNormalConversationId_shortcutRequired() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); final String xml = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" @@ -4190,7 +4247,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testNoConversationId_shortcutRequired() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); final String xml = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" @@ -4210,7 +4267,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testDeleted_noTime() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); final String xml = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" @@ -4230,7 +4287,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testDeleted_twice() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); mHelper.createNotificationChannel( PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false); @@ -4242,7 +4299,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testDeleted_recentTime() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); mHelper.createNotificationChannel( PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false); @@ -4260,7 +4317,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { parser.nextTag(); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); mHelper.readXml(parser, true, USER_SYSTEM); NotificationChannel nc = mHelper.getNotificationChannel(PKG_P, UID_P, "id", true); @@ -4272,7 +4329,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testUnDelete_time() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); mHelper.createNotificationChannel( PKG_P, UID_P, new NotificationChannel("id", "id", 2), true, false); @@ -4292,7 +4349,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testDeleted_longTime() throws Exception { mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, - mAppOpsManager, mStatsEventBuilderFactory); + mAppOpsManager, mStatsEventBuilderFactory, false); long time = System.currentTimeMillis() - (DateUtils.DAY_IN_MILLIS * 30); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java index 4ed7d35a097f..b49e5cbfa9dc 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java @@ -114,6 +114,11 @@ public class TestableNotificationManagerService extends NotificationManagerServi mChannelToastsSent.add(uid); } + // Helper method for testing behavior when turning on/off the review permissions notification. + protected void setShowReviewPermissionsNotification(boolean setting) { + mShowReviewPermissionsNotification = setting; + } + public class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker { private int mGetStrongAuthForUserReturnValue = 0; StrongAuthTrackerFake(Context context) { diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 43fcc8feaec2..73b2510e6cf1 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -35,6 +35,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.ShortcutServiceInternal; @@ -104,6 +105,7 @@ import com.android.server.pm.permission.LegacyPermissionManagerInternal; import com.android.server.soundtrigger.SoundTriggerInternal; import com.android.server.utils.TimingsTraceAndSlog; import com.android.server.wm.ActivityTaskManagerInternal; +import com.android.server.wm.ActivityTaskManagerInternal.ActivityTokens; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -126,6 +128,7 @@ public class VoiceInteractionManagerService extends SystemService { final ActivityManagerInternal mAmInternal; final ActivityTaskManagerInternal mAtmInternal; final UserManagerInternal mUserManagerInternal; + final PackageManagerInternal mPackageManagerInternal; final ArrayMap<Integer, VoiceInteractionManagerServiceStub.SoundTriggerSession> mLoadedKeyphraseIds = new ArrayMap<>(); ShortcutServiceInternal mShortcutServiceInternal; @@ -146,6 +149,8 @@ public class VoiceInteractionManagerService extends SystemService { LocalServices.getService(ActivityTaskManagerInternal.class)); mUserManagerInternal = Objects.requireNonNull( LocalServices.getService(UserManagerInternal.class)); + mPackageManagerInternal = Objects.requireNonNull( + LocalServices.getService(PackageManagerInternal.class)); LegacyPermissionManagerInternal permissionManagerInternal = LocalServices.getService( LegacyPermissionManagerInternal.class); @@ -369,6 +374,21 @@ public class VoiceInteractionManagerService extends SystemService { return new SoundTriggerSessionBinderProxy(session); } + @GuardedBy("this") + private void grantImplicitAccessLocked(int grantRecipientUid, @Nullable Intent intent) { + if (mImpl == null) { + Slog.w(TAG, "Cannot grant implicit access because mImpl is null."); + return; + } + + final int grantRecipientAppId = UserHandle.getAppId(grantRecipientUid); + final int grantRecipientUserId = UserHandle.getUserId(grantRecipientUid); + final int voiceInteractionUid = mImpl.mInfo.getServiceInfo().applicationInfo.uid; + mPackageManagerInternal.grantImplicitAccess( + grantRecipientUserId, intent, grantRecipientAppId, voiceInteractionUid, + /* direct= */ true); + } + private IVoiceInteractionSoundTriggerSession createSoundTriggerSessionForSelfIdentity( IBinder client) { Identity identity = new Identity(); @@ -386,6 +406,7 @@ public class VoiceInteractionManagerService extends SystemService { void startLocalVoiceInteraction(final IBinder token, Bundle options) { if (mImpl == null) return; + final int callingUid = Binder.getCallingUid(); final long caller = Binder.clearCallingIdentity(); try { mImpl.showSessionLocked(options, @@ -397,6 +418,11 @@ public class VoiceInteractionManagerService extends SystemService { @Override public void onShown() { + synchronized (VoiceInteractionManagerServiceStub.this) { + VoiceInteractionManagerServiceStub.this + .grantImplicitAccessLocked(callingUid, + /* intent= */ null); + } mAtmInternal.onLocalVoiceInteractionStarted(token, mImpl.mActiveSession.mSession, mImpl.mActiveSession.mInteractor); @@ -965,8 +991,16 @@ public class VoiceInteractionManagerService extends SystemService { final int callingUid = Binder.getCallingUid(); final long caller = Binder.clearCallingIdentity(); try { - return mImpl.startVoiceActivityLocked(callingFeatureId, callingPid, callingUid, - token, intent, resolvedType); + final ActivityInfo activityInfo = intent.resolveActivityInfo( + mContext.getPackageManager(), PackageManager.MATCH_ALL); + if (activityInfo != null) { + final int activityUid = activityInfo.applicationInfo.uid; + grantImplicitAccessLocked(activityUid, intent); + } else { + Slog.w(TAG, "Cannot find ActivityInfo in startVoiceActivity."); + } + return mImpl.startVoiceActivityLocked( + callingFeatureId, callingPid, callingUid, token, intent, resolvedType); } finally { Binder.restoreCallingIdentity(caller); } @@ -1005,6 +1039,15 @@ public class VoiceInteractionManagerService extends SystemService { } final long caller = Binder.clearCallingIdentity(); try { + // Getting the UID corresponding to the taskId, and grant the visibility to it. + final ActivityTokens tokens = mAtmInternal + .getAttachedNonFinishingActivityForTask(taskId, /* token= */ null); + final ComponentName componentName = mAtmInternal.getActivityName( + tokens.getActivityToken()); + grantImplicitAccessLocked(mPackageManagerInternal.getPackageUid( + componentName.getPackageName(), PackageManager.MATCH_ALL, + UserHandle.myUserId()), /* intent= */ null); + mImpl.requestDirectActionsLocked(token, taskId, assistToken, cancellationCallback, resultCallback); } finally { diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java index fa1bae41f433..5e111639b100 100644 --- a/telephony/java/android/telephony/data/DataProfile.java +++ b/telephony/java/android/telephony/data/DataProfile.java @@ -232,13 +232,14 @@ public final class DataProfile implements Parcelable { } /** - * @return True if the profile is enabled. + * @return {@code true} if the profile is enabled. If the profile only has a + * {@link TrafficDescriptor}, but no {@link ApnSetting}, then this profile is always enabled. */ public boolean isEnabled() { if (mApnSetting != null) { return mApnSetting.isEnabled(); } - return false; + return true; } /** @@ -534,7 +535,7 @@ public final class DataProfile implements Parcelable { @Type private int mType = -1; - private boolean mEnabled; + private boolean mEnabled = true; @ApnType private int mSupportedApnTypesBitmask; |