diff options
42 files changed, 893 insertions, 342 deletions
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java index e5a06c9bd146..3c361d772d3d 100644 --- a/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java +++ b/apct-tests/perftests/core/src/android/graphics/perftests/CanvasPerfTest.java @@ -16,12 +16,14 @@ package android.graphics.perftests; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.Color; +import android.graphics.ColorSpace; import android.graphics.ImageDecoder; import android.graphics.Paint; import android.graphics.RecordingCanvas; @@ -104,15 +106,36 @@ public class CanvasPerfTest { } @Test - public void testCreateScaledBitmap() throws IOException { + public void testCreateScaledSrgbBitmap() throws IOException { BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); final Context context = InstrumentationRegistry.getContext(); Bitmap source = ImageDecoder.decodeBitmap( ImageDecoder.createSource(context.getResources(), R.drawable.fountain_night), (decoder, info, source1) -> { decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + decoder.setTargetColorSpace(ColorSpace.get(ColorSpace.Named.SRGB)); }); source.setGainmap(null); + assertEquals(source.getColorSpace().getId(), ColorSpace.Named.SRGB.ordinal()); + + while (state.keepRunning()) { + Bitmap.createScaledBitmap(source, source.getWidth() / 2, source.getHeight() / 2, true) + .recycle(); + } + } + + @Test + public void testCreateScaledP3Bitmap() throws IOException { + BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + final Context context = InstrumentationRegistry.getContext(); + Bitmap source = ImageDecoder.decodeBitmap( + ImageDecoder.createSource(context.getResources(), R.drawable.fountain_night), + (decoder, info, source1) -> { + decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE); + decoder.setTargetColorSpace(ColorSpace.get(ColorSpace.Named.DISPLAY_P3)); + }); + source.setGainmap(null); + assertEquals(source.getColorSpace().getId(), ColorSpace.Named.DISPLAY_P3.ordinal()); while (state.keepRunning()) { Bitmap.createScaledBitmap(source, source.getWidth() / 2, source.getHeight() / 2, true) diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index b75c64dcc3c1..fa76e3976a58 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3547,6 +3547,8 @@ public abstract class Context { * * @param receiver The BroadcastReceiver to unregister. * + * @throws IllegalArgumentException if the {@code receiver} was not previously registered or + * already unregistered. * @see #registerReceiver */ public abstract void unregisterReceiver(BroadcastReceiver receiver); diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java index b69410cf7d73..a86396cd7c8d 100644 --- a/core/java/android/net/LocalSocket.java +++ b/core/java/android/net/LocalSocket.java @@ -196,7 +196,8 @@ public class LocalSocket implements Closeable { } /** - * Retrieves the input stream for this instance. + * Retrieves the input stream for this instance. Closing this stream is equivalent to closing + * the entire socket and its associated streams using {@link #close()}. * * @return input stream * @throws IOException if socket has been closed or cannot be created. @@ -207,7 +208,8 @@ public class LocalSocket implements Closeable { } /** - * Retrieves the output stream for this instance. + * Retrieves the output stream for this instance. Closing this stream is equivalent to closing + * the entire socket and its associated streams using {@link #close()}. * * @return output stream * @throws IOException if socket has been closed or cannot be created. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 1f81a6418962..fcdad6673fd0 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -11910,6 +11910,12 @@ public final class ViewRootImpl implements ViewParent, if (syncBuffer) { boolean result = mBlastBufferQueue.syncNextTransaction(transaction -> { + Runnable timeoutRunnable = () -> Log.e(mTag, + "Failed to submit the sync transaction after 4s. Likely to ANR " + + "soon"); + mHandler.postDelayed(timeoutRunnable, 4L * Build.HW_TIMEOUT_MULTIPLIER); + transaction.addTransactionCommittedListener(mSimpleExecutor, + () -> mHandler.removeCallbacks(timeoutRunnable)); surfaceSyncGroup.addTransaction(transaction); surfaceSyncGroup.markSyncReady(); }); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index 662a5c47d633..a76bd269f31b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -95,7 +95,6 @@ public class BubblePositioner { private int mMinimumFlyoutWidthLargeScreen; private PointF mRestingStackPosition; - private int[] mPaddings = new int[4]; private boolean mShowingInBubbleBar; private final Point mBubbleBarPosition = new Point(); @@ -344,46 +343,44 @@ public class BubblePositioner { final int pointerTotalHeight = getPointerSize(); final int expandedViewLargeScreenInsetFurthestEdge = getExpandedViewLargeScreenInsetFurthestEdge(isOverflow); + int[] paddings = new int[4]; if (mDeviceConfig.isLargeScreen()) { // Note: // If we're in portrait OR if we're a small tablet, then the two insets values will // be equal. If we're landscape and a large tablet, the two values will be different. // [left, top, right, bottom] - mPaddings[0] = onLeft + paddings[0] = onLeft ? mExpandedViewLargeScreenInsetClosestEdge - pointerTotalHeight : expandedViewLargeScreenInsetFurthestEdge; - mPaddings[1] = 0; - mPaddings[2] = onLeft + paddings[1] = 0; + paddings[2] = onLeft ? expandedViewLargeScreenInsetFurthestEdge : mExpandedViewLargeScreenInsetClosestEdge - pointerTotalHeight; // Overflow doesn't show manage button / get padding from it so add padding here - mPaddings[3] = isOverflow ? mExpandedViewPadding : 0; - return mPaddings; + paddings[3] = isOverflow ? mExpandedViewPadding : 0; + return paddings; } else { int leftPadding = mInsets.left + mExpandedViewPadding; int rightPadding = mInsets.right + mExpandedViewPadding; - final float expandedViewWidth = isOverflow - ? mOverflowWidth - : mExpandedViewLargeScreenWidth; if (showBubblesVertically()) { if (!onLeft) { rightPadding += mBubbleSize - pointerTotalHeight; leftPadding += isOverflow - ? (mPositionRect.width() - rightPadding - expandedViewWidth) + ? (mPositionRect.width() - rightPadding - mOverflowWidth) : 0; } else { leftPadding += mBubbleSize - pointerTotalHeight; rightPadding += isOverflow - ? (mPositionRect.width() - leftPadding - expandedViewWidth) + ? (mPositionRect.width() - leftPadding - mOverflowWidth) : 0; } } // [left, top, right, bottom] - mPaddings[0] = leftPadding; - mPaddings[1] = showBubblesVertically() ? 0 : mPointerMargin; - mPaddings[2] = rightPadding; - mPaddings[3] = 0; - return mPaddings; + paddings[0] = leftPadding; + paddings[1] = showBubblesVertically() ? 0 : mPointerMargin; + paddings[2] = rightPadding; + paddings[3] = 0; + return paddings; } } @@ -395,7 +392,7 @@ public class BubblePositioner { } /** Gets the y position of the expanded view if it was top-aligned. */ - public float getExpandedViewYTopAligned() { + public int getExpandedViewYTopAligned() { final int top = getAvailableRect().top; if (showBubblesVertically()) { return top - mPointerWidth + mExpandedViewPadding; @@ -413,7 +410,7 @@ public class BubblePositioner { return getExpandedViewHeightForLargeScreen(); } // Subtract top insets because availableRect.height would account for that - int expandedContainerY = (int) getExpandedViewYTopAligned() - getInsets().top; + int expandedContainerY = getExpandedViewYTopAligned() - getInsets().top; int paddingTop = showBubblesVertically() ? 0 : mPointerHeight; @@ -474,11 +471,11 @@ public class BubblePositioner { public float getExpandedViewY(BubbleViewProvider bubble, float bubblePosition) { boolean isOverflow = bubble == null || BubbleOverflow.KEY.equals(bubble.getKey()); float expandedViewHeight = getExpandedViewHeight(bubble); - float topAlignment = getExpandedViewYTopAligned(); + int topAlignment = getExpandedViewYTopAligned(); int manageButtonHeight = isOverflow ? mExpandedViewPadding : mManageButtonHeightIncludingMargins; - // On largescreen portrait bubbles are bottom aligned. + // On large screen portrait bubbles are bottom aligned. if (areBubblesBottomAligned() && expandedViewHeight == MAX_HEIGHT) { return mPositionRect.bottom - manageButtonHeight - getExpandedViewHeightForLargeScreen() - mPointerWidth; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java index 02af2d06a1dd..7798aa753aa2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java @@ -420,7 +420,7 @@ public class ExpandedAnimationController bubbleView.setTranslationY(y); } - final float expandedY = mPositioner.getExpandedViewYTopAligned(); + final int expandedY = mPositioner.getExpandedViewYTopAligned(); final boolean draggedOutEnough = y > expandedY + mBubbleSizePx || y < expandedY - mBubbleSizePx; if (draggedOutEnough != mBubbleDraggedOutEnough) { diff --git a/libs/incident/libincident.map.txt b/libs/incident/libincident.map.txt index f75cceaf59fa..d8650e129742 100644 --- a/libs/incident/libincident.map.txt +++ b/libs/incident/libincident.map.txt @@ -1,15 +1,15 @@ LIBINCIDENT { global: - AIncidentReportArgs_init; # systemapi # introduced=30 - AIncidentReportArgs_clone; # systemapi # introduced=30 - AIncidentReportArgs_delete; # systemapi # introduced=30 - AIncidentReportArgs_setAll; # systemapi # introduced=30 - AIncidentReportArgs_setPrivacyPolicy; # systemapi # introduced=30 - AIncidentReportArgs_addSection; # systemapi # introduced=30 - AIncidentReportArgs_setReceiverPackage; # systemapi # introduced=30 - AIncidentReportArgs_setReceiverClass; # systemapi # introduced=30 - AIncidentReportArgs_addHeader; # systemapi # introduced=30 - AIncidentReportArgs_takeReport; # systemapi # introduced=30 + AIncidentReportArgs_init; # systemapi introduced=30 + AIncidentReportArgs_clone; # systemapi introduced=30 + AIncidentReportArgs_delete; # systemapi introduced=30 + AIncidentReportArgs_setAll; # systemapi introduced=30 + AIncidentReportArgs_setPrivacyPolicy; # systemapi introduced=30 + AIncidentReportArgs_addSection; # systemapi introduced=30 + AIncidentReportArgs_setReceiverPackage; # systemapi introduced=30 + AIncidentReportArgs_setReceiverClass; # systemapi introduced=30 + AIncidentReportArgs_addHeader; # systemapi introduced=30 + AIncidentReportArgs_takeReport; # systemapi introduced=30 local: *; }; diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING index f8176a1dd052..8f5f1f6a4794 100644 --- a/media/TEST_MAPPING +++ b/media/TEST_MAPPING @@ -50,7 +50,7 @@ ] } ], - "presubmit": [ + "postsubmit": [ { "file_patterns": [ "[^/]*(LoudnessCodec)[^/]*\\.java" diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index 9f2a9ac4798d..960510879a4c 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -1,9 +1,9 @@ LIBANDROID { global: - AActivityManager_addUidImportanceListener; # systemapi # introduced=31 - AActivityManager_removeUidImportanceListener; # systemapi # introduced=31 - AActivityManager_isUidActive; # systemapi # introduced=31 - AActivityManager_getUidImportance; # systemapi # introduced=31 + AActivityManager_addUidImportanceListener; # systemapi introduced=31 + AActivityManager_removeUidImportanceListener; # systemapi introduced=31 + AActivityManager_isUidActive; # systemapi introduced=31 + AActivityManager_getUidImportance; # systemapi introduced=31 AAssetDir_close; AAssetDir_getNextFileName; AAssetDir_rewind; diff --git a/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml b/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml new file mode 100644 index 000000000000..9d16f32db932 --- /dev/null +++ b/packages/CredentialManager/res/drawable/autofill_light_selectable_item_background.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2023 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. + --> + +<!-- Copied from //frameworks/base/core/res/res/drawable/item_background_material.xml --> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="@color/autofill_light_colorControlHighlight"> + <item android:id="@android:id/mask"> + <color android:color="@android:color/white"/> + </item> +</ripple>
\ No newline at end of file diff --git a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml new file mode 100644 index 000000000000..2f0c83b6556a --- /dev/null +++ b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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. + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" tools:ignore="NewApi" + android:color="@android:color/transparent"> + <item + android:bottom="1dp" + android:shape="rectangle" + android:top="1dp"> + <shape> + <corners android:radius="28dp" /> + <solid android:color="@android:color/system_surface_container_high_light" /> + </shape> + </item> +</ripple>
\ No newline at end of file diff --git a/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one_dark.xml b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one_dark.xml new file mode 100644 index 000000000000..39f49caa7051 --- /dev/null +++ b/packages/CredentialManager/res/drawable/fill_dialog_dynamic_list_item_one_dark.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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. + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" tools:ignore="NewApi" + android:color="@android:color/transparent"> + <item + android:bottom="1dp" + android:shape="rectangle" + android:top="1dp"> + <shape> + <corners android:radius="28dp" /> + <solid android:color="@android:color/system_surface_container_high_dark" /> + </shape> + </item> +</ripple>
\ No newline at end of file diff --git a/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml b/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml new file mode 100644 index 000000000000..e4e9f7ac85a9 --- /dev/null +++ b/packages/CredentialManager/res/layout/autofill_dataset_left_with_item_tag_hint.xml @@ -0,0 +1,37 @@ +<!-- + ~ Copyright (C) 2023 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. + --> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@style/autofill.Dataset"> + <ImageView + android:id="@android:id/icon1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerVertical="true" + android:layout_alignParentStart="true" + android:background="@null"/> + <TextView + android:id="@android:id/text1" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentTop="true" + android:layout_toEndOf="@android:id/icon1" + style="@style/autofill.TextAppearance"/> + +</RelativeLayout> diff --git a/packages/CredentialManager/res/values/colors.xml b/packages/CredentialManager/res/values/colors.xml new file mode 100644 index 000000000000..63b9f24d9033 --- /dev/null +++ b/packages/CredentialManager/res/values/colors.xml @@ -0,0 +1,38 @@ +<!-- + ~ Copyright (C) 2023 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. + --> + +<!-- Color palette --> +<resources> + <color name="autofill_light_colorPrimary">@color/primary_material_light</color> + <color name="autofill_light_colorAccent">@color/accent_material_light</color> + <color name="autofill_light_colorControlHighlight">@color/ripple_material_light</color> + <color name="autofill_light_colorButtonNormal">@color/button_material_light</color> + + <!-- Text colors --> + <color name="autofill_light_textColorPrimary">@color/abc_primary_text_material_light</color> + <color name="autofill_light_textColorSecondary">@color/abc_secondary_text_material_light</color> + <color name="autofill_light_textColorHint">@color/abc_hint_foreground_material_light</color> + <color name="autofill_light_textColorHintInverse">@color/abc_hint_foreground_material_dark + </color> + <color name="autofill_light_textColorHighlight">@color/highlighted_text_material_light</color> + <color name="autofill_light_textColorLink">@color/autofill_light_colorAccent</color> + + <!-- These colors are used for Remote Views. --> + <color name="background_dark_mode">#0E0C0B</color> + <color name="background">#F1F3F4</color> + <color name="text_primary_dark_mode">#DFDEDB</color> + <color name="text_primary">#202124</color> +</resources>
\ No newline at end of file diff --git a/packages/CredentialManager/res/values/dimens.xml b/packages/CredentialManager/res/values/dimens.xml new file mode 100644 index 000000000000..67003a330974 --- /dev/null +++ b/packages/CredentialManager/res/values/dimens.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <dimen name="autofill_view_padding">16dp</dimen> + <dimen name="autofill_icon_size">16dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/CredentialManager/res/values/styles.xml b/packages/CredentialManager/res/values/styles.xml new file mode 100644 index 000000000000..4a5761acdd83 --- /dev/null +++ b/packages/CredentialManager/res/values/styles.xml @@ -0,0 +1,38 @@ +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <style name="autofill.TextAppearance.Small" parent="@style/autofill.TextAppearance"> + <item name="android:textSize">12sp</item> + </style> + + + <style name="autofill.Dataset" parent=""> + <item name="android:background">@drawable/autofill_light_selectable_item_background</item> + </style> + + <style name="autofill.TextAppearance" parent=""> + <item name="android:textColor">@color/autofill_light_textColorPrimary</item> + <item name="android:textColorHint">@color/autofill_light_textColorHint</item> + <item name="android:textColorHighlight">@color/autofill_light_textColorHighlight</item> + <item name="android:textColorLink">@color/autofill_light_textColorLink</item> + <item name="android:textSize">14sp</item> + </style> + + <style name="autofill.TextAppearance.Primary"> + <item name="android:textColor">@color/autofill_light_textColorPrimary</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt index 20d2f09ced8f..0ff1c7fd8953 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt @@ -16,6 +16,7 @@ package com.android.credentialmanager.autofill +import android.R import android.app.assist.AssistStructure import android.content.Context import android.credentials.CredentialManager @@ -41,18 +42,19 @@ import android.service.autofill.SaveRequest import android.service.credentials.CredentialProviderService import android.util.Log import android.view.autofill.AutofillId -import org.json.JSONException import android.widget.inline.InlinePresentationSpec import androidx.autofill.inline.v1.InlineSuggestionUi import androidx.credentials.provider.CustomCredentialEntry import androidx.credentials.provider.PasswordCredentialEntry import androidx.credentials.provider.PublicKeyCredentialEntry import com.android.credentialmanager.GetFlowUtils -import com.android.credentialmanager.model.get.CredentialEntryInfo +import com.android.credentialmanager.common.ui.RemoteViewsFactory import com.android.credentialmanager.getflow.ProviderDisplayInfo -import com.android.credentialmanager.model.get.ProviderInfo import com.android.credentialmanager.getflow.toProviderDisplayInfo import com.android.credentialmanager.ktx.credentialEntry +import com.android.credentialmanager.model.get.CredentialEntryInfo +import com.android.credentialmanager.model.get.ProviderInfo +import org.json.JSONException import org.json.JSONObject import java.util.concurrent.Executors @@ -127,9 +129,11 @@ class CredentialAutofillService : AutofillService() { is PasswordCredentialEntry -> { entryIconMap[entry.key + entry.subkey] = credentialEntry.icon } + is PublicKeyCredentialEntry -> { entryIconMap[entry.key + entry.subkey] = credentialEntry.icon } + is CustomCredentialEntry -> { entryIconMap[entry.key + entry.subkey] = credentialEntry.icon } @@ -172,11 +176,11 @@ class CredentialAutofillService : AutofillService() { } private fun processProvidersForAutofillId( - filLRequest: FillRequest, - autofillId: AutofillId, - providerList: List<ProviderInfo>, - entryIconMap: Map<String, Icon>, - fillResponseBuilder: FillResponse.Builder + filLRequest: FillRequest, + autofillId: AutofillId, + providerList: List<ProviderInfo>, + entryIconMap: Map<String, Icon>, + fillResponseBuilder: FillResponse.Builder ): Boolean { if (providerList.isEmpty()) { return false @@ -197,7 +201,7 @@ class CredentialAutofillService : AutofillService() { var i = 0 var datasetAdded = false - providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@ { + providerDisplayInfo.sortedUserNameToCredentialEntryList.forEach usernameLoop@{ val primaryEntry = it.sortedCredentialEntryList.first() val pendingIntent = primaryEntry.pendingIntent val fillInIntent = primaryEntry.fillInIntent @@ -206,37 +210,48 @@ class CredentialAutofillService : AutofillService() { Log.e(TAG, "PendingIntent was missing from the entry.") return@usernameLoop } - if (inlinePresentationSpecs == null || i >= maxItemCount) { + if (inlinePresentationSpecs == null) { + Log.i(TAG, "Inline presentation spec is null, " + + "building dropdown presentation only") + } + if (i >= maxItemCount) { Log.e(TAG, "Skipping because reached the max item count.") return@usernameLoop } - // Create inline presentation - val spec: InlinePresentationSpec - if (i < inlinePresentationSpecsCount) { - spec = inlinePresentationSpecs[i] - } else { - spec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1] - } - val sliceBuilder = InlineSuggestionUi - .newContentBuilder(pendingIntent) - .setTitle(primaryEntry.userName) - val icon: Icon - if (primaryEntry.icon == null) { + val icon: Icon = if (primaryEntry.icon == null) { // The empty entry icon has non-null icon reference but null drawable reference. // If the drawable reference is null, then use the default icon. - icon = getDefaultIcon() + getDefaultIcon() } else { - icon = entryIconMap[primaryEntry.entryKey + primaryEntry.entrySubkey] + entryIconMap[primaryEntry.entryKey + primaryEntry.entrySubkey] ?: getDefaultIcon() } - sliceBuilder.setStartIcon(icon) - val inlinePresentation = InlinePresentation( - sliceBuilder.build().slice, spec, /* pinned= */ false) + // Create inline presentation + var inlinePresentation: InlinePresentation? = null; + if (inlinePresentationSpecs != null) { + val spec: InlinePresentationSpec + if (i < inlinePresentationSpecsCount) { + spec = inlinePresentationSpecs[i] + } else { + spec = inlinePresentationSpecs[inlinePresentationSpecsCount - 1] + } + val sliceBuilder = InlineSuggestionUi + .newContentBuilder(pendingIntent) + .setTitle(primaryEntry.userName) + sliceBuilder.setStartIcon(icon) + inlinePresentation = InlinePresentation( + sliceBuilder.build().slice, spec, /* pinned= */ false) + } + val dropdownPresentation = RemoteViewsFactory.createDropdownPresentation( + this, icon, primaryEntry) i++ val dataSetBuilder = Dataset.Builder() val presentationBuilder = Presentations.Builder() - .setInlinePresentation(inlinePresentation) + .setMenuPresentation(dropdownPresentation) + if (inlinePresentation != null) { + presentationBuilder.setInlinePresentation(inlinePresentation) + } fillResponseBuilder.addDataset( dataSetBuilder @@ -305,7 +320,7 @@ class CredentialAutofillService : AutofillService() { ): MutableMap<AutofillId, MutableList<CredentialEntryInfo>> { val autofillIdToCredentialEntries: MutableMap<AutofillId, MutableList<CredentialEntryInfo>> = mutableMapOf() - credentialEntryList.forEach entryLoop@ { credentialEntry -> + credentialEntryList.forEach entryLoop@{ credentialEntry -> val autofillId: AutofillId? = credentialEntry .fillInIntent ?.getParcelableExtra( @@ -323,8 +338,8 @@ class CredentialAutofillService : AutofillService() { } private fun copyProviderInfo( - providerInfo: ProviderInfo, - credentialList: List<CredentialEntryInfo> + providerInfo: ProviderInfo, + credentialList: List<CredentialEntryInfo> ): ProviderInfo { return ProviderInfo( providerInfo.id, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt new file mode 100644 index 000000000000..4dc7f00c1550 --- /dev/null +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/RemoteViewsFactory.kt @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2023 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.credentialmanager.common.ui + +import android.content.Context +import android.content.res.Configuration +import android.widget.RemoteViews +import androidx.core.content.ContextCompat +import com.android.credentialmanager.model.get.CredentialEntryInfo +import android.graphics.drawable.Icon + +class RemoteViewsFactory { + + companion object { + private const val setAdjustViewBoundsMethodName = "setAdjustViewBounds" + private const val setMaxHeightMethodName = "setMaxHeight" + private const val setBackgroundResourceMethodName = "setBackgroundResource" + + fun createDropdownPresentation( + context: Context, + icon: Icon, + credentialEntryInfo: CredentialEntryInfo + ): RemoteViews { + val padding = context.resources.getDimensionPixelSize(com.android + .credentialmanager.R.dimen.autofill_view_padding) + var layoutId: Int = com.android.credentialmanager.R.layout + .autofill_dataset_left_with_item_tag_hint + val remoteViews = RemoteViews(context.packageName, layoutId) + setRemoteViewsPaddings(remoteViews, padding) + val textColorPrimary = getTextColorPrimary(isDarkMode(context), context); + remoteViews.setTextColor(android.R.id.text1, textColorPrimary); + remoteViews.setTextViewText(android.R.id.text1, credentialEntryInfo.userName) + + remoteViews.setImageViewIcon(android.R.id.icon1, icon); + remoteViews.setBoolean( + android.R.id.icon1, setAdjustViewBoundsMethodName, true); + remoteViews.setInt( + android.R.id.icon1, + setMaxHeightMethodName, + context.resources.getDimensionPixelSize( + com.android.credentialmanager.R.dimen.autofill_icon_size)); + val drawableId = if (isDarkMode(context)) + com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one_dark + else com.android.credentialmanager.R.drawable.fill_dialog_dynamic_list_item_one + remoteViews.setInt( + android.R.id.content, setBackgroundResourceMethodName, drawableId); + return remoteViews + } + + private fun setRemoteViewsPaddings( + remoteViews: RemoteViews, + padding: Int) { + val halfPadding = padding / 2 + remoteViews.setViewPadding( + android.R.id.text1, + halfPadding, + halfPadding, + halfPadding, + halfPadding) + } + + private fun isDarkMode(context: Context): Boolean { + val currentNightMode = context.resources.configuration.uiMode and + Configuration.UI_MODE_NIGHT_MASK + return currentNightMode == Configuration.UI_MODE_NIGHT_YES + } + + private fun getTextColorPrimary(darkMode: Boolean, context: Context): Int { + return if (darkMode) ContextCompat.getColor( + context, com.android.credentialmanager.R.color.text_primary_dark_mode) + else ContextCompat.getColor(context, com.android.credentialmanager.R.color.text_primary) + } + } +} diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml index 557fbf21d5c4..e6122a094707 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml @@ -43,7 +43,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> <FrameLayout - android:id="@+id/status_view_media_container" + android:id="@id/status_view_media_container" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="@dimen/qs_media_padding" diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index d511caba941b..80725c2bd4b5 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -225,6 +225,8 @@ <item type="id" name="communal_tutorial_indicator" /> <item type="id" name="nssl_placeholder_barrier_bottom" /> <item type="id" name="ambient_indication_container" /> + <item type="id" name="status_view_media_container" /> + <item type="id" name="smart_space_barrier_bottom" /> <!-- Privacy dialog --> <item type="id" name="privacy_dialog_close_app_button" /> diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 83d415fa936e..ab23564a1df4 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -403,13 +403,6 @@ public class AuthContainerView extends LinearLayout final BiometricPromptLayout view = (BiometricPromptLayout) layoutInflater.inflate( R.layout.biometric_prompt_layout, null, false); - /** - * View is only set visible in BiometricViewSizeBinder once PromptSize is determined - * that accounts for iconView size, to prevent prompt resizing being visible to the - * user. - * TODO(b/288175072): May be able to remove this once constraint layout is implemented - */ - view.setVisibility(View.INVISIBLE); mBiometricView = BiometricViewBinder.bind(view, viewModel, mPanelController, // TODO(b/201510778): This uses the wrong timeout in some cases getJankListener(view, TRANSIT, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt index a7fb6f72a41e..90e4a3821634 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt @@ -97,13 +97,7 @@ object BiometricViewBinder { val iconOverlayView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon_overlay) val iconView = view.requireViewById<LottieAnimationView>(R.id.biometric_icon) - /** - * View is only set visible in BiometricViewSizeBinder once PromptSize is determined that - * accounts for iconView size, to prevent prompt resizing being visible to the user. - * - * TODO(b/288175072): May be able to remove this once constraint layout is implemented - */ - iconView.addLottieOnCompositionLoadedListener { viewModel.setIsIconViewLoaded(true) } + PromptIconViewBinder.bind( iconView, iconOverlayView, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt index f340bd81f951..7e16d1e1d668 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt @@ -30,6 +30,7 @@ import androidx.core.animation.addListener import androidx.core.view.doOnLayout import androidx.core.view.isGone import androidx.lifecycle.lifecycleScope +import com.android.systemui.res.R import com.android.systemui.biometrics.AuthPanelController import com.android.systemui.biometrics.Utils import com.android.systemui.biometrics.ui.BiometricPromptLayout @@ -40,8 +41,6 @@ import com.android.systemui.biometrics.ui.viewmodel.isMedium import com.android.systemui.biometrics.ui.viewmodel.isNullOrNotSmall import com.android.systemui.biometrics.ui.viewmodel.isSmall import com.android.systemui.lifecycle.repeatWhenAttached -import com.android.systemui.res.R -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch /** Helper for [BiometricViewBinder] to handle resize transitions. */ @@ -93,22 +92,8 @@ object BiometricViewSizeBinder { // TODO(b/251476085): migrate the legacy panel controller and simplify this view.repeatWhenAttached { var currentSize: PromptSize? = null - lifecycleScope.launch { - /** - * View is only set visible in BiometricViewSizeBinder once PromptSize is - * determined that accounts for iconView size, to prevent prompt resizing being - * visible to the user. - * - * TODO(b/288175072): May be able to remove isIconViewLoaded once constraint - * layout is implemented - */ - combine(viewModel.isIconViewLoaded, viewModel.size, ::Pair).collect { - (isIconViewLoaded, size) -> - if (!isIconViewLoaded) { - return@collect - } - + viewModel.size.collect { size -> // prepare for animated size transitions for (v in viewsToHideWhenSmall) { v.showTextOrHide(forceHide = size.isSmall) @@ -211,9 +196,8 @@ object BiometricViewSizeBinder { } } } + currentSize = size - view.visibility = View.VISIBLE - viewModel.setIsIconViewLoaded(false) notifyAccessibilityChanged() } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt index d899827ebb2e..6d0a58e202bd 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt @@ -192,28 +192,6 @@ constructor( val iconViewModel: PromptIconViewModel = PromptIconViewModel(this, displayStateInteractor, promptSelectorInteractor) - private val _isIconViewLoaded = MutableStateFlow(false) - - /** - * For prompts with an iconView, false until the prompt's iconView animation has been loaded in - * the view, otherwise true by default. Used for BiometricViewSizeBinder to wait for the icon - * asset to be loaded before determining the prompt size. - */ - val isIconViewLoaded: Flow<Boolean> = - combine(credentialKind, _isIconViewLoaded.asStateFlow()) { credentialKind, isIconViewLoaded - -> - if (credentialKind is PromptKind.Biometric) { - isIconViewLoaded - } else { - true - } - } - - // Sets whether the prompt's iconView animation has been loaded in the view yet. - fun setIsIconViewLoaded(iconViewLoaded: Boolean) { - _isIconViewLoaded.value = iconViewLoaded - } - /** Padding for prompt UI elements */ val promptPadding: Flow<Rect> = combine(size, displayStateInteractor.currentRotation) { size, rotation -> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 1f69cc0a8ec3..0d405119f25b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -397,8 +397,10 @@ public class KeyguardSliceProvider extends SliceProvider implements IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_DATE_CHANGED); filter.addAction(Intent.ACTION_LOCALE_CHANGED); - getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/, - null /* scheduler */); + mBgHandler.post(() -> { + getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/, + null /* scheduler */); + }); mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback); mRegistered = true; } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt index 16539db648bc..5344696b92fe 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt @@ -33,6 +33,7 @@ import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsMod import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeClockSection import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines +import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeMediaSection import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeNotificationStackScrollLayoutSection import com.android.systemui.util.kotlin.getOrNull import java.util.Optional @@ -63,6 +64,7 @@ constructor( communalTutorialIndicatorSection: CommunalTutorialIndicatorSection, smartspaceSection: SmartspaceSection, clockSection: SplitShadeClockSection, + mediaSection: SplitShadeMediaSection, ) : KeyguardBlueprint { override val id: String = ID @@ -81,6 +83,7 @@ constructor( aodBurnInSection, communalTutorialIndicatorSection, clockSection, + mediaSection, defaultDeviceEntrySection, // Add LAST: Intentionally has z-order above other views. ) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt new file mode 100644 index 000000000000..5afdbaa47d95 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeMediaSection.kt @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.ui.view.layout.sections + +import android.content.Context +import android.view.View +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT +import android.widget.FrameLayout +import androidx.constraintlayout.widget.Barrier +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet +import androidx.constraintlayout.widget.ConstraintSet.BOTTOM +import androidx.constraintlayout.widget.ConstraintSet.END +import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT +import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID +import androidx.constraintlayout.widget.ConstraintSet.START +import androidx.constraintlayout.widget.ConstraintSet.TOP +import com.android.systemui.Flags.migrateClocksToBlueprint +import com.android.systemui.keyguard.shared.model.KeyguardSection +import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel +import com.android.systemui.media.controls.ui.KeyguardMediaController +import com.android.systemui.res.R +import com.android.systemui.shade.NotificationPanelView +import javax.inject.Inject + +/** Aligns media on left side for split shade, below smartspace, date, and weather. */ +class SplitShadeMediaSection +@Inject +constructor( + private val context: Context, + private val notificationPanelView: NotificationPanelView, + private val keyguardSmartspaceViewModel: KeyguardSmartspaceViewModel, + private val keyguardMediaController: KeyguardMediaController +) : KeyguardSection() { + private val mediaContainerId = R.id.status_view_media_container + private val smartSpaceBarrier = R.id.smart_space_barrier_bottom + + override fun addViews(constraintLayout: ConstraintLayout) { + if (!migrateClocksToBlueprint()) { + return + } + + notificationPanelView.findViewById<View>(mediaContainerId)?.let { + notificationPanelView.removeView(it) + } + + val mediaFrame = + FrameLayout(context, null).apply { + id = mediaContainerId + val padding = context.resources.getDimensionPixelSize(R.dimen.qs_media_padding) + val horizontalPadding = + padding + + context.resources.getDimensionPixelSize( + R.dimen.status_view_margin_horizontal + ) + + setPaddingRelative(horizontalPadding, padding, horizontalPadding, padding) + } + constraintLayout.addView(mediaFrame) + keyguardMediaController.attachSplitShadeContainer(mediaFrame) + } + + override fun bindData(constraintLayout: ConstraintLayout) {} + + override fun applyConstraints(constraintSet: ConstraintSet) { + if (!migrateClocksToBlueprint()) { + return + } + + constraintSet.apply { + constrainWidth(mediaContainerId, MATCH_CONSTRAINT) + constrainHeight(mediaContainerId, WRAP_CONTENT) + + createBarrier( + smartSpaceBarrier, + Barrier.BOTTOM, + 0, + *intArrayOf( + keyguardSmartspaceViewModel.smartspaceViewId, + keyguardSmartspaceViewModel.dateId, + keyguardSmartspaceViewModel.weatherId, + ) + ) + connect(mediaContainerId, TOP, smartSpaceBarrier, BOTTOM) + connect(mediaContainerId, START, PARENT_ID, START) + connect(mediaContainerId, END, R.id.split_shade_guideline, END) + } + } + + override fun removeViews(constraintLayout: ConstraintLayout) { + if (!migrateClocksToBlueprint()) { + return + } + + constraintLayout.removeView(mediaContainerId) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt index 945bf9a5c0b2..e15e03822610 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt @@ -27,6 +27,7 @@ import android.view.View import android.view.ViewGroup import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable +import com.android.systemui.Flags.migrateClocksToBlueprint import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager @@ -180,7 +181,11 @@ constructor( /** Called whenever the media hosts visibility changes */ private fun onMediaHostVisibilityChanged(visible: Boolean) { refreshMediaPosition(reason = "onMediaHostVisibilityChanged") + if (visible) { + if (migrateClocksToBlueprint() && useSplitShade) { + return + } mediaHost.hostView.layoutParams.apply { height = ViewGroup.LayoutParams.WRAP_CONTENT width = ViewGroup.LayoutParams.MATCH_PARENT diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 17eb3c83fefe..286037ef1961 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -1160,9 +1160,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump // Occluded->Lockscreen collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(), mOccludedToLockscreenTransition, mMainDispatcher); - if (!KeyguardShadeMigrationNssl.isEnabled()) { - collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(), + collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(), setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); + if (!KeyguardShadeMigrationNssl.isEnabled()) { collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenTranslationY(), setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); @@ -1192,8 +1192,10 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mLockscreenToOccludedTransition, mMainDispatcher); collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(), setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); - collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenTranslationY(), - setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); + if (!KeyguardShadeMigrationNssl.isEnabled()) { + collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenTranslationY(), + setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); + } // Primary bouncer->Gone (ensures lockscreen content is not visible on successful auth) collectFlow(mView, mPrimaryBouncerToGoneTransitionViewModel.getLockscreenAlpha(), @@ -1463,6 +1465,9 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump } private void attachSplitShadeMediaPlayerContainer(FrameLayout container) { + if (migrateClocksToBlueprint()) { + return; + } mKeyguardMediaController.attachSplitShadeContainer(container); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java index 70ccc4f3ae43..80ef14bb4673 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.collection.inflation; +import static com.android.systemui.Flags.screenshareNotificationHiding; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_EXPANDED; import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC; @@ -243,7 +244,11 @@ public class NotificationRowBinderImpl implements NotificationRowBinder { params.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight); params.setUseLowPriority(isLowPriority); - if (mNotificationLockscreenUserManager.needsRedaction(entry)) { + // If screenshareNotificationHiding is enabled, both public and private views should be + // inflated to avoid any latency associated with reinflating all notification views when + // screen share starts and stops + if (screenshareNotificationHiding() + || mNotificationLockscreenUserManager.needsRedaction(entry)) { params.requireContentViews(FLAG_CONTENT_VIEW_PUBLIC); } else { params.markContentViewsFreeable(FLAG_CONTENT_VIEW_PUBLIC); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index eff91e55d9a8..5ee38bebc99b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -25,6 +25,8 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE_LOCKED +import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING +import com.android.systemui.keyguard.shared.model.TransitionState.STARTED import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -71,6 +73,20 @@ constructor( KeyguardState.PRIMARY_BOUNCER ) + private val lockscreenToOccludedRunning = + keyguardTransitionInteractor + .transition(KeyguardState.LOCKSCREEN, KeyguardState.OCCLUDED) + .map { it.transitionState == STARTED || it.transitionState == RUNNING } + .distinctUntilChanged() + .onStart { emit(false) } + + private val occludedToLockscreenRunning = + keyguardTransitionInteractor + .transition(KeyguardState.OCCLUDED, KeyguardState.LOCKSCREEN) + .map { it.transitionState == STARTED || it.transitionState == RUNNING } + .distinctUntilChanged() + .onStart { emit(false) } + val shadeCollapseFadeInComplete = MutableStateFlow(false) val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> = @@ -122,7 +138,11 @@ constructor( ) { isKeyguard, isShadeVisible, qsExpansion -> isKeyguard && !(isShadeVisible || qsExpansion) } - .distinctUntilChanged() + .stateIn( + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = false, + ) /** Fade in only for use after the shade collapses */ val shadeCollpaseFadeIn: Flow<Boolean> = @@ -182,26 +202,37 @@ constructor( ) val alpha: Flow<Float> = - isOnLockscreenWithoutShade - .flatMapLatest { isOnLockscreenWithoutShade -> - combineTransform( - merge( - occludedToLockscreenTransitionViewModel.lockscreenAlpha, - lockscreenToOccludedTransitionViewModel.lockscreenAlpha, - keyguardInteractor.keyguardAlpha, - ), - shadeCollpaseFadeIn, - ) { alpha, shadeCollpaseFadeIn -> - if (isOnLockscreenWithoutShade) { - if (!shadeCollpaseFadeIn) { - emit(alpha) - } + // Due to issues with the legacy shade, some shade expansion events are sent incorrectly, + // such as when the shade resets. This can happen while the LOCKSCREEN<->OCCLUDED transition + // is running. Therefore use a series of flatmaps to prevent unwanted interruptions while + // those transitions are in progress. Without this, the alpha value will produce a visible + // flicker. + lockscreenToOccludedRunning.flatMapLatest { isLockscreenToOccludedRunning -> + if (isLockscreenToOccludedRunning) { + lockscreenToOccludedTransitionViewModel.lockscreenAlpha + } else { + occludedToLockscreenRunning.flatMapLatest { isOccludedToLockscreenRunning -> + if (isOccludedToLockscreenRunning) { + occludedToLockscreenTransitionViewModel.lockscreenAlpha.onStart { emit(0f) } } else { - emit(1f) + isOnLockscreenWithoutShade.flatMapLatest { isOnLockscreenWithoutShade -> + combineTransform( + keyguardInteractor.keyguardAlpha, + shadeCollpaseFadeIn, + ) { alpha, shadeCollpaseFadeIn -> + if (isOnLockscreenWithoutShade) { + if (!shadeCollpaseFadeIn) { + emit(alpha) + } + } else { + emit(1f) + } + } + } } } } - .distinctUntilChanged() + } /** * Under certain scenarios, such as swiping up on the lockscreen, the container will need to be diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java index 5e57c832074e..bc50c25a77a0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceTest.java @@ -198,7 +198,7 @@ public class PluginInstanceTest extends SysuiTestCase { AtomicBoolean isBgThreadFailed = new AtomicBoolean(false); Thread bgThread = new Thread(() -> { assertTrue(getLock(unloadLock, 10)); - assertTrue(getLock(loadLock, 3000)); // Wait for the foreground thread + assertTrue(getLock(loadLock, 4000)); // Wait for the foreground thread assertNotNull(mPluginInstance.getPlugin()); // Attempt to delete the plugin, this should block until the load completes mPluginInstance.unloadPlugin(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt index 36a471238c8a..20020f23b2df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.notification.stack.domain.interactor.share import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -418,13 +419,13 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { } @Test - fun shadeCollpaseFadeIn() = + fun shadeCollapseFadeIn() = testScope.runTest { + val fadeIn by collectLastValue(underTest.shadeCollpaseFadeIn) + // Start on lockscreen without the shade underTest.setShadeCollapseFadeInComplete(false) showLockscreen() - - val fadeIn by collectLastValue(underTest.shadeCollpaseFadeIn) assertThat(fadeIn).isEqualTo(false) // ... then the shade expands @@ -440,10 +441,12 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { assertThat(fadeIn).isEqualTo(false) } - private suspend fun showLockscreen() { + private suspend fun TestScope.showLockscreen() { shadeRepository.setLockscreenShadeExpansion(0f) shadeRepository.setQsExpansion(0f) + runCurrent() keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD) + runCurrent() keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.AOD, to = KeyguardState.LOCKSCREEN, @@ -451,10 +454,12 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { ) } - private suspend fun showLockscreenWithShadeExpanded() { + private suspend fun TestScope.showLockscreenWithShadeExpanded() { shadeRepository.setLockscreenShadeExpansion(1f) shadeRepository.setQsExpansion(0f) + runCurrent() keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED) + runCurrent() keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.AOD, to = KeyguardState.LOCKSCREEN, @@ -462,10 +467,12 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { ) } - private suspend fun showLockscreenWithQSExpanded() { + private suspend fun TestScope.showLockscreenWithQSExpanded() { shadeRepository.setLockscreenShadeExpansion(0f) shadeRepository.setQsExpansion(1f) + runCurrent() keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED) + runCurrent() keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.AOD, to = KeyguardState.LOCKSCREEN, diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 96b1650d9575..02f4485d5b40 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -2118,11 +2118,6 @@ public final class ActiveServices { // anyway, so we just remove the SHORT_SERVICE type. foregroundServiceType &= ~FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; } - if (!shouldAllowBootCompletedStart(r, foregroundServiceType)) { - throw new ForegroundServiceStartNotAllowedException("FGS type " - + ServiceInfo.foregroundServiceTypeToLabel(foregroundServiceType) - + " not allowed to start from BOOT_COMPLETED!"); - } boolean alreadyStartedOp = false; boolean stopProcStatsOp = false; @@ -2137,6 +2132,12 @@ public final class ActiveServices { mServiceFGAnrTimer.cancel(r); } + if (!shouldAllowBootCompletedStart(r, foregroundServiceType)) { + throw new ForegroundServiceStartNotAllowedException("FGS type " + + ServiceInfo.foregroundServiceTypeToLabel(foregroundServiceType) + + " not allowed to start from BOOT_COMPLETED!"); + } + final ProcessServiceRecord psr = r.app.mServices; try { boolean ignoreForeground = false; diff --git a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java index 4df25811cc99..5d609bca334c 100644 --- a/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java +++ b/services/core/java/com/android/server/biometrics/AuthenticationStatsCollector.java @@ -54,8 +54,8 @@ public class AuthenticationStatsCollector { @NonNull private final Context mContext; @NonNull private final PackageManager mPackageManager; - @NonNull private final FaceManager mFaceManager; - @NonNull private final FingerprintManager mFingerprintManager; + @Nullable private final FaceManager mFaceManager; + @Nullable private final FingerprintManager mFingerprintManager; private final boolean mEnabled; private final float mThreshold; @@ -197,11 +197,11 @@ public class AuthenticationStatsCollector { } private boolean hasEnrolledFace(int userId) { - return mFaceManager.hasEnrolledTemplates(userId); + return mFaceManager != null && mFaceManager.hasEnrolledTemplates(userId); } private boolean hasEnrolledFingerprint(int userId) { - return mFingerprintManager.hasEnrolledTemplates(userId); + return mFingerprintManager != null && mFingerprintManager.hasEnrolledTemplates(userId); } /** diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java index e546f42c9c22..1660c3ef952a 100644 --- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java +++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java @@ -21,11 +21,13 @@ import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled; import android.Manifest; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; import android.app.role.RoleManager; import android.content.Context; import android.content.pm.PackageManager; +import android.content.pm.UserInfo; import android.os.Binder; import android.os.BugreportManager.BugreportCallback; import android.os.BugreportParams; @@ -37,7 +39,6 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; -import android.os.UserManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArrayMap; @@ -95,7 +96,6 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { private static final long DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS = 30 * 1000; private final Object mLock = new Object(); - private final Injector mInjector; private final Context mContext; private final AppOpsManager mAppOps; private final TelephonyManager mTelephonyManager; @@ -346,14 +346,6 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { AtomicFile getMappingFile() { return mMappingFile; } - - UserManager getUserManager() { - return mContext.getSystemService(UserManager.class); - } - - DevicePolicyManager getDevicePolicyManager() { - return mContext.getSystemService(DevicePolicyManager.class); - } } BugreportManagerServiceImpl(Context context) { @@ -365,7 +357,6 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) BugreportManagerServiceImpl(Injector injector) { - mInjector = injector; mContext = injector.getContext(); mAppOps = mContext.getSystemService(AppOpsManager.class); mTelephonyManager = mContext.getSystemService(TelephonyManager.class); @@ -398,7 +389,12 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { int callingUid = Binder.getCallingUid(); enforcePermission(callingPackage, callingUid, bugreportMode == BugreportParams.BUGREPORT_MODE_TELEPHONY /* checkCarrierPrivileges */); - ensureUserCanTakeBugReport(bugreportMode); + final long identity = Binder.clearCallingIdentity(); + try { + ensureUserCanTakeBugReport(bugreportMode); + } finally { + Binder.restoreCallingIdentity(identity); + } Slogf.i(TAG, "Starting bugreport for %s / %d", callingPackage, callingUid); synchronized (mLock) { @@ -437,6 +433,7 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { @RequiresPermission(value = Manifest.permission.DUMP, conditional = true) public void retrieveBugreport(int callingUidUnused, String callingPackage, int userId, FileDescriptor bugreportFd, String bugreportFile, + boolean keepBugreportOnRetrievalUnused, IDumpstateListener listener) { int callingUid = Binder.getCallingUid(); enforcePermission(callingPackage, callingUid, false); @@ -568,48 +565,54 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { } /** - * Validates that the calling user is an admin user or, when bugreport is requested remotely - * that the user is an affiliated user. + * Validates that the current user is an admin user or, when bugreport is requested remotely + * that the current user is an affiliated user. * - * @throws IllegalArgumentException if the calling user is not an admin user + * @throws IllegalArgumentException if the current user is not an admin user */ private void ensureUserCanTakeBugReport(int bugreportMode) { - // Get the calling userId before clearing the caller identity. - int callingUserId = UserHandle.getUserId(Binder.getCallingUid()); - boolean isAdminUser = false; - final long identity = Binder.clearCallingIdentity(); + UserInfo currentUser = null; try { - isAdminUser = mInjector.getUserManager().isUserAdmin(callingUserId); - } finally { - Binder.restoreCallingIdentity(identity); + currentUser = ActivityManager.getService().getCurrentUser(); + } catch (RemoteException e) { + // Impossible to get RemoteException for an in-process call. + } + + if (currentUser == null) { + logAndThrow("There is no current user, so no bugreport can be requested."); } - if (!isAdminUser) { + + if (!currentUser.isAdmin()) { if (bugreportMode == BugreportParams.BUGREPORT_MODE_REMOTE - && isUserAffiliated(callingUserId)) { + && isCurrentUserAffiliated(currentUser.id)) { return; } - logAndThrow(TextUtils.formatSimple("Calling user %s is not an admin user." - + " Only admin users are allowed to take bugreport.", callingUserId)); + logAndThrow(TextUtils.formatSimple("Current user %s is not an admin user." + + " Only admin users are allowed to take bugreport.", currentUser.id)); } } /** - * Returns {@code true} if the device has device owner and the specified user is affiliated + * Returns {@code true} if the device has device owner and the current user is affiliated * with the device owner. */ - private boolean isUserAffiliated(int userId) { - DevicePolicyManager dpm = mInjector.getDevicePolicyManager(); + private boolean isCurrentUserAffiliated(int currentUserId) { + DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); int deviceOwnerUid = dpm.getDeviceOwnerUserId(); if (deviceOwnerUid == UserHandle.USER_NULL) { return false; } - if (DEBUG) { - Slog.d(TAG, "callingUid: " + userId + " deviceOwnerUid: " + deviceOwnerUid); - } + int callingUserId = UserHandle.getUserId(Binder.getCallingUid()); - if (userId != deviceOwnerUid && !dpm.isAffiliatedUser(userId)) { - logAndThrow("User " + userId + " is not affiliated to the device owner."); + Slog.i(TAG, "callingUid: " + callingUserId + " deviceOwnerUid: " + deviceOwnerUid + + " currentUserId: " + currentUserId); + + if (callingUserId != deviceOwnerUid) { + logAndThrow("Caller is not device owner on provisioned device."); + } + if (!dpm.isAffiliatedUser(currentUserId)) { + logAndThrow("Current user is not affiliated to the device owner."); } return true; } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 5b13d3fead90..edce3ec4b37c 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -473,6 +473,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { private TalkbackShortcutController mTalkbackShortcutController; + private WindowWakeUpPolicy mWindowWakeUpPolicy; + boolean mSafeMode; // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key. @@ -640,15 +642,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Whether to lock the device after the next dreaming transition has finished. private boolean mLockAfterDreamingTransitionFinished; - // Allowed theater mode wake actions - private boolean mAllowTheaterModeWakeFromKey; - private boolean mAllowTheaterModeWakeFromPowerKey; - private boolean mAllowTheaterModeWakeFromMotion; - private boolean mAllowTheaterModeWakeFromMotionWhenNotDreaming; - private boolean mAllowTheaterModeWakeFromCameraLens; - private boolean mAllowTheaterModeWakeFromLidSwitch; - private boolean mAllowTheaterModeWakeFromWakeGesture; - // If true, the power button long press behavior will be invoked even if the default display is // non-interactive. If false, the power button long press behavior will be skipped if the // default display is non-interactive. @@ -930,8 +923,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (shouldEnableWakeGestureLp()) { performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false, "Wake Up"); - wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture, - PowerManager.WAKE_REASON_GESTURE, "android.policy:GESTURE"); + mWindowWakeUpPolicy.wakeUpFromWakeGesture(); } } } @@ -1067,7 +1059,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { || handledByPowerManager || mKeyCombinationManager.isPowerKeyIntercepted(); if (!mPowerKeyHandled) { if (!interactive) { - wakeUpFromPowerKey(event.getDownTime()); + wakeUpFromWakeKey(event); } } else { // handled by another power key policy. @@ -1309,7 +1301,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0); if (!interactive) { - wakeUpFromPowerKey(eventTime); + wakeUpFromWakeKey(eventTime, KEYCODE_POWER); } } else { Slog.i(TAG, "Toggling theater mode on."); @@ -1325,7 +1317,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MULTI_PRESS_POWER_BRIGHTNESS_BOOST: Slog.i(TAG, "Starting brightness boost."); if (!interactive) { - wakeUpFromPowerKey(eventTime); + wakeUpFromWakeKey(eventTime, KEYCODE_POWER); } mPowerManager.boostScreenBrightness(eventTime); break; @@ -2312,22 +2304,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { mLidNavigationAccessibility = mContext.getResources().getInteger( com.android.internal.R.integer.config_lidNavigationAccessibility); - mAllowTheaterModeWakeFromKey = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_allowTheaterModeWakeFromKey); - mAllowTheaterModeWakeFromPowerKey = mAllowTheaterModeWakeFromKey - || mContext.getResources().getBoolean( - com.android.internal.R.bool.config_allowTheaterModeWakeFromPowerKey); - mAllowTheaterModeWakeFromMotion = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_allowTheaterModeWakeFromMotion); - mAllowTheaterModeWakeFromMotionWhenNotDreaming = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_allowTheaterModeWakeFromMotionWhenNotDreaming); - mAllowTheaterModeWakeFromCameraLens = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_allowTheaterModeWakeFromCameraLens); - mAllowTheaterModeWakeFromLidSwitch = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_allowTheaterModeWakeFromLidSwitch); - mAllowTheaterModeWakeFromWakeGesture = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture); - mGoToSleepOnButtonPressTheaterMode = mContext.getResources().getBoolean( com.android.internal.R.bool.config_goToSleepOnButtonPressTheaterMode); @@ -2457,6 +2433,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.integer.config_keyguardDrawnTimeout); mKeyguardDelegate = injector.getKeyguardServiceDelegate(); mTalkbackShortcutController = injector.getTalkbackShortcutController(); + mWindowWakeUpPolicy = new WindowWakeUpPolicy(mContext); initKeyCombinationRules(); initSingleKeyGestureRules(injector.getLooper()); mButtonOverridePermissionChecker = injector.getButtonOverridePermissionChecker(); @@ -4483,8 +4460,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { updateRotation(true); if (lidOpen) { - wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromLidSwitch, - PowerManager.WAKE_REASON_LID, "android.policy:LID"); + mWindowWakeUpPolicy.wakeUpFromLid(); } else if (getLidBehavior() != LID_BEHAVIOR_SLEEP) { mPowerManager.userActivity(SystemClock.uptimeMillis(), false); } @@ -4510,8 +4486,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } else { intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA); } - wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromCameraLens, - PowerManager.WAKE_REASON_CAMERA_LAUNCH, "android.policy:CAMERA_COVER"); + mWindowWakeUpPolicy.wakeUpFromCameraCover(whenNanos / 1000000); startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF); } mCameraLensCoverState = lensCoverState; @@ -4589,7 +4564,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean shouldTurnOnTv = false; if (down && (keyCode == KeyEvent.KEYCODE_POWER || keyCode == KeyEvent.KEYCODE_TV_POWER)) { - wakeUpFromPowerKey(event.getDownTime()); + wakeUpFromWakeKey(event); shouldTurnOnTv = true; } else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP) && isWakeKeyWhenScreenOff(keyCode)) { @@ -5104,9 +5079,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mRequestedOrSleepingDefaultDisplay) { mCameraGestureTriggeredDuringGoingToSleep = true; // Wake device up early to prevent display doing redundant turning off/on stuff. - wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromPowerKey, - PowerManager.WAKE_REASON_CAMERA_LAUNCH, - "android.policy:CAMERA_GESTURE_PREVENT_LOCK"); + mWindowWakeUpPolicy.wakeUpFromPowerKeyCameraGesture(); } return true; } @@ -5204,8 +5177,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { public int interceptMotionBeforeQueueingNonInteractive(int displayId, int source, int action, long whenNanos, int policyFlags) { if ((policyFlags & FLAG_WAKE) != 0) { - if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion, - PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) { + if (mWindowWakeUpPolicy.wakeUpFromMotion(whenNanos / 1000000)) { // Woke up. Pass motion events to user. return ACTION_PASS_TO_USER; } @@ -5219,8 +5191,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // there will be no dream to intercept the touch and wake into ambient. The device should // wake up in this case. if (isTheaterModeEnabled() && (policyFlags & FLAG_WAKE) != 0) { - if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotionWhenNotDreaming, - PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) { + if (mWindowWakeUpPolicy.wakeUpFromMotion(whenNanos / 1000000)) { // Woke up. Pass motion events to user. return ACTION_PASS_TO_USER; } @@ -5554,39 +5525,24 @@ public class PhoneWindowManager implements WindowManagerPolicy { return sleepDurationRealtime > mWakeUpToLastStateTimeout; } - private void wakeUpFromPowerKey(long eventTime) { - if (wakeUp(eventTime, mAllowTheaterModeWakeFromPowerKey, - PowerManager.WAKE_REASON_POWER_BUTTON, "android.policy:POWER")) { - // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout - if (shouldWakeUpWithHomeIntent()) { - startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ false, /*wakenFromDreams*/ true, - PowerManager.wakeReasonToString(PowerManager.WAKE_REASON_POWER_BUTTON)); - } - } + private void wakeUpFromWakeKey(KeyEvent event) { + wakeUpFromWakeKey(event.getEventTime(), event.getKeyCode()); } - private void wakeUpFromWakeKey(KeyEvent event) { - if (wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, - PowerManager.WAKE_REASON_WAKE_KEY, "android.policy:KEY")) { + private void wakeUpFromWakeKey(long eventTime, int keyCode) { + if (mWindowWakeUpPolicy.wakeUpFromKey(eventTime, keyCode)) { + final boolean keyCanLaunchHome = keyCode == KEYCODE_HOME || keyCode == KEYCODE_POWER; // Start HOME with "reason" extra if sleeping for more than mWakeUpToLastStateTimeout - if (shouldWakeUpWithHomeIntent() && event.getKeyCode() == KEYCODE_HOME) { - startDockOrHome(DEFAULT_DISPLAY, /*fromHomeKey*/ true, /*wakenFromDreams*/ true, - PowerManager.wakeReasonToString(PowerManager.WAKE_REASON_WAKE_KEY)); + if (shouldWakeUpWithHomeIntent() && keyCanLaunchHome) { + startDockOrHome( + DEFAULT_DISPLAY, + /*fromHomeKey*/ keyCode == KEYCODE_HOME, + /*wakenFromDreams*/ true, + "Wake from " + KeyEvent. keyCodeToString(keyCode)); } } } - private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason, - String details) { - final boolean theaterModeEnabled = isTheaterModeEnabled(); - if (!wakeInTheaterMode && theaterModeEnabled) { - return false; - } - - mPowerManager.wakeUp(wakeTime, reason, details); - return true; - } - private void finishKeyguardDrawn() { if (!mDefaultDisplayPolicy.finishKeyguardDrawn()) { return; diff --git a/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java new file mode 100644 index 000000000000..392d0d4fdb52 --- /dev/null +++ b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.policy; + +import static android.os.PowerManager.WAKE_REASON_CAMERA_LAUNCH; +import static android.os.PowerManager.WAKE_REASON_GESTURE; +import static android.os.PowerManager.WAKE_REASON_LID; +import static android.os.PowerManager.WAKE_REASON_POWER_BUTTON; +import static android.os.PowerManager.WAKE_REASON_WAKE_KEY; +import static android.os.PowerManager.WAKE_REASON_WAKE_MOTION; +import static android.view.KeyEvent.KEYCODE_POWER; + +import android.content.Context; +import android.content.res.Resources; +import android.os.PowerManager; +import android.os.PowerManager.WakeReason; +import android.os.SystemClock; +import android.provider.Settings; +import android.util.Slog; + + +/** Policy controlling the decision and execution of window-related wake ups. */ +class WindowWakeUpPolicy { + private static final String TAG = "WindowWakeUpPolicy"; + + private static final boolean DEBUG = false; + + private final Context mContext; + private final PowerManager mPowerManager; + + private final boolean mAllowTheaterModeWakeFromKey; + private final boolean mAllowTheaterModeWakeFromPowerKey; + private final boolean mAllowTheaterModeWakeFromMotion; + private final boolean mAllowTheaterModeWakeFromMotionWhenNotDreaming; + private final boolean mAllowTheaterModeWakeFromCameraLens; + private final boolean mAllowTheaterModeWakeFromLidSwitch; + private final boolean mAllowTheaterModeWakeFromWakeGesture; + + WindowWakeUpPolicy(Context context) { + mContext = context; + mPowerManager = context.getSystemService(PowerManager.class); + + final Resources res = context.getResources(); + mAllowTheaterModeWakeFromKey = res.getBoolean( + com.android.internal.R.bool.config_allowTheaterModeWakeFromKey); + mAllowTheaterModeWakeFromPowerKey = mAllowTheaterModeWakeFromKey + || res.getBoolean( + com.android.internal.R.bool.config_allowTheaterModeWakeFromPowerKey); + mAllowTheaterModeWakeFromMotion = res.getBoolean( + com.android.internal.R.bool.config_allowTheaterModeWakeFromMotion); + mAllowTheaterModeWakeFromMotionWhenNotDreaming = res.getBoolean( + com.android.internal.R.bool.config_allowTheaterModeWakeFromMotionWhenNotDreaming); + mAllowTheaterModeWakeFromCameraLens = res.getBoolean( + com.android.internal.R.bool.config_allowTheaterModeWakeFromCameraLens); + mAllowTheaterModeWakeFromLidSwitch = res.getBoolean( + com.android.internal.R.bool.config_allowTheaterModeWakeFromLidSwitch); + mAllowTheaterModeWakeFromWakeGesture = res.getBoolean( + com.android.internal.R.bool.config_allowTheaterModeWakeFromGesture); + } + + /** + * Wakes up from a key event. + * + * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}. + * @param keyCode the {@link android.view.KeyEvent} key code of the key event. + * @return {@code true} if the policy allows the requested wake up and the request has been + * executed; {@code false} otherwise. + */ + boolean wakeUpFromKey(long eventTime, int keyCode) { + final boolean wakeAllowedDuringTheaterMode = + keyCode == KEYCODE_POWER + ? mAllowTheaterModeWakeFromPowerKey + : mAllowTheaterModeWakeFromKey; + return wakeUp( + eventTime, + wakeAllowedDuringTheaterMode, + keyCode == KEYCODE_POWER ? WAKE_REASON_POWER_BUTTON : WAKE_REASON_WAKE_KEY, + keyCode == KEYCODE_POWER ? "POWER" : "KEY"); + } + + /** + * Wakes up from a motion event. + * + * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}. + * @return {@code true} if the policy allows the requested wake up and the request has been + * executed; {@code false} otherwise. + */ + boolean wakeUpFromMotion(long eventTime) { + return wakeUp( + eventTime, mAllowTheaterModeWakeFromMotion, WAKE_REASON_WAKE_MOTION, "MOTION"); + } + + /** + * Wakes up due to an opened camera cover. + * + * @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}. + * @return {@code true} if the policy allows the requested wake up and the request has been + * executed; {@code false} otherwise. + */ + boolean wakeUpFromCameraCover(long eventTime) { + return wakeUp( + eventTime, + mAllowTheaterModeWakeFromCameraLens, + WAKE_REASON_CAMERA_LAUNCH, + "CAMERA_COVER"); + } + + /** + * Wakes up due to an opened lid. + * + * @return {@code true} if the policy allows the requested wake up and the request has been + * executed; {@code false} otherwise. + */ + boolean wakeUpFromLid() { + return wakeUp( + SystemClock.uptimeMillis(), + mAllowTheaterModeWakeFromLidSwitch, + WAKE_REASON_LID, + "LID"); + } + + /** + * Wakes up to prevent sleeping when opening camera through power button. + * + * @return {@code true} if the policy allows the requested wake up and the request has been + * executed; {@code false} otherwise. + */ + boolean wakeUpFromPowerKeyCameraGesture() { + return wakeUp( + SystemClock.uptimeMillis(), + mAllowTheaterModeWakeFromPowerKey, + WAKE_REASON_CAMERA_LAUNCH, + "CAMERA_GESTURE_PREVENT_LOCK"); + } + + /** + * Wake up from a wake gesture. + * + * @return {@code true} if the policy allows the requested wake up and the request has been + * executed; {@code false} otherwise. + */ + boolean wakeUpFromWakeGesture() { + return wakeUp( + SystemClock.uptimeMillis(), + mAllowTheaterModeWakeFromWakeGesture, + WAKE_REASON_GESTURE, + "GESTURE"); + } + + private boolean wakeUp( + long wakeTime, boolean wakeInTheaterMode, @WakeReason int reason, String details) { + final boolean isTheaterModeEnabled = + Settings.Global.getInt( + mContext.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0) == 1; + if (!wakeInTheaterMode && isTheaterModeEnabled) { + if (DEBUG) Slog.d(TAG, "Unable to wake up from " + details); + return false; + } + mPowerManager.wakeUp(wakeTime, reason, "android.policy:" + details); + return true; + } +} diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index 4a2e1cba5cce..686b2a813bd3 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -65,7 +65,6 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; -import com.android.internal.content.PackageMonitor; import com.android.server.credentials.metrics.ApiName; import com.android.server.credentials.metrics.ApiStatus; import com.android.server.infra.AbstractMasterSystemService; @@ -1204,6 +1203,9 @@ public final class CredentialManagerService // If the app being removed matches any of the package names from // this list then don't add it in the output. Set<String> providers = new HashSet<>(); + if (rawProviders == null || packageName == null) { + return providers; + } for (String rawComponentName : rawProviders.split(":")) { if (TextUtils.isEmpty(rawComponentName) || rawComponentName.equals("null")) { diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java index d2e83e9b0708..9eeb4f3f218f 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthenticationStatsCollectorTest.java @@ -271,6 +271,7 @@ public class AuthenticationStatsCollectorTest { .thenReturn(true); when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false); when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true); + when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(null); mAuthenticationStatsCollector.authenticate(USER_ID_1, false /* authenticated */); diff --git a/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java index fd1abff8610b..d850c73ebc26 100644 --- a/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java @@ -53,6 +53,12 @@ public final class CredentialManagerServiceTest { } @Test + public void getStoredProviders_nullValue_success() { + Set<String> providers = CredentialManagerService.getStoredProviders(null, null); + assertThat(providers.size()).isEqualTo(0); + } + + @Test public void getStoredProviders_success() { Set<String> providers = CredentialManagerService.getStoredProviders( diff --git a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java index 21b8a94d17fb..dc1d2c5e54b6 100644 --- a/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java +++ b/services/tests/servicestests/src/com/android/server/os/BugreportManagerServiceImplTest.java @@ -16,26 +16,23 @@ package com.android.server.os; +import android.app.admin.flags.Flags; +import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled; + import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.when; -import android.app.admin.DevicePolicyManager; -import android.app.admin.flags.Flags; import android.app.role.RoleManager; import android.content.Context; import android.os.Binder; import android.os.BugreportManager.BugreportCallback; -import android.os.BugreportParams; import android.os.IBinder; import android.os.IDumpstateListener; import android.os.Process; import android.os.RemoteException; -import android.os.UserManager; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; @@ -51,8 +48,6 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import java.io.FileDescriptor; import java.util.concurrent.CompletableFuture; @@ -71,11 +66,6 @@ public class BugreportManagerServiceImplTest { private BugreportManagerServiceImpl mService; private BugreportManagerServiceImpl.BugreportFileManager mBugreportFileManager; - @Mock - private UserManager mMockUserManager; - @Mock - private DevicePolicyManager mMockDevicePolicyManager; - private int mCallingUid = 1234; private String mCallingPackage = "test.package"; private AtomicFile mMappingFile; @@ -85,17 +75,14 @@ public class BugreportManagerServiceImplTest { @Before public void setUp() { - MockitoAnnotations.initMocks(this); mContext = InstrumentationRegistry.getInstrumentation().getContext(); mMappingFile = new AtomicFile(mContext.getFilesDir(), "bugreport-mapping.xml"); ArraySet<String> mAllowlistedPackages = new ArraySet<>(); mAllowlistedPackages.add(mContext.getPackageName()); mService = new BugreportManagerServiceImpl( - new TestInjector(mContext, mAllowlistedPackages, mMappingFile, - mMockUserManager, mMockDevicePolicyManager)); + new BugreportManagerServiceImpl.Injector(mContext, mAllowlistedPackages, + mMappingFile)); mBugreportFileManager = new BugreportManagerServiceImpl.BugreportFileManager(mMappingFile); - // The calling user is an admin user by default. - when(mMockUserManager.isUserAdmin(anyInt())).thenReturn(true); } @After @@ -178,36 +165,6 @@ public class BugreportManagerServiceImplTest { } @Test - public void testStartBugreport_throwsForNonAdminUser() throws Exception { - when(mMockUserManager.isUserAdmin(anyInt())).thenReturn(false); - - Exception thrown = assertThrows(Exception.class, - () -> mService.startBugreport(mCallingUid, mContext.getPackageName(), - new FileDescriptor(), /* screenshotFd= */ null, - BugreportParams.BUGREPORT_MODE_FULL, - /* flags= */ 0, new Listener(new CountDownLatch(1)), - /* isScreenshotRequested= */ false)); - - assertThat(thrown.getMessage()).contains("not an admin user"); - } - - @Test - public void testStartBugreport_throwsForNotAffiliatedUser() throws Exception { - when(mMockUserManager.isUserAdmin(anyInt())).thenReturn(false); - when(mMockDevicePolicyManager.getDeviceOwnerUserId()).thenReturn(-1); - when(mMockDevicePolicyManager.isAffiliatedUser(anyInt())).thenReturn(false); - - Exception thrown = assertThrows(Exception.class, - () -> mService.startBugreport(mCallingUid, mContext.getPackageName(), - new FileDescriptor(), /* screenshotFd= */ null, - BugreportParams.BUGREPORT_MODE_REMOTE, - /* flags= */ 0, new Listener(new CountDownLatch(1)), - /* isScreenshotRequested= */ false)); - - assertThat(thrown.getMessage()).contains("not affiliated to the device owner"); - } - - @Test public void testRetrieveBugreportWithoutFilesForCaller() throws Exception { CountDownLatch latch = new CountDownLatch(1); Listener listener = new Listener(latch); @@ -250,8 +207,7 @@ public class BugreportManagerServiceImplTest { private void clearAllowlist() { mService = new BugreportManagerServiceImpl( - new TestInjector(mContext, new ArraySet<>(), mMappingFile, - mMockUserManager, mMockDevicePolicyManager)); + new BugreportManagerServiceImpl.Injector(mContext, new ArraySet<>(), mMappingFile)); } private static class Listener implements IDumpstateListener { @@ -302,27 +258,4 @@ public class BugreportManagerServiceImplTest { complete(successful); } } - - private static class TestInjector extends BugreportManagerServiceImpl.Injector { - - private final UserManager mUserManager; - private final DevicePolicyManager mDevicePolicyManager; - - TestInjector(Context context, ArraySet<String> allowlistedPackages, AtomicFile mappingFile, - UserManager um, DevicePolicyManager dpm) { - super(context, allowlistedPackages, mappingFile); - mUserManager = um; - mDevicePolicyManager = dpm; - } - - @Override - public UserManager getUserManager() { - return mUserManager; - } - - @Override - public DevicePolicyManager getDevicePolicyManager() { - return mDevicePolicyManager; - } - } } |