diff options
64 files changed, 1246 insertions, 960 deletions
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index dbc1be141571..d9ac4850e924 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -868,6 +868,11 @@ public abstract class WallpaperService extends Service { * This will trigger a {@link #onComputeColors()} call. */ public void notifyColorsChanged() { + if (mDestroyed) { + Log.i(TAG, "Ignoring notifyColorsChanged(), Engine has already been destroyed."); + return; + } + final long now = mClockFunction.get(); if (now - mLastColorInvalidation < NOTIFY_COLORS_RATE_LIMIT_MS) { Log.w(TAG, "This call has been deferred. You should only call " @@ -2226,7 +2231,11 @@ public abstract class WallpaperService extends Service { } } - void detach() { + /** + * @hide + */ + @VisibleForTesting + public void detach() { if (mDestroyed) { return; } @@ -2442,6 +2451,14 @@ public abstract class WallpaperService extends Service { } public void reportShown() { + if (mEngine == null) { + Log.i(TAG, "Can't report null engine as shown."); + return; + } + if (mEngine.mDestroyed) { + Log.i(TAG, "Engine was destroyed before we could draw."); + return; + } if (!mShownReported) { mShownReported = true; Trace.beginSection("WPMS.mConnection.engineShown"); diff --git a/core/java/com/android/internal/os/TimeoutRecord.java b/core/java/com/android/internal/os/TimeoutRecord.java index a0e29347d07f..f8a5520c26d5 100644 --- a/core/java/com/android/internal/os/TimeoutRecord.java +++ b/core/java/com/android/internal/os/TimeoutRecord.java @@ -58,6 +58,7 @@ public class TimeoutRecord { int APP_REGISTERED = 7; int SHORT_FGS_TIMEOUT = 8; int JOB_SERVICE = 9; + int APP_START = 10; } /** Kind of timeout, e.g. BROADCAST_RECEIVER, etc. */ @@ -186,4 +187,10 @@ public class TimeoutRecord { public static TimeoutRecord forJobService(String reason) { return TimeoutRecord.endingNow(TimeoutKind.JOB_SERVICE, reason); } + + /** Record for app startup timeout. */ + @NonNull + public static TimeoutRecord forAppStart(String reason) { + return TimeoutRecord.endingNow(TimeoutKind.APP_START, reason); + } } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index e0183931777b..1b1efeed5ff2 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -1035,7 +1035,7 @@ public class LockPatternUtils { CREDENTIAL_TYPE_API, CREDENTIAL_TYPE_API, mCredentialTypeQuery); /** - * Invalidate the credential cache + * Invalidate the credential type cache * @hide */ public final static void invalidateCredentialTypeCache() { diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 2d8bfbb8517d..66ff01e6e7b9 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -5236,7 +5236,7 @@ </string-array> <!-- The integer index of the selected option in config_udfps_touch_detection_options --> - <integer name="config_selected_udfps_touch_detection">3</integer> + <integer name="config_selected_udfps_touch_detection">0</integer> <!-- An array of arrays of side fingerprint sensor properties relative to each display. Note: this value is temporary and is expected to be queried directly diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 2c85fe4e9206..c4530f64a82d 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -346,4 +346,8 @@ <!-- Allow IMS service entitlement app to schedule jobs to run when app in background. --> <allow-in-power-save-except-idle package="com.android.imsserviceentitlement" /> + + <!-- Allow device lock controller app to schedule jobs and alarms when app in background, + otherwise, it may not be able to enforce provision for managed devices. --> + <allow-in-power-save package="com.android.devicelockcontroller" /> </permissions> diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp index f71e7289bd37..b87002371775 100644 --- a/libs/hwui/pipeline/skia/ShaderCache.cpp +++ b/libs/hwui/pipeline/skia/ShaderCache.cpp @@ -88,6 +88,9 @@ void ShaderCache::initShaderDiskCache(const void* identity, ssize_t size) { mBlobCache.reset(new FileBlobCache(maxKeySize, maxValueSize, maxTotalSize, mFilename)); validateCache(identity, size); mInitialized = true; + if (identity != nullptr && size > 0 && mIDHash.size()) { + set(&sIDKey, sizeof(sIDKey), mIDHash.data(), mIDHash.size()); + } } } @@ -96,11 +99,6 @@ void ShaderCache::setFilename(const char* filename) { mFilename = filename; } -BlobCache* ShaderCache::getBlobCacheLocked() { - LOG_ALWAYS_FATAL_IF(!mInitialized, "ShaderCache has not been initialized"); - return mBlobCache.get(); -} - sk_sp<SkData> ShaderCache::load(const SkData& key) { ATRACE_NAME("ShaderCache::load"); size_t keySize = key.size(); @@ -115,8 +113,7 @@ sk_sp<SkData> ShaderCache::load(const SkData& key) { if (!valueBuffer) { return nullptr; } - BlobCache* bc = getBlobCacheLocked(); - size_t valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize); + size_t valueSize = mBlobCache->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize); int maxTries = 3; while (valueSize > mObservedBlobValueSize && maxTries > 0) { mObservedBlobValueSize = std::min(valueSize, maxValueSize); @@ -126,7 +123,7 @@ sk_sp<SkData> ShaderCache::load(const SkData& key) { return nullptr; } valueBuffer = newValueBuffer; - valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize); + valueSize = mBlobCache->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize); maxTries--; } if (!valueSize) { @@ -143,16 +140,17 @@ sk_sp<SkData> ShaderCache::load(const SkData& key) { return SkData::MakeFromMalloc(valueBuffer, valueSize); } -namespace { -// Helper for BlobCache::set to trace the result. -void set(BlobCache* cache, const void* key, size_t keySize, const void* value, size_t valueSize) { - switch (cache->set(key, keySize, value, valueSize)) { +void ShaderCache::set(const void* key, size_t keySize, const void* value, size_t valueSize) { + switch (mBlobCache->set(key, keySize, value, valueSize)) { case BlobCache::InsertResult::kInserted: // This is what we expect/hope. It means the cache is large enough. return; case BlobCache::InsertResult::kDidClean: { ATRACE_FORMAT("ShaderCache: evicted an entry to fit {key: %lu value %lu}!", keySize, valueSize); + if (mIDHash.size()) { + set(&sIDKey, sizeof(sIDKey), mIDHash.data(), mIDHash.size()); + } return; } case BlobCache::InsertResult::kNotEnoughSpace: { @@ -172,15 +170,10 @@ void set(BlobCache* cache, const void* key, size_t keySize, const void* value, s } } } -} // namespace void ShaderCache::saveToDiskLocked() { ATRACE_NAME("ShaderCache::saveToDiskLocked"); if (mInitialized && mBlobCache) { - if (mIDHash.size()) { - auto key = sIDKey; - set(mBlobCache.get(), &key, sizeof(key), mIDHash.data(), mIDHash.size()); - } // The most straightforward way to make ownership shared mMutex.unlock(); mMutex.lock_shared(); @@ -209,11 +202,10 @@ void ShaderCache::store(const SkData& key, const SkData& data, const SkString& / const void* value = data.data(); - BlobCache* bc = getBlobCacheLocked(); if (mInStoreVkPipelineInProgress) { if (mOldPipelineCacheSize == -1) { // Record the initial pipeline cache size stored in the file. - mOldPipelineCacheSize = bc->get(key.data(), keySize, nullptr, 0); + mOldPipelineCacheSize = mBlobCache->get(key.data(), keySize, nullptr, 0); } if (mNewPipelineCacheSize != -1 && mNewPipelineCacheSize == valueSize) { // There has not been change in pipeline cache size. Stop trying to save. @@ -228,7 +220,7 @@ void ShaderCache::store(const SkData& key, const SkData& data, const SkString& / mNewPipelineCacheSize = -1; mTryToStorePipelineCache = true; } - set(bc, key.data(), keySize, value, valueSize); + set(key.data(), keySize, value, valueSize); if (!mSavePending && mDeferredSaveDelayMs > 0) { mSavePending = true; diff --git a/libs/hwui/pipeline/skia/ShaderCache.h b/libs/hwui/pipeline/skia/ShaderCache.h index 2f91c778b8a0..74955503dbb1 100644 --- a/libs/hwui/pipeline/skia/ShaderCache.h +++ b/libs/hwui/pipeline/skia/ShaderCache.h @@ -96,20 +96,18 @@ private: void operator=(const ShaderCache&) = delete; /** - * "getBlobCacheLocked" returns the BlobCache object being used to store the - * key/value blob pairs. If the BlobCache object has not yet been created, - * this will do so, loading the serialized cache contents from disk if - * possible. - */ - BlobCache* getBlobCacheLocked() REQUIRES(mMutex); - - /** * "validateCache" updates the cache to match the given identity. If the * cache currently has the wrong identity, all entries in the cache are cleared. */ bool validateCache(const void* identity, ssize_t size) REQUIRES(mMutex); /** + * Helper for BlobCache::set to trace the result and ensure the identity hash + * does not get evicted. + */ + void set(const void* key, size_t keySize, const void* value, size_t valueSize) REQUIRES(mMutex); + + /** * "saveToDiskLocked" attempts to save the current contents of the cache to * disk. If the identity hash exists, we will insert the identity hash into * the cache for next validation. @@ -127,11 +125,9 @@ private: bool mInitialized GUARDED_BY(mMutex) = false; /** - * "mBlobCache" is the cache in which the key/value blob pairs are stored. It - * is initially NULL, and will be initialized by getBlobCacheLocked the - * first time it's needed. - * The blob cache contains the Android build number. We treat version mismatches as an empty - * cache (logic implemented in BlobCache::unflatten). + * "mBlobCache" is the cache in which the key/value blob pairs are stored. + * The blob cache contains the Android build number. We treat version mismatches + * as an empty cache (logic implemented in BlobCache::unflatten). */ std::unique_ptr<FileBlobCache> mBlobCache GUARDED_BY(mMutex); diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt index 8e79e3ce1742..38b99cc5f5ee 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt @@ -70,6 +70,9 @@ class ViewHierarchyAnimator { * If a new layout change happens while an animation is already in progress, the animation * is updated to continue from the current values to the new end state. * + * A set of [excludedViews] can be passed. If any dependent view from [rootView] matches an + * entry in this set, changes to that view will not be animated. + * * The animator continues to respond to layout changes until [stopAnimating] is called. * * Successive calls to this method override the previous settings ([interpolator] and @@ -82,9 +85,16 @@ class ViewHierarchyAnimator { fun animate( rootView: View, interpolator: Interpolator = DEFAULT_INTERPOLATOR, - duration: Long = DEFAULT_DURATION + duration: Long = DEFAULT_DURATION, + excludedViews: Set<View> = emptySet() ): Boolean { - return animate(rootView, interpolator, duration, ephemeral = false) + return animate( + rootView, + interpolator, + duration, + ephemeral = false, + excludedViews = excludedViews + ) } /** @@ -95,16 +105,24 @@ class ViewHierarchyAnimator { fun animateNextUpdate( rootView: View, interpolator: Interpolator = DEFAULT_INTERPOLATOR, - duration: Long = DEFAULT_DURATION + duration: Long = DEFAULT_DURATION, + excludedViews: Set<View> = emptySet() ): Boolean { - return animate(rootView, interpolator, duration, ephemeral = true) + return animate( + rootView, + interpolator, + duration, + ephemeral = true, + excludedViews = excludedViews + ) } private fun animate( rootView: View, interpolator: Interpolator, duration: Long, - ephemeral: Boolean + ephemeral: Boolean, + excludedViews: Set<View> = emptySet() ): Boolean { if ( !occupiesSpace( @@ -119,7 +137,7 @@ class ViewHierarchyAnimator { } val listener = createUpdateListener(interpolator, duration, ephemeral) - addListener(rootView, listener, recursive = true) + addListener(rootView, listener, recursive = true, excludedViews = excludedViews) return true } @@ -921,8 +939,11 @@ class ViewHierarchyAnimator { private fun addListener( view: View, listener: View.OnLayoutChangeListener, - recursive: Boolean = false + recursive: Boolean = false, + excludedViews: Set<View> = emptySet() ) { + if (excludedViews.contains(view)) return + // Make sure that only one listener is active at a time. val previousListener = view.getTag(R.id.tag_layout_listener) if (previousListener != null && previousListener is View.OnLayoutChangeListener) { @@ -933,7 +954,12 @@ class ViewHierarchyAnimator { view.setTag(R.id.tag_layout_listener, listener) if (view is ViewGroup && recursive) { for (i in 0 until view.childCount) { - addListener(view.getChildAt(i), listener, recursive = true) + addListener( + view.getChildAt(i), + listener, + recursive = true, + excludedViews = excludedViews + ) } } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt index ca91b8a21a81..38b751c9445d 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt @@ -17,15 +17,19 @@ package com.android.systemui.notifications.ui.composable +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp @Composable @@ -34,10 +38,26 @@ fun Notifications( ) { // TODO(b/272779828): implement. Column( - modifier = modifier.fillMaxWidth().defaultMinSize(minHeight = 300.dp).padding(4.dp), + modifier = + modifier + .fillMaxWidth() + .defaultMinSize(minHeight = 300.dp) + .clip(RoundedCornerShape(32.dp)) + .background(MaterialTheme.colorScheme.surface) + .padding(16.dp), ) { - Text("Notifications", modifier = Modifier.align(Alignment.CenterHorizontally)) + Text( + text = "Notifications", + modifier = Modifier.align(Alignment.CenterHorizontally), + style = MaterialTheme.typography.titleLarge, + color = MaterialTheme.colorScheme.onSurface, + ) Spacer(modifier = Modifier.weight(1f)) - Text("Shelf", modifier = Modifier.align(Alignment.CenterHorizontally)) + Text( + text = "Shelf", + modifier = Modifier.align(Alignment.CenterHorizontally), + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.onSurface, + ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/QuickSettings.kt index 665d6dd0cfa2..1bb341c76e69 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/QuickSettings.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/QuickSettings.kt @@ -17,15 +17,19 @@ package com.android.systemui.qs.footer.ui.compose +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp @Composable @@ -34,10 +38,26 @@ fun QuickSettings( ) { // TODO(b/272780058): implement. Column( - modifier = modifier.fillMaxWidth().defaultMinSize(minHeight = 300.dp).padding(4.dp), + modifier = + modifier + .fillMaxWidth() + .defaultMinSize(minHeight = 300.dp) + .clip(RoundedCornerShape(32.dp)) + .background(MaterialTheme.colorScheme.primary) + .padding(16.dp), ) { - Text("Quick settings", modifier = Modifier.align(Alignment.CenterHorizontally)) + Text( + text = "Quick settings", + modifier = Modifier.align(Alignment.CenterHorizontally), + style = MaterialTheme.typography.titleLarge, + color = MaterialTheme.colorScheme.onPrimary, + ) Spacer(modifier = Modifier.weight(1f)) - Text("QS footer actions", modifier = Modifier.align(Alignment.CenterHorizontally)) + Text( + text = "QS footer actions", + modifier = Modifier.align(Alignment.CenterHorizontally), + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.onPrimary, + ) } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt index 20e175160aa6..27358f53aaf2 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt @@ -16,18 +16,19 @@ package com.android.systemui.shade.ui.composable +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.material3.Button -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.notifications.ui.composable.Notifications +import com.android.systemui.qs.footer.ui.compose.QuickSettings import com.android.systemui.scene.shared.model.Direction import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel @@ -62,12 +63,7 @@ class ShadeScene( override fun Content( containerName: String, modifier: Modifier, - ) { - ShadeScene( - viewModel = viewModel, - modifier = modifier, - ) - } + ) = ShadeScene(viewModel, modifier) private fun destinationScenes( up: SceneKey, @@ -84,23 +80,15 @@ private fun ShadeScene( viewModel: ShadeSceneViewModel, modifier: Modifier = Modifier, ) { - // TODO(b/280887022): implement the real UI. - - Box(modifier = modifier) { - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.align(Alignment.Center) - ) { - Text("Shade", style = MaterialTheme.typography.headlineMedium) - Row( - horizontalArrangement = Arrangement.spacedBy(8.dp), - ) { - Button( - onClick = { viewModel.onContentClicked() }, - ) { - Text("Open some content") - } - } - } + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp), + modifier = + Modifier.fillMaxSize() + .clickable(onClick = { viewModel.onContentClicked() }) + .padding(horizontal = 16.dp, vertical = 48.dp) + ) { + QuickSettings(modifier = modifier.height(160.dp)) + Notifications(modifier = modifier.weight(1f)) } } diff --git a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml deleted file mode 100644 index 9304ff72f054..000000000000 --- a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml +++ /dev/null @@ -1,136 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2019 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 - --> - -<!-- Layout for media recommendations inside QSPanel carousel --> -<!-- See media_recommendation_expanded.xml and media_recommendation_collapsed.xml for the - constraints. --> -<com.android.systemui.util.animation.TransitionLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:id="@+id/media_recommendations" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:clipChildren="false" - android:clipToPadding="false" - android:forceHasOverlappingRendering="false" - android:background="@drawable/qs_media_background" - android:theme="@style/MediaPlayer"> - - <!-- This view just ensures the full media player is a certain height. --> - <View - android:id="@+id/sizing_view" - android:layout_width="match_parent" - android:layout_height="@dimen/qs_media_session_height_expanded" /> - - <com.android.internal.widget.CachingIconView - android:id="@+id/recommendation_card_icon" - android:layout_width="@dimen/qs_media_app_icon_size" - android:layout_height="@dimen/qs_media_app_icon_size" - android:minWidth="@dimen/qs_media_app_icon_size" - android:minHeight="@dimen/qs_media_app_icon_size" - android:layout_marginStart="@dimen/qs_media_padding" - android:layout_marginTop="@dimen/qs_media_rec_icon_top_margin" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" /> - - <FrameLayout - android:id="@+id/media_cover1_container" - style="@style/MediaPlayer.Recommendation.AlbumContainer" - > - <ImageView - android:id="@+id/media_cover1" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:minWidth="@dimen/qs_media_rec_album_size" - android:minHeight="@dimen/qs_media_rec_album_size" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toBottomOf="parent" - android:adjustViewBounds="true" - android:background="@drawable/bg_smartspace_media_item" - style="@style/MediaPlayer.Recommendation.Album" - android:clipToOutline="true" - android:scaleType="centerCrop"/> - </FrameLayout> - - <TextView - android:id="@+id/media_title1" - style="@style/MediaPlayer.Recommendation.Text.Title" - /> - - <TextView - android:id="@+id/media_subtitle1" - style="@style/MediaPlayer.Recommendation.Text.Subtitle" - /> - - <FrameLayout - android:id="@+id/media_cover2_container" - style="@style/MediaPlayer.Recommendation.AlbumContainer" - > - <ImageView - android:id="@+id/media_cover2" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:minWidth="@dimen/qs_media_rec_album_size" - android:minHeight="@dimen/qs_media_rec_album_size" - android:adjustViewBounds="true" - android:background="@drawable/bg_smartspace_media_item" - style="@style/MediaPlayer.Recommendation.Album" - android:clipToOutline="true" - android:scaleType="centerCrop"/> - </FrameLayout> - - <TextView - android:id="@+id/media_title2" - style="@style/MediaPlayer.Recommendation.Text.Title" - /> - - <TextView - android:id="@+id/media_subtitle2" - style="@style/MediaPlayer.Recommendation.Text.Subtitle" - /> - - <FrameLayout - android:id="@+id/media_cover3_container" - style="@style/MediaPlayer.Recommendation.AlbumContainer" - > - <ImageView - android:id="@+id/media_cover3" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:minWidth="@dimen/qs_media_rec_album_size" - android:minHeight="@dimen/qs_media_rec_album_size" - android:adjustViewBounds="true" - android:background="@drawable/bg_smartspace_media_item" - style="@style/MediaPlayer.Recommendation.Album" - android:clipToOutline="true" - android:scaleType="centerCrop"/> - </FrameLayout> - - <TextView - android:id="@+id/media_title3" - style="@style/MediaPlayer.Recommendation.Text.Title" - /> - - <TextView - android:id="@+id/media_subtitle3" - style="@style/MediaPlayer.Recommendation.Text.Subtitle" - /> - - <include - layout="@layout/media_long_press_menu" /> - -</com.android.systemui.util.animation.TransitionLayout> diff --git a/packages/SystemUI/res/xml/media_recommendation_collapsed.xml b/packages/SystemUI/res/xml/media_recommendation_collapsed.xml deleted file mode 100644 index b7d4b3aac079..000000000000 --- a/packages/SystemUI/res/xml/media_recommendation_collapsed.xml +++ /dev/null @@ -1,101 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2020 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 - --> -<ConstraintSet - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" > - - <Constraint - android:id="@+id/sizing_view" - android:layout_width="match_parent" - android:layout_height="@dimen/qs_media_session_height_collapsed" - /> - - <!-- Only the constraintBottom and marginBottom are different. The rest of the constraints are - the same as the constraints in media_recommendations_expanded.xml. But, due to how - ConstraintSets work, all the constraints need to be in the same place. So, the shared - constraints can't be put in the shared layout file media_smartspace_recommendations.xml and - the constraints are instead duplicated between here and media_recommendations_expanded.xml. - Ditto for the other cover containers. --> - <Constraint - android:id="@+id/media_cover1_container" - app:layout_constraintBottom_toBottomOf="parent" - android:layout_marginBottom="@dimen/qs_media_padding" - style="@style/MediaPlayer.Recommendation.AlbumContainer" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toStartOf="@id/media_cover2_container" - android:layout_marginEnd="@dimen/qs_media_rec_album_side_margin" - app:layout_constraintHorizontal_chainStyle="packed" - app:layout_constraintHorizontal_bias="1.0" - app:layout_constraintVertical_bias="0.5" - /> - - <Constraint - android:id="@+id/media_title1" - android:visibility="gone" - /> - - <Constraint - android:id="@+id/media_subtitle1" - android:visibility="gone" - /> - - <Constraint - android:id="@+id/media_cover2_container" - app:layout_constraintBottom_toBottomOf="parent" - android:layout_marginBottom="@dimen/qs_media_padding" - style="@style/MediaPlayer.Recommendation.AlbumContainer" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintStart_toEndOf="@id/media_cover1_container" - app:layout_constraintEnd_toStartOf="@id/media_cover3_container" - android:layout_marginEnd="@dimen/qs_media_rec_album_side_margin" - app:layout_constraintVertical_bias="0.5" - /> - - <Constraint - android:id="@+id/media_title2" - android:visibility="gone" - /> - - <Constraint - android:id="@+id/media_subtitle2" - android:visibility="gone" - /> - - <Constraint - android:id="@+id/media_cover3_container" - app:layout_constraintBottom_toBottomOf="parent" - android:layout_marginBottom="@dimen/qs_media_padding" - style="@style/MediaPlayer.Recommendation.AlbumContainer" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintStart_toEndOf="@id/media_cover2_container" - app:layout_constraintEnd_toEndOf="parent" - android:layout_marginEnd="@dimen/qs_media_padding" - app:layout_constraintVertical_bias="0.5" - /> - - <Constraint - android:id="@+id/media_title3" - android:visibility="gone" - /> - - <Constraint - android:id="@+id/media_subtitle3" - android:visibility="gone" - /> - -</ConstraintSet> diff --git a/packages/SystemUI/res/xml/media_recommendation_expanded.xml b/packages/SystemUI/res/xml/media_recommendation_expanded.xml deleted file mode 100644 index ce25a7d01bf7..000000000000 --- a/packages/SystemUI/res/xml/media_recommendation_expanded.xml +++ /dev/null @@ -1,123 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2020 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 - --> -<ConstraintSet - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - > - - <Constraint - android:id="@+id/sizing_view" - android:layout_width="match_parent" - android:layout_height="@dimen/qs_media_session_height_expanded" - /> - - <Constraint - android:id="@+id/media_cover1_container" - style="@style/MediaPlayer.Recommendation.AlbumContainer" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toTopOf="@+id/media_title1" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toStartOf="@id/media_cover2_container" - android:layout_marginEnd="@dimen/qs_media_rec_album_side_margin" - app:layout_constraintHorizontal_chainStyle="packed" - app:layout_constraintVertical_chainStyle="packed" - app:layout_constraintHorizontal_bias="1.0" - app:layout_constraintVertical_bias="0.4" - /> - - <Constraint - android:id="@+id/media_title1" - style="@style/MediaPlayer.Recommendation.Text.Title" - app:layout_constraintStart_toStartOf="@+id/media_cover1_container" - app:layout_constraintEnd_toEndOf="@+id/media_cover1_container" - app:layout_constraintTop_toBottomOf="@+id/media_cover1_container" - app:layout_constraintBottom_toTopOf="@+id/media_subtitle1" - /> - - <Constraint - android:id="@+id/media_subtitle1" - style="@style/MediaPlayer.Recommendation.Text.Subtitle" - app:layout_constraintStart_toStartOf="@+id/media_cover1_container" - app:layout_constraintEnd_toEndOf="@+id/media_cover1_container" - app:layout_constraintTop_toBottomOf="@+id/media_title1" - app:layout_constraintBottom_toBottomOf="parent" - android:layout_marginBottom="@dimen/qs_media_padding" - /> - - <Constraint - android:id="@+id/media_cover2_container" - style="@style/MediaPlayer.Recommendation.AlbumContainer" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toTopOf="@id/media_title2" - app:layout_constraintStart_toEndOf="@id/media_cover1_container" - app:layout_constraintEnd_toStartOf="@id/media_cover3_container" - android:layout_marginEnd="@dimen/qs_media_rec_album_side_margin" - app:layout_constraintVertical_chainStyle="packed" - app:layout_constraintVertical_bias="0.4" - /> - - <Constraint - android:id="@+id/media_title2" - style="@style/MediaPlayer.Recommendation.Text.Title" - app:layout_constraintStart_toStartOf="@+id/media_cover2_container" - app:layout_constraintEnd_toEndOf="@+id/media_cover2_container" - app:layout_constraintTop_toBottomOf="@+id/media_cover2_container" - app:layout_constraintBottom_toTopOf="@+id/media_subtitle2" - /> - - <Constraint - android:id="@+id/media_subtitle2" - style="@style/MediaPlayer.Recommendation.Text.Subtitle" - app:layout_constraintStart_toStartOf="@+id/media_cover2_container" - app:layout_constraintEnd_toEndOf="@+id/media_cover2_container" - app:layout_constraintTop_toBottomOf="@+id/media_title2" - app:layout_constraintBottom_toBottomOf="parent" - android:layout_marginBottom="@dimen/qs_media_padding" - /> - - <Constraint - android:id="@+id/media_cover3_container" - style="@style/MediaPlayer.Recommendation.AlbumContainer" - app:layout_constraintTop_toTopOf="parent" - app:layout_constraintBottom_toTopOf="@id/media_title3" - app:layout_constraintStart_toEndOf="@id/media_cover2_container" - app:layout_constraintEnd_toEndOf="parent" - android:layout_marginEnd="@dimen/qs_media_padding" - app:layout_constraintVertical_chainStyle="packed" - app:layout_constraintVertical_bias="0.4" - /> - - <Constraint - android:id="@+id/media_title3" - style="@style/MediaPlayer.Recommendation.Text.Title" - app:layout_constraintStart_toStartOf="@+id/media_cover3_container" - app:layout_constraintEnd_toEndOf="@+id/media_cover3_container" - app:layout_constraintTop_toBottomOf="@+id/media_cover3_container" - app:layout_constraintBottom_toTopOf="@+id/media_subtitle3" - /> - - <Constraint - android:id="@+id/media_subtitle3" - style="@style/MediaPlayer.Recommendation.Text.Subtitle" - app:layout_constraintStart_toStartOf="@+id/media_cover3_container" - app:layout_constraintEnd_toEndOf="@+id/media_cover3_container" - app:layout_constraintTop_toBottomOf="@+id/media_title3" - app:layout_constraintBottom_toBottomOf="parent" - android:layout_marginBottom="@dimen/qs_media_padding" - /> - -</ConstraintSet> diff --git a/packages/SystemUI/res/xml/media_recommendations_view_collapsed.xml b/packages/SystemUI/res/xml/media_recommendations_collapsed.xml index d3be3c7de5ad..d3be3c7de5ad 100644 --- a/packages/SystemUI/res/xml/media_recommendations_view_collapsed.xml +++ b/packages/SystemUI/res/xml/media_recommendations_collapsed.xml diff --git a/packages/SystemUI/res/xml/media_recommendations_view_expanded.xml b/packages/SystemUI/res/xml/media_recommendations_expanded.xml index 88c70552e9e8..88c70552e9e8 100644 --- a/packages/SystemUI/res/xml/media_recommendations_view_expanded.xml +++ b/packages/SystemUI/res/xml/media_recommendations_expanded.xml diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 7a2f2443dbd2..9df56fcce430 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -250,7 +250,7 @@ public class AuthContainerView extends LinearLayout .setMessage(messageBody) .setPositiveButton(android.R.string.ok, null) .create(); - alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); + alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); alertDialog.show(); } @@ -263,7 +263,7 @@ public class AuthContainerView extends LinearLayout .setOnDismissListener( dialog -> animateAway(AuthDialogCallback.DISMISSED_ERROR)) .create(); - alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); + alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); alertDialog.show(); } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index add323983928..0670ec380861 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -438,10 +438,6 @@ object Flags { // TODO(b/266157412): Tracking Bug val MEDIA_RETAIN_SESSIONS = unreleasedFlag(913, "media_retain_sessions") - // TODO(b/266739309): Tracking Bug - @JvmField - val MEDIA_RECOMMENDATION_CARD_UPDATE = releasedFlag(914, "media_recommendation_card_update") - // TODO(b/267007629): Tracking Bug val MEDIA_RESUME_PROGRESS = releasedFlag(915, "media_resume_progress") diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt index 0b33904829d5..258284e8af51 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/RecommendationViewHolder.kt @@ -31,15 +31,12 @@ import com.android.systemui.util.animation.TransitionLayout private const val TAG = "RecommendationViewHolder" /** ViewHolder for a Smartspace media recommendation. */ -class RecommendationViewHolder private constructor(itemView: View, updatedView: Boolean) { +class RecommendationViewHolder private constructor(itemView: View) { val recommendations = itemView as TransitionLayout // Recommendation screen - lateinit var cardIcon: ImageView - lateinit var mediaAppIcons: List<CachingIconView> - lateinit var mediaProgressBars: List<SeekBar> - lateinit var cardTitle: TextView + val cardTitle: TextView = itemView.requireViewById(R.id.media_rec_title) val mediaCoverContainers = listOf<ViewGroup>( @@ -47,53 +44,25 @@ class RecommendationViewHolder private constructor(itemView: View, updatedView: itemView.requireViewById(R.id.media_cover2_container), itemView.requireViewById(R.id.media_cover3_container) ) + val mediaAppIcons: List<CachingIconView> = + mediaCoverContainers.map { it.requireViewById(R.id.media_rec_app_icon) } val mediaTitles: List<TextView> = - if (updatedView) { - mediaCoverContainers.map { it.requireViewById(R.id.media_title) } - } else { - listOf( - itemView.requireViewById(R.id.media_title1), - itemView.requireViewById(R.id.media_title2), - itemView.requireViewById(R.id.media_title3) - ) - } + mediaCoverContainers.map { it.requireViewById(R.id.media_title) } val mediaSubtitles: List<TextView> = - if (updatedView) { - mediaCoverContainers.map { it.requireViewById(R.id.media_subtitle) } - } else { - listOf( - itemView.requireViewById(R.id.media_subtitle1), - itemView.requireViewById(R.id.media_subtitle2), - itemView.requireViewById(R.id.media_subtitle3) - ) + mediaCoverContainers.map { it.requireViewById(R.id.media_subtitle) } + val mediaProgressBars: List<SeekBar> = + mediaCoverContainers.map { + it.requireViewById<SeekBar?>(R.id.media_progress_bar).apply { + // Media playback is in the direction of tape, not time, so it stays LTR + layoutDirection = View.LAYOUT_DIRECTION_LTR + } } val mediaCoverItems: List<ImageView> = - if (updatedView) { - mediaCoverContainers.map { it.requireViewById(R.id.media_cover) } - } else { - listOf( - itemView.requireViewById(R.id.media_cover1), - itemView.requireViewById(R.id.media_cover2), - itemView.requireViewById(R.id.media_cover3) - ) - } + mediaCoverContainers.map { it.requireViewById(R.id.media_cover) } val gutsViewHolder = GutsViewHolder(itemView) init { - if (updatedView) { - mediaAppIcons = mediaCoverContainers.map { it.requireViewById(R.id.media_rec_app_icon) } - cardTitle = itemView.requireViewById(R.id.media_rec_title) - mediaProgressBars = - mediaCoverContainers.map { - it.requireViewById<SeekBar?>(R.id.media_progress_bar).apply { - // Media playback is in the direction of tape, not time, so it stays LTR - layoutDirection = View.LAYOUT_DIRECTION_LTR - } - } - } else { - cardIcon = itemView.requireViewById<ImageView>(R.id.recommendation_card_icon) - } (recommendations.background as IlluminationDrawable).let { background -> mediaCoverContainers.forEach { background.registerLightSource(it) } background.registerLightSource(gutsViewHolder.cancel) @@ -114,63 +83,31 @@ class RecommendationViewHolder private constructor(itemView: View, updatedView: * @param parent Parent of inflated view. */ @JvmStatic - fun create( - inflater: LayoutInflater, - parent: ViewGroup, - updatedView: Boolean, - ): RecommendationViewHolder { + fun create(inflater: LayoutInflater, parent: ViewGroup): RecommendationViewHolder { val itemView = - if (updatedView) { - inflater.inflate( - R.layout.media_recommendations, - parent, - false /* attachToRoot */ - ) - } else { - inflater.inflate( - R.layout.media_smartspace_recommendations, - parent, - false /* attachToRoot */ - ) - } + inflater.inflate(R.layout.media_recommendations, parent, false /* attachToRoot */) // Because this media view (a TransitionLayout) is used to measure and layout the views // in various states before being attached to its parent, we can't depend on the default // LAYOUT_DIRECTION_INHERIT to correctly resolve the ltr direction. itemView.layoutDirection = View.LAYOUT_DIRECTION_LOCALE - return RecommendationViewHolder(itemView, updatedView) + return RecommendationViewHolder(itemView) } // Res Ids for the control components on the recommendation view. val controlsIds = setOf( - R.id.recommendation_card_icon, R.id.media_rec_title, - R.id.media_cover1, - R.id.media_cover2, - R.id.media_cover3, R.id.media_cover, R.id.media_cover1_container, R.id.media_cover2_container, R.id.media_cover3_container, - R.id.media_title1, - R.id.media_title2, - R.id.media_title3, R.id.media_title, - R.id.media_subtitle1, - R.id.media_subtitle2, - R.id.media_subtitle3, R.id.media_subtitle, ) val mediaTitlesAndSubtitlesIds = setOf( - R.id.media_title1, - R.id.media_title2, - R.id.media_title3, R.id.media_title, - R.id.media_subtitle1, - R.id.media_subtitle2, - R.id.media_subtitle3, R.id.media_subtitle, ) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt index 70b5e75e6048..398dcf260dff 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt @@ -744,11 +744,7 @@ constructor( val newRecs = mediaControlPanelFactory.get() newRecs.attachRecommendation( - RecommendationViewHolder.create( - LayoutInflater.from(context), - mediaContent, - mediaFlags.isRecommendationCardUpdateEnabled() - ) + RecommendationViewHolder.create(LayoutInflater.from(context), mediaContent) ) newRecs.mediaViewController.sizeChangedListener = this::updateCarouselDimensions val lp = diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java index a978b92cb234..a12bc2c99d63 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java @@ -784,14 +784,7 @@ public class MediaControlPanel { contentDescription = mRecommendationViewHolder.getGutsViewHolder().getGutsText().getText(); } else if (data != null) { - if (mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) { - contentDescription = mContext.getString( - R.string.controls_media_smartspace_rec_header); - } else { - contentDescription = mContext.getString( - R.string.controls_media_smartspace_rec_description, - data.getAppName(mContext)); - } + contentDescription = mContext.getString(R.string.controls_media_smartspace_rec_header); } else { contentDescription = null; } @@ -1377,10 +1370,6 @@ public class MediaControlPanel { PackageManager packageManager = mContext.getPackageManager(); // Set up media source app's logo. Drawable icon = packageManager.getApplicationIcon(applicationInfo); - if (!mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) { - ImageView headerLogoImageView = mRecommendationViewHolder.getCardIcon(); - headerLogoImageView.setImageDrawable(icon); - } fetchAndUpdateRecommendationColors(icon); // Set up media rec card's tap action if applicable. @@ -1401,16 +1390,7 @@ public class MediaControlPanel { // Set up media item cover. ImageView mediaCoverImageView = mediaCoverItems.get(itemIndex); - if (mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) { - bindRecommendationArtwork( - recommendation, - data.getPackageName(), - itemIndex - ); - } else { - mediaCoverImageView.post( - () -> mediaCoverImageView.setImageIcon(recommendation.getIcon())); - } + bindRecommendationArtwork(recommendation, data.getPackageName(), itemIndex); // Set up the media item's click listener if applicable. ViewGroup mediaCoverContainer = mediaCoverContainers.get(itemIndex); @@ -1455,21 +1435,18 @@ public class MediaControlPanel { subtitleView.setText(subtitle); // Set up progress bar - if (mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) { - SeekBar mediaProgressBar = - mRecommendationViewHolder.getMediaProgressBars().get(itemIndex); - TextView mediaSubtitle = - mRecommendationViewHolder.getMediaSubtitles().get(itemIndex); - // show progress bar if the recommended album is played. - Double progress = MediaDataUtils.getDescriptionProgress(recommendation.getExtras()); - if (progress == null || progress <= 0.0) { - mediaProgressBar.setVisibility(View.GONE); - mediaSubtitle.setVisibility(View.VISIBLE); - } else { - mediaProgressBar.setProgress((int) (progress * 100)); - mediaProgressBar.setVisibility(View.VISIBLE); - mediaSubtitle.setVisibility(View.GONE); - } + SeekBar mediaProgressBar = + mRecommendationViewHolder.getMediaProgressBars().get(itemIndex); + TextView mediaSubtitle = mRecommendationViewHolder.getMediaSubtitles().get(itemIndex); + // show progress bar if the recommended album is played. + Double progress = MediaDataUtils.getDescriptionProgress(recommendation.getExtras()); + if (progress == null || progress <= 0.0) { + mediaProgressBar.setVisibility(View.GONE); + mediaSubtitle.setVisibility(View.VISIBLE); + } else { + mediaProgressBar.setProgress((int) (progress * 100)); + mediaProgressBar.setVisibility(View.VISIBLE); + mediaSubtitle.setVisibility(View.GONE); } } mSmartspaceMediaItemsCount = NUM_REQUIRED_RECOMMENDATIONS; @@ -1588,9 +1565,7 @@ public class MediaControlPanel { int textPrimaryColor = MediaColorSchemesKt.textPrimaryFromScheme(colorScheme); int textSecondaryColor = MediaColorSchemesKt.textSecondaryFromScheme(colorScheme); - if (mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) { - mRecommendationViewHolder.getCardTitle().setTextColor(textPrimaryColor); - } + mRecommendationViewHolder.getCardTitle().setTextColor(textPrimaryColor); mRecommendationViewHolder.getRecommendations() .setBackgroundTintList(ColorStateList.valueOf(backgroundColor)); @@ -1598,12 +1573,9 @@ public class MediaControlPanel { (title) -> title.setTextColor(textPrimaryColor)); mRecommendationViewHolder.getMediaSubtitles().forEach( (subtitle) -> subtitle.setTextColor(textSecondaryColor)); - if (mFeatureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE)) { - mRecommendationViewHolder.getMediaProgressBars().forEach( - (progressBar) -> progressBar.setProgressTintList( - ColorStateList.valueOf(textPrimaryColor)) - ); - } + mRecommendationViewHolder.getMediaProgressBars().forEach( + (progressBar) -> progressBar.setProgressTintList( + ColorStateList.valueOf(textPrimaryColor))); mRecommendationViewHolder.getGutsViewHolder().setColors(colorScheme); } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt index 4bca778b77c5..1dd969f9bea5 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt @@ -655,13 +655,8 @@ constructor( expandedLayout.load(context, R.xml.media_session_expanded) } TYPE.RECOMMENDATION -> { - if (mediaFlags.isRecommendationCardUpdateEnabled()) { - collapsedLayout.load(context, R.xml.media_recommendations_view_collapsed) - expandedLayout.load(context, R.xml.media_recommendations_view_expanded) - } else { - collapsedLayout.load(context, R.xml.media_recommendation_collapsed) - expandedLayout.load(context, R.xml.media_recommendation_expanded) - } + collapsedLayout.load(context, R.xml.media_recommendations_collapsed) + expandedLayout.load(context, R.xml.media_recommendations_expanded) } } refreshState() diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt index 01f047ccd4f5..09aef8826200 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt @@ -49,10 +49,6 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) { */ fun isRetainingPlayersEnabled() = featureFlags.isEnabled(Flags.MEDIA_RETAIN_SESSIONS) - /** Check whether we show the updated recommendation card. */ - fun isRecommendationCardUpdateEnabled() = - featureFlags.isEnabled(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE) - /** Check whether to get progress information for resume players */ fun isResumeProgressEnabled() = featureFlags.isEnabled(Flags.MEDIA_RESUME_PROGRESS) diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java index 83b373d5e626..856a92e85ad7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java @@ -58,6 +58,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { private final BrightnessSliderController mBrightnessSliderController; private final BrightnessMirrorHandler mBrightnessMirrorHandler; private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + private boolean mListening; private View.OnTouchListener mTileLayoutTouchListener = new View.OnTouchListener() { @Override @@ -159,12 +160,15 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> { public void setListening(boolean listening, boolean expanded) { setListening(listening && expanded); - // Set the listening as soon as the QS fragment starts listening regardless of the - //expansion, so it will update the current brightness before the slider is visible. - if (listening) { - mBrightnessController.registerCallbacks(); - } else { - mBrightnessController.unregisterCallbacks(); + if (listening != mListening) { + mListening = listening; + // Set the listening as soon as the QS fragment starts listening regardless of the + //expansion, so it will update the current brightness before the slider is visible. + if (listening) { + mBrightnessController.registerCallbacks(); + } else { + mBrightnessController.unregisterCallbacks(); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java index 9594ba374fc3..6af9b739da52 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java @@ -22,7 +22,6 @@ import static com.android.settingslib.display.BrightnessUtils.convertLinearToGam import android.animation.ValueAnimator; import android.annotation.NonNull; -import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; import android.hardware.display.BrightnessInfo; @@ -31,10 +30,10 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Handler; import android.os.HandlerExecutor; +import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -43,6 +42,8 @@ import android.service.vr.IVrStateCallbacks; import android.util.Log; import android.util.MathUtils; +import androidx.annotation.Nullable; + import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; @@ -52,10 +53,13 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.BrightnessMirrorController; +import com.android.systemui.util.settings.SecureSettings; import java.util.concurrent.Executor; -import javax.inject.Inject; +import dagger.assisted.Assisted; +import dagger.assisted.AssistedFactory; +import dagger.assisted.AssistedInject; public class BrightnessController implements ToggleSlider.Listener, MirroredBrightnessController { private static final String TAG = "CentralSurfaces.BrightnessController"; @@ -75,8 +79,11 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig private final DisplayManager mDisplayManager; private final UserTracker mUserTracker; private final DisplayTracker mDisplayTracker; + @Nullable private final IVrManager mVrManager; + private final SecureSettings mSecureSettings; + private final Executor mMainExecutor; private final Handler mBackgroundHandler; private final BrightnessObserver mBrightnessObserver; @@ -106,6 +113,8 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig /** ContentObserver to watch brightness */ private class BrightnessObserver extends ContentObserver { + private boolean mObserving = false; + BrightnessObserver(Handler handler) { super(handler); } @@ -124,19 +133,17 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig } public void startObserving() { - final ContentResolver cr = mContext.getContentResolver(); - cr.unregisterContentObserver(this); - cr.registerContentObserver( - BRIGHTNESS_MODE_URI, - false, this, UserHandle.USER_ALL); - mDisplayTracker.addBrightnessChangeCallback(mBrightnessListener, - new HandlerExecutor(mHandler)); + if (!mObserving) { + mObserving = true; + mSecureSettings.registerContentObserverForUser( + BRIGHTNESS_MODE_URI, + false, this, UserHandle.USER_ALL); + } } public void stopObserving() { - final ContentResolver cr = mContext.getContentResolver(); - cr.unregisterContentObserver(this); - mDisplayTracker.removeCallback(mBrightnessListener); + mSecureSettings.unregisterContentObserver(this); + mObserving = false; } } @@ -159,6 +166,8 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig } mBrightnessObserver.startObserving(); + mDisplayTracker.addBrightnessChangeCallback(mBrightnessListener, + new HandlerExecutor(mMainHandler)); mUserTracker.addCallback(mUserChangedCallback, mMainExecutor); // Update the slider and mode before attaching the listener so we don't @@ -166,7 +175,7 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig mUpdateModeRunnable.run(); mUpdateSliderRunnable.run(); - mHandler.sendEmptyMessage(MSG_ATTACH_LISTENER); + mMainHandler.sendEmptyMessage(MSG_ATTACH_LISTENER); } }; @@ -187,9 +196,10 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig } mBrightnessObserver.stopObserving(); + mDisplayTracker.removeCallback(mBrightnessListener); mUserTracker.removeCallback(mUserChangedCallback); - mHandler.sendEmptyMessage(MSG_DETACH_LISTENER); + mMainHandler.sendEmptyMessage(MSG_DETACH_LISTENER); } }; @@ -225,7 +235,7 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig mBrightnessMin = info.brightnessMinimum; // Value is passed as intbits, since this is what the message takes. final int valueAsIntBits = Float.floatToIntBits(info.brightness); - mHandler.obtainMessage(MSG_UPDATE_SLIDER, valueAsIntBits, + mMainHandler.obtainMessage(MSG_UPDATE_SLIDER, valueAsIntBits, inVrMode ? 1 : 0).sendToTarget(); } }; @@ -233,14 +243,14 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() { @Override public void onVrStateChanged(boolean enabled) { - mHandler.obtainMessage(MSG_VR_MODE_CHANGED, enabled ? 1 : 0, 0) + mMainHandler.obtainMessage(MSG_VR_MODE_CHANGED, enabled ? 1 : 0, 0) .sendToTarget(); } }; - private final Handler mHandler = new Handler() { + private final Handler.Callback mHandlerCallback = new Handler.Callback() { @Override - public void handleMessage(Message msg) { + public boolean handleMessage(Message msg) { mExternalChange = true; try { switch (msg.what) { @@ -257,14 +267,18 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig updateVrMode(msg.arg1 != 0); break; default: - super.handleMessage(msg); + return false; + } } finally { mExternalChange = false; } + return true; } }; + private final Handler mMainHandler; + private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() { @Override @@ -274,12 +288,17 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig } }; + @AssistedInject public BrightnessController( Context context, - ToggleSlider control, + @Assisted ToggleSlider control, UserTracker userTracker, DisplayTracker displayTracker, + DisplayManager displayManager, + SecureSettings secureSettings, + @Nullable IVrManager iVrManager, @Main Executor mainExecutor, + @Main Looper mainLooper, @Background Handler bgHandler) { mContext = context; mControl = control; @@ -288,22 +307,23 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig mBackgroundHandler = bgHandler; mUserTracker = userTracker; mDisplayTracker = displayTracker; - mBrightnessObserver = new BrightnessObserver(mHandler); - + mSecureSettings = secureSettings; mDisplayId = mContext.getDisplayId(); - PowerManager pm = context.getSystemService(PowerManager.class); + mDisplayManager = displayManager; + mVrManager = iVrManager; - mDisplayManager = context.getSystemService(DisplayManager.class); - mVrManager = IVrManager.Stub.asInterface(ServiceManager.getService( - Context.VR_SERVICE)); + mMainHandler = new Handler(mainLooper, mHandlerCallback); + mBrightnessObserver = new BrightnessObserver(mMainHandler); } public void registerCallbacks() { + mBackgroundHandler.removeCallbacks(mStartListeningRunnable); mBackgroundHandler.post(mStartListeningRunnable); } /** Unregister all call backs, both to and from the controller */ public void unregisterCallbacks() { + mBackgroundHandler.removeCallbacks(mStopListeningRunnable); mBackgroundHandler.post(mStopListeningRunnable); mControlValueInitialized = false; } @@ -418,38 +438,12 @@ public class BrightnessController implements ToggleSlider.Listener, MirroredBrig mSliderAnimator.start(); } - /** Factory for creating a {@link BrightnessController}. */ - public static class Factory { - private final Context mContext; - private final UserTracker mUserTracker; - private final DisplayTracker mDisplayTracker; - private final Executor mMainExecutor; - private final Handler mBackgroundHandler; - - @Inject - public Factory( - Context context, - UserTracker userTracker, - DisplayTracker displayTracker, - @Main Executor mainExecutor, - @Background Handler bgHandler) { - mContext = context; - mUserTracker = userTracker; - mDisplayTracker = displayTracker; - mMainExecutor = mainExecutor; - mBackgroundHandler = bgHandler; - } + + /** Factory for creating a {@link BrightnessController}. */ + @AssistedFactory + public interface Factory { /** Create a {@link BrightnessController} */ - public BrightnessController create(ToggleSlider toggleSlider) { - return new BrightnessController( - mContext, - toggleSlider, - mUserTracker, - mDisplayTracker, - mMainExecutor, - mBackgroundHandler); - } + BrightnessController create(ToggleSlider toggleSlider); } - } diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java index 182e4569b549..38b1f14e45de 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java @@ -23,7 +23,6 @@ import static android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KE import android.app.Activity; import android.graphics.Rect; import android.os.Bundle; -import android.os.Handler; import android.view.Gravity; import android.view.KeyEvent; import android.view.View; @@ -37,10 +36,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.R; -import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.settings.DisplayTracker; -import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.util.concurrency.DelayableExecutor; @@ -56,26 +52,21 @@ public class BrightnessDialog extends Activity { private BrightnessController mBrightnessController; private final BrightnessSliderController.Factory mToggleSliderFactory; - private final UserTracker mUserTracker; - private final DisplayTracker mDisplayTracker; + private final BrightnessController.Factory mBrightnessControllerFactory; private final DelayableExecutor mMainExecutor; - private final Handler mBackgroundHandler; private final AccessibilityManagerWrapper mAccessibilityMgr; private Runnable mCancelTimeoutRunnable; @Inject public BrightnessDialog( - UserTracker userTracker, - DisplayTracker displayTracker, - BrightnessSliderController.Factory factory, + BrightnessSliderController.Factory brightnessSliderfactory, + BrightnessController.Factory brightnessControllerFactory, @Main DelayableExecutor mainExecutor, - @Background Handler bgHandler, - AccessibilityManagerWrapper accessibilityMgr) { - mUserTracker = userTracker; - mDisplayTracker = displayTracker; - mToggleSliderFactory = factory; + AccessibilityManagerWrapper accessibilityMgr + ) { + mToggleSliderFactory = brightnessSliderfactory; + mBrightnessControllerFactory = brightnessControllerFactory; mMainExecutor = mainExecutor; - mBackgroundHandler = bgHandler; mAccessibilityMgr = accessibilityMgr; } @@ -121,8 +112,7 @@ public class BrightnessDialog extends Activity { controller.init(); frame.addView(controller.getRootView(), MATCH_PARENT, WRAP_CONTENT); - mBrightnessController = new BrightnessController( - this, controller, mUserTracker, mDisplayTracker, mMainExecutor, mBackgroundHandler); + mBrightnessController = mBrightnessControllerFactory.create(controller); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index cfecf7de3780..9399d48be5f3 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -168,7 +168,6 @@ import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.PulseExpansionHandler; -import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.VibratorHelper; @@ -224,6 +223,8 @@ import com.android.systemui.util.Utils; import com.android.systemui.util.time.SystemClock; import com.android.wm.shell.animation.FlingAnimationUtils; +import kotlin.Unit; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; @@ -234,8 +235,6 @@ import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Provider; -import kotlin.Unit; - import kotlinx.coroutines.CoroutineDispatcher; @SysUISingleton @@ -440,8 +439,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private final FalsingCollector mFalsingCollector; private final ShadeHeadsUpTrackerImpl mShadeHeadsUpTracker = new ShadeHeadsUpTrackerImpl(); private final ShadeFoldAnimator mShadeFoldAnimator = new ShadeFoldAnimatorImpl(); - private final ShadeNotificationPresenterImpl mShadeNotificationPresenter = - new ShadeNotificationPresenterImpl(); private boolean mShowIconsWhenExpanded; private int mIndicationBottomPadding; @@ -3323,23 +3320,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump ).printTableData(ipw); } - private final class ShadeNotificationPresenterImpl implements ShadeNotificationPresenter{ - @Override - public RemoteInputController.Delegate createRemoteInputDelegate() { - return mNotificationStackScrollLayoutController.createDelegate(); - } - - @Override - public boolean hasPulsingNotifications() { - return mNotificationListContainer.hasPulsingNotifications(); - } - } - - @Override - public ShadeNotificationPresenter getShadeNotificationPresenter() { - return mShadeNotificationPresenter; - } - @Override public void initDependencies( CentralSurfaces centralSurfaces, diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt index 8d5c30b51677..2532bad1d7a7 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeSurface.kt @@ -18,6 +18,7 @@ package com.android.systemui.shade import android.view.ViewPropertyAnimator import com.android.systemui.statusbar.GestureRecorder import com.android.systemui.statusbar.NotificationShelfController +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.HeadsUpManagerPhone @@ -63,6 +64,9 @@ interface ShadeSurface : ShadeViewController { /** Animates the view from its current alpha to zero then runs the runnable. */ fun fadeOut(startDelayMs: Long, durationMs: Long, endAction: Runnable): ViewPropertyAnimator + /** Returns the NSSL controller. */ + val notificationStackScrollLayoutController: NotificationStackScrollLayoutController + /** Set whether the bouncer is showing. */ fun setBouncerShowing(bouncerShowing: Boolean) diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt index 9aa5eb0cd68b..d5b5c87ec781 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt @@ -19,9 +19,7 @@ import android.view.MotionEvent import android.view.ViewGroup import android.view.ViewTreeObserver import com.android.systemui.keyguard.shared.model.WakefulnessModel -import com.android.systemui.statusbar.RemoteInputController import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController import com.android.systemui.statusbar.phone.HeadsUpAppearanceController import com.android.systemui.statusbar.phone.KeyguardStatusBarView import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController @@ -141,9 +139,6 @@ interface ShadeViewController { /** Returns the StatusBarState. */ val barState: Int - /** Returns the NSSL controller. */ - val notificationStackScrollLayoutController: NotificationStackScrollLayoutController - /** Sets the amount of progress in the status bar launch animation. */ fun applyLaunchAnimationProgress(linearProgress: Float) @@ -261,9 +256,6 @@ interface ShadeViewController { /** Returns the ShadeFoldAnimator. */ val shadeFoldAnimator: ShadeFoldAnimator - /** Returns the ShadeNotificationPresenter. */ - val shadeNotificationPresenter: ShadeNotificationPresenter - companion object { /** * Returns a multiplicative factor to use when determining the falsing threshold for touches @@ -325,16 +317,7 @@ interface ShadeFoldAnimator { fun cancelFoldToAodAnimation() /** Returns the main view of the shade. */ - val view: ViewGroup -} - -/** Handles the shade's interactions with StatusBarNotificationPresenter. */ -interface ShadeNotificationPresenter { - /** Returns a new delegate for some view controller pieces of the remote input process. */ - fun createRemoteInputDelegate(): RemoteInputController.Delegate - - /** Returns whether the screen has temporarily woken up to display notifications. */ - fun hasPulsingNotifications(): Boolean + val view: ViewGroup? } /** diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt new file mode 100644 index 000000000000..287ac528385f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.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.shade + +import android.view.MotionEvent +import android.view.ViewGroup +import android.view.ViewTreeObserver +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow +import com.android.systemui.statusbar.phone.HeadsUpAppearanceController +import java.util.function.Consumer +import javax.inject.Inject + +/** Empty implementation of ShadeViewController for variants with no shade. */ +class ShadeViewControllerEmptyImpl @Inject constructor() : ShadeViewController { + override fun expand(animate: Boolean) {} + override fun expandToQs() {} + override fun expandToNotifications() {} + override val isExpandingOrCollapsing: Boolean = false + override val isExpanded: Boolean = false + override val isPanelExpanded: Boolean = false + override val isShadeFullyExpanded: Boolean = false + override fun collapse(delayed: Boolean, speedUpFactor: Float) {} + override fun collapse(animate: Boolean, delayed: Boolean, speedUpFactor: Float) {} + override fun collapseWithDuration(animationDuration: Int) {} + override fun instantCollapse() {} + override fun animateCollapseQs(fullyCollapse: Boolean) {} + override fun canBeCollapsed(): Boolean = false + override val isCollapsing: Boolean = false + override val isFullyCollapsed: Boolean = false + override val isTracking: Boolean = false + override val isViewEnabled: Boolean = false + override fun setOpenCloseListener(openCloseListener: OpenCloseListener) {} + override fun shouldHideStatusBarIconsWhenExpanded() = false + override fun blockExpansionForCurrentTouch() {} + override fun setTrackingStartedListener(trackingStartedListener: TrackingStartedListener) {} + override fun disableHeader(state1: Int, state2: Int, animated: Boolean) {} + override fun startExpandLatencyTracking() {} + override fun startBouncerPreHideAnimation() {} + override fun dozeTimeTick() {} + override fun resetViews(animate: Boolean) {} + override val barState: Int = 0 + override fun applyLaunchAnimationProgress(linearProgress: Float) {} + override fun closeUserSwitcherIfOpen(): Boolean { + return false + } + override fun onBackPressed() {} + override fun setIsLaunchAnimationRunning(running: Boolean) {} + override fun setAlpha(alpha: Int, animate: Boolean) {} + override fun setAlphaChangeAnimationEndAction(r: Runnable) {} + override fun setPulsing(pulsing: Boolean) {} + override fun setQsScrimEnabled(qsScrimEnabled: Boolean) {} + override fun setAmbientIndicationTop(ambientIndicationTop: Int, ambientTextVisible: Boolean) {} + override fun updateSystemUiStateFlags() {} + override fun updateTouchableRegion() {} + override fun addOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener) {} + override fun removeOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener) {} + override fun postToView(action: Runnable): Boolean { + return false + } + override fun transitionToExpandedShade(delay: Long) {} + override val isUnlockHintRunning: Boolean = false + + override fun resetViewGroupFade() {} + override fun setKeyguardTransitionProgress(keyguardAlpha: Float, keyguardTranslationY: Int) {} + override fun setOverStretchAmount(amount: Float) {} + override fun setKeyguardStatusBarAlpha(alpha: Float) {} + override fun showAodUi() {} + override fun isFullyExpanded(): Boolean { + return false + } + override fun handleExternalTouch(event: MotionEvent): Boolean { + return false + } + override fun startTrackingExpansionFromStatusBar() {} + override val shadeHeadsUpTracker = ShadeHeadsUpTrackerEmptyImpl() + override val shadeFoldAnimator = ShadeFoldAnimatorEmptyImpl() +} + +class ShadeHeadsUpTrackerEmptyImpl : ShadeHeadsUpTracker { + override fun addTrackingHeadsUpListener(listener: Consumer<ExpandableNotificationRow>) {} + override fun removeTrackingHeadsUpListener(listener: Consumer<ExpandableNotificationRow>) {} + override fun setHeadsUpAppearanceController( + headsUpAppearanceController: HeadsUpAppearanceController? + ) {} + override val trackedHeadsUpNotification: ExpandableNotificationRow? = null +} + +class ShadeFoldAnimatorEmptyImpl : ShadeFoldAnimator { + override fun prepareFoldToAodAnimation() {} + override fun startFoldToAodAnimation( + startAction: Runnable, + endAction: Runnable, + cancelAction: Runnable, + ) {} + override fun cancelFoldToAodAnimation() {} + override val view: ViewGroup? = null +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java index b2a3780c1024..867e08b2e743 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification.row; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import dagger.Binds; import dagger.Module; @@ -58,9 +59,13 @@ public abstract class NotificationRowModule { @ElementsIntoSet @Named(NOTIF_REMOTEVIEWS_FACTORIES) static Set<NotifRemoteViewsFactory> provideNotifRemoteViewsFactories( - FeatureFlags featureFlags + FeatureFlags featureFlags, + PrecomputedTextViewFactory precomputedTextViewFactory ) { final Set<NotifRemoteViewsFactory> replacementFactories = new HashSet<>(); + if (featureFlags.isEnabled(Flags.PRECOMPUTED_TEXT)) { + replacementFactories.add(precomputedTextViewFactory); + } return replacementFactories; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt new file mode 100644 index 000000000000..b0023305ecdd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextViewFactory.kt @@ -0,0 +1,41 @@ +/* + * 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.statusbar.notification.row + +import android.content.Context +import android.util.AttributeSet +import android.view.View +import android.widget.TextView +import com.android.internal.widget.ImageFloatingTextView +import javax.inject.Inject + +class PrecomputedTextViewFactory @Inject constructor() : NotifRemoteViewsFactory { + override fun instantiate( + parent: View?, + name: String, + context: Context, + attrs: AttributeSet + ): View? { + return when (name) { + TextView::class.java.name, + TextView::class.java.simpleName -> PrecomputedTextView(context, attrs) + ImageFloatingTextView::class.java.name -> + PrecomputedImageFloatingTextView(context, attrs) + else -> null + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index 2d8f371aadac..1f9c9f2b8c14 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -160,6 +160,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp private KeyguardViewController mKeyguardViewController; private DozeScrimController mDozeScrimController; private KeyguardViewMediator mKeyguardViewMediator; + private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private PendingAuthenticated mPendingAuthenticated = null; private boolean mHasScreenTurnedOnSinceAuthenticating; private boolean mFadedAwayAfterWakeAndUnlock; @@ -280,7 +281,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp LatencyTracker latencyTracker, ScreenOffAnimationController screenOffAnimationController, VibratorHelper vibrator, - SystemClock systemClock + SystemClock systemClock, + StatusBarKeyguardViewManager statusBarKeyguardViewManager ) { mPowerManager = powerManager; mUpdateMonitor = keyguardUpdateMonitor; @@ -308,6 +310,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp mVibratorHelper = vibrator; mLogger = biometricUnlockLogger; mSystemClock = systemClock; + mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; dumpManager.registerDumpable(getClass().getName(), this); } @@ -449,8 +452,19 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp // During wake and unlock, we need to draw black before waking up to avoid abrupt // brightness changes due to display state transitions. Runnable wakeUp = ()-> { - if (!wasDeviceInteractive || mUpdateMonitor.isDreaming()) { + // Check to see if we are still locked when we are waking and unlocking from dream. + // This runnable should be executed after unlock. If that's true, we could be not + // dreaming, but still locked. In this case, we should attempt to authenticate instead + // of waking up. + if (mode == MODE_WAKE_AND_UNLOCK_FROM_DREAM + && !mKeyguardStateController.isUnlocked() + && !mUpdateMonitor.isDreaming()) { + // Post wakeUp runnable is called from a callback in keyguard. + mHandler.post(() -> mKeyguardViewController.notifyKeyguardAuthenticated( + false /* primaryAuth */)); + } else if (!wasDeviceInteractive || mUpdateMonitor.isDreaming()) { mLogger.i("bio wakelock: Authenticated, waking up..."); + mPowerManager.wakeUp( mSystemClock.uptimeMillis(), PowerManager.WAKE_REASON_BIOMETRIC, @@ -462,7 +476,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp Trace.endSection(); }; - if (mMode != MODE_NONE) { + if (mMode != MODE_NONE && mMode != MODE_WAKE_AND_UNLOCK_FROM_DREAM) { wakeUp.run(); } switch (mMode) { @@ -484,6 +498,10 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp Trace.endSection(); break; case MODE_WAKE_AND_UNLOCK_FROM_DREAM: + // In the case of waking and unlocking from dream, waking up is delayed until after + // unlock is complete to avoid conflicts during each sequence's transitions. + mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(wakeUp); + // Execution falls through here to proceed unlocking. case MODE_WAKE_AND_UNLOCK_PULSING: case MODE_WAKE_AND_UNLOCK: if (mMode == MODE_WAKE_AND_UNLOCK_PULSING) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt index f0fc1432c5a3..862f169b2176 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt @@ -30,6 +30,7 @@ import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.shade.ShadeController import com.android.systemui.shade.ShadeLogger +import com.android.systemui.shade.ShadeViewController import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.unfold.SysUIUnfoldComponent @@ -51,6 +52,7 @@ class PhoneStatusBarViewController private constructor( @Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider?, private val centralSurfaces: CentralSurfaces, private val shadeController: ShadeController, + private val shadeViewController: ShadeViewController, private val shadeLogger: ShadeLogger, private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?, private val userChipViewModel: StatusBarUserChipViewModel, @@ -165,20 +167,20 @@ class PhoneStatusBarViewController private constructor( if (event.action == MotionEvent.ACTION_DOWN) { // If the view that would receive the touch is disabled, just have status // bar eat the gesture. - if (!centralSurfaces.shadeViewController.isViewEnabled) { + if (!shadeViewController.isViewEnabled) { shadeLogger.logMotionEvent(event, "onTouchForwardedFromStatusBar: panel view disabled") return true } - if (centralSurfaces.shadeViewController.isFullyCollapsed && + if (shadeViewController.isFullyCollapsed && event.y < 1f) { // b/235889526 Eat events on the top edge of the phone when collapsed shadeLogger.logMotionEvent(event, "top edge touch ignored") return true } - centralSurfaces.shadeViewController.startTrackingExpansionFromStatusBar() + shadeViewController.startTrackingExpansionFromStatusBar() } - return centralSurfaces.shadeViewController.handleExternalTouch(event) + return shadeViewController.handleExternalTouch(event) } } @@ -222,6 +224,7 @@ class PhoneStatusBarViewController private constructor( private val userChipViewModel: StatusBarUserChipViewModel, private val centralSurfaces: CentralSurfaces, private val shadeController: ShadeController, + private val shadeViewController: ShadeViewController, private val shadeLogger: ShadeLogger, private val viewUtil: ViewUtil, private val configurationController: ConfigurationController, @@ -241,6 +244,7 @@ class PhoneStatusBarViewController private constructor( progressProvider.getOrNull(), centralSurfaces, shadeController, + shadeViewController, shadeLogger, statusBarMoveFromCenterAnimationController, userChipViewModel, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java index 481cf3ceb197..9a295e63fb9e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java @@ -21,6 +21,7 @@ import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.statusbar.window.StatusBarWindowController; @@ -35,6 +36,7 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener private final NotificationShadeWindowController mNotificationShadeWindowController; private final StatusBarWindowController mStatusBarWindowController; private final ShadeViewController mShadeViewController; + private final NotificationStackScrollLayoutController mNsslController; private final KeyguardBypassController mKeyguardBypassController; private final HeadsUpManagerPhone mHeadsUpManager; private final StatusBarStateController mStatusBarStateController; @@ -45,14 +47,15 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener NotificationShadeWindowController notificationShadeWindowController, StatusBarWindowController statusBarWindowController, ShadeViewController shadeViewController, + NotificationStackScrollLayoutController nsslController, KeyguardBypassController keyguardBypassController, HeadsUpManagerPhone headsUpManager, StatusBarStateController statusBarStateController, NotificationRemoteInputManager notificationRemoteInputManager) { - mNotificationShadeWindowController = notificationShadeWindowController; mStatusBarWindowController = statusBarWindowController; mShadeViewController = shadeViewController; + mNsslController = nsslController; mKeyguardBypassController = keyguardBypassController; mHeadsUpManager = headsUpManager; mStatusBarStateController = statusBarStateController; @@ -85,8 +88,7 @@ public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener //animation // is finished. mHeadsUpManager.setHeadsUpGoingAway(true); - mShadeViewController.getNotificationStackScrollLayoutController() - .runAfterAnimationFinished(() -> { + mNsslController.runAfterAnimationFinished(() -> { if (!mHeadsUpManager.hasPinnedHeadsUp()) { mNotificationShadeWindowController.setHeadsUpShowing(false); mHeadsUpManager.setHeadsUpGoingAway(false); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 35285b222f63..e63fecd33383 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -28,7 +28,6 @@ import android.service.vr.IVrStateCallbacks; import android.util.Log; import android.util.Slog; import android.view.View; -import android.view.accessibility.AccessibilityManager; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.InitController; @@ -50,7 +49,6 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.AboveShelfObserver; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; import com.android.systemui.statusbar.notification.domain.interactor.NotificationsInteractor; @@ -59,7 +57,6 @@ import com.android.systemui.statusbar.notification.interruption.NotificationInte import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener; -import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; @@ -78,21 +75,18 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu private final NotifShadeEventSource mNotifShadeEventSource; private final NotificationMediaManager mMediaManager; private final NotificationGutsManager mGutsManager; - private final ShadeViewController mNotificationPanel; private final HeadsUpManagerPhone mHeadsUpManager; private final AboveShelfObserver mAboveShelfObserver; private final DozeScrimController mDozeScrimController; private final CentralSurfaces mCentralSurfaces; private final NotificationsInteractor mNotificationsInteractor; + private final NotificationStackScrollLayoutController mNsslController; private final LockscreenShadeTransitionController mShadeTransitionController; private final PowerInteractor mPowerInteractor; private final CommandQueue mCommandQueue; - - private final AccessibilityManager mAccessibilityManager; private final KeyguardManager mKeyguardManager; private final NotificationShadeWindowController mNotificationShadeWindowController; - private final NotifPipelineFlags mNotifPipelineFlags; private final IStatusBarService mBarService; private final DynamicPrivacyController mDynamicPrivacyController; private final NotificationListContainer mNotifListContainer; @@ -123,11 +117,9 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu NotifShadeEventSource notifShadeEventSource, NotificationMediaManager notificationMediaManager, NotificationGutsManager notificationGutsManager, - LockscreenGestureLogger lockscreenGestureLogger, InitController initController, NotificationInterruptStateProvider notificationInterruptStateProvider, NotificationRemoteInputManager remoteInputManager, - NotifPipelineFlags notifPipelineFlags, NotificationRemoteInputManager.Callback remoteInputManagerCallback, NotificationListContainer notificationListContainer) { mActivityStarter = activityStarter; @@ -139,6 +131,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu // TODO: use KeyguardStateController#isOccluded to remove this dependency mCentralSurfaces = centralSurfaces; mNotificationsInteractor = notificationsInteractor; + mNsslController = stackScrollerController; mShadeTransitionController = shadeTransitionController; mPowerInteractor = powerInteractor; mCommandQueue = commandQueue; @@ -149,10 +142,8 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu mGutsManager = notificationGutsManager; mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView()); mNotificationShadeWindowController = notificationShadeWindowController; - mNotifPipelineFlags = notifPipelineFlags; mAboveShelfObserver.setListener(statusBarWindow.findViewById( R.id.notification_container_parent)); - mAccessibilityManager = context.getSystemService(AccessibilityManager.class); mDozeScrimController = dozeScrimController; mKeyguardManager = context.getSystemService(KeyguardManager.class); mBarService = IStatusBarService.Stub.asInterface( @@ -170,7 +161,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu } remoteInputManager.setUpWithCallback( remoteInputManagerCallback, - mNotificationPanel.getShadeNotificationPresenter().createRemoteInputDelegate()); + mNsslController.createDelegate()); initController.addPostInitTask(() -> { mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied); @@ -202,7 +193,7 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu } private void maybeEndAmbientPulse() { - if (mNotificationPanel.getShadeNotificationPresenter().hasPulsingNotifications() + if (mNsslController.getNotificationListContainer().hasPulsingNotifications() && !mHeadsUpManager.hasNotifications()) { // We were showing a pulse for a notification, but no notifications are pulsing anymore. // Finish the pulse. @@ -272,22 +263,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, CommandQu } }; - private final CheckSaveListener mCheckSaveListener = new CheckSaveListener() { - @Override - public void checkSave(Runnable saveImportance, StatusBarNotification sbn) { - // If the user has security enabled, show challenge if the setting is changed. - if (mLockscreenUserManager.isLockscreenPublicMode(sbn.getUser().getIdentifier()) - && mKeyguardManager.isKeyguardLocked()) { - onLockedNotificationImportanceChange(() -> { - saveImportance.run(); - return true; - }); - } else { - saveImportance.run(); - } - } - }; - private final OnSettingsClickListener mOnSettingsClickListener = new OnSettingsClickListener() { @Override public void onSettingsClick(String key) { diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt index eed7950abacb..cbe402017c41 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt @@ -94,7 +94,7 @@ constructor( wakefulnessLifecycle.addObserver(this) // TODO(b/254878364): remove this call to NPVC.getView() - getShadeFoldAnimator().view.repeatWhenAttached { + getShadeFoldAnimator().view?.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { listenForDozing(this) } } } @@ -161,10 +161,9 @@ constructor( // but we should wait for the initial animation preparations to be drawn // (setting initial alpha/translation) // TODO(b/254878364): remove this call to NPVC.getView() - OneShotPreDrawListener.add( - getShadeFoldAnimator().view, - onReady - ) + getShadeFoldAnimator().view?.let { + OneShotPreDrawListener.add(it, onReady) + } } else { // No animation, call ready callback immediately onReady.run() diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt index da9ceb47446a..212dad78d5b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt @@ -8,6 +8,7 @@ import android.view.ViewGroup import android.widget.LinearLayout import android.widget.RelativeLayout import androidx.test.filters.SmallTest +import com.android.app.animation.Interpolators import com.android.systemui.SysuiTestCase import com.android.systemui.util.children import junit.framework.Assert.assertEquals @@ -19,7 +20,6 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import com.android.app.animation.Interpolators @SmallTest @RunWith(AndroidTestingRunner::class) @@ -178,7 +178,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { } @Test - fun animatesRootAndChildren() { + fun animatesRootAndChildren_withoutExcludedViews() { setUpRootWithChildren() val success = ViewHierarchyAnimator.animate(rootView) @@ -208,6 +208,40 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { } @Test + fun animatesRootAndChildren_withExcludedViews() { + setUpRootWithChildren() + + val success = ViewHierarchyAnimator.animate( + rootView, + excludedViews = setOf(rootView.getChildAt(0)) + ) + // Change all bounds. + rootView.measure( + View.MeasureSpec.makeMeasureSpec(180, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY) + ) + rootView.layout(10 /* l */, 20 /* t */, 200 /* r */, 120 /* b */) + + assertTrue(success) + assertNotNull(rootView.getTag(R.id.tag_animator)) + assertNull(rootView.getChildAt(0).getTag(R.id.tag_animator)) + assertNotNull(rootView.getChildAt(1).getTag(R.id.tag_animator)) + // The initial values for the affected views should be those of the previous layout, while + // the excluded view should be at the final values from the beginning. + checkBounds(rootView, l = 0, t = 0, r = 200, b = 100) + checkBounds(rootView.getChildAt(0), l = 0, t = 0, r = 90, b = 100) + checkBounds(rootView.getChildAt(1), l = 100, t = 0, r = 200, b = 100) + endAnimation(rootView) + assertNull(rootView.getTag(R.id.tag_animator)) + assertNull(rootView.getChildAt(0).getTag(R.id.tag_animator)) + assertNull(rootView.getChildAt(1).getTag(R.id.tag_animator)) + // The end values should be those of the latest layout. + checkBounds(rootView, l = 10, t = 20, r = 200, b = 120) + checkBounds(rootView.getChildAt(0), l = 0, t = 0, r = 90, b = 100) + checkBounds(rootView.getChildAt(1), l = 90, t = 0, r = 180, b = 100) + } + + @Test fun animatesInvisibleViews() { rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */) rootView.visibility = View.INVISIBLE diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt index b4b307301138..9a90a5ceb259 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt @@ -216,9 +216,6 @@ public class MediaControlPanelTest : SysuiTestCase() { @Mock private lateinit var recCardTitle: TextView @Mock private lateinit var coverItem: ImageView @Mock private lateinit var matrix: Matrix - private lateinit var coverItem1: ImageView - private lateinit var coverItem2: ImageView - private lateinit var coverItem3: ImageView private lateinit var recTitle1: TextView private lateinit var recTitle2: TextView private lateinit var recTitle3: TextView @@ -233,7 +230,6 @@ public class MediaControlPanelTest : SysuiTestCase() { FakeFeatureFlags().apply { this.set(Flags.UMO_SURFACE_RIPPLE, false) this.set(Flags.UMO_TURBULENCE_NOISE, false) - this.set(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE, false) } @Mock private lateinit var globalSettings: GlobalSettings @@ -467,21 +463,25 @@ public class MediaControlPanelTest : SysuiTestCase() { recSubtitle3 = TextView(context) whenever(recommendationViewHolder.recommendations).thenReturn(view) - whenever(recommendationViewHolder.cardIcon).thenReturn(appIcon) - - // Add a recommendation item - coverItem1 = ImageView(context).also { it.setId(R.id.media_cover1) } - coverItem2 = ImageView(context).also { it.setId(R.id.media_cover2) } - coverItem3 = ImageView(context).also { it.setId(R.id.media_cover3) } - + whenever(recommendationViewHolder.mediaAppIcons) + .thenReturn(listOf(recAppIconItem, recAppIconItem, recAppIconItem)) + whenever(recommendationViewHolder.cardTitle).thenReturn(recCardTitle) whenever(recommendationViewHolder.mediaCoverItems) - .thenReturn(listOf(coverItem1, coverItem2, coverItem3)) + .thenReturn(listOf(coverItem, coverItem, coverItem)) whenever(recommendationViewHolder.mediaCoverContainers) .thenReturn(listOf(coverContainer1, coverContainer2, coverContainer3)) whenever(recommendationViewHolder.mediaTitles) .thenReturn(listOf(recTitle1, recTitle2, recTitle3)) whenever(recommendationViewHolder.mediaSubtitles) .thenReturn(listOf(recSubtitle1, recSubtitle2, recSubtitle3)) + whenever(recommendationViewHolder.mediaProgressBars) + .thenReturn(listOf(recProgressBar1, recProgressBar2, recProgressBar3)) + whenever(coverItem.imageMatrix).thenReturn(matrix) + + // set ids for recommendation containers + whenever(coverContainer1.id).thenReturn(1) + whenever(coverContainer2.id).thenReturn(2) + whenever(coverContainer3.id).thenReturn(3) whenever(recommendationViewHolder.gutsViewHolder).thenReturn(gutsViewHolder) @@ -1561,7 +1561,8 @@ public class MediaControlPanelTest : SysuiTestCase() { verify(viewHolder.player).contentDescription = descriptionCaptor.capture() val description = descriptionCaptor.value.toString() - assertThat(description).contains(REC_APP_NAME) + assertThat(description) + .isEqualTo(context.getString(R.string.controls_media_smartspace_rec_header)) } @Test @@ -1585,7 +1586,8 @@ public class MediaControlPanelTest : SysuiTestCase() { verify(viewHolder.player).contentDescription = descriptionCaptor.capture() val description = descriptionCaptor.value.toString() - assertThat(description).contains(REC_APP_NAME) + assertThat(description) + .isEqualTo(context.getString(R.string.controls_media_smartspace_rec_header)) } @Test @@ -2151,7 +2153,6 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun bindRecommendation_setAfterExecutors() { - setupUpdatedRecommendationViewHolder() val albumArt = getColorIcon(Color.RED) val data = smartspaceData.copy( @@ -2189,7 +2190,6 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun bindRecommendationWithProgressBars() { useRealConstraintSets() - setupUpdatedRecommendationViewHolder() val albumArt = getColorIcon(Color.RED) val bundle = Bundle().apply { @@ -2236,7 +2236,6 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun bindRecommendation_carouselNotFitThreeRecs_OrientationPortrait() { useRealConstraintSets() - setupUpdatedRecommendationViewHolder() val albumArt = getColorIcon(Color.RED) val data = smartspaceData.copy( @@ -2290,7 +2289,6 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun bindRecommendation_carouselNotFitThreeRecs_OrientationLandscape() { useRealConstraintSets() - setupUpdatedRecommendationViewHolder() val albumArt = getColorIcon(Color.RED) val data = smartspaceData.copy( @@ -2505,27 +2503,6 @@ public class MediaControlPanelTest : SysuiTestCase() { verify(activityStarter).postStartActivityDismissingKeyguard(eq(pendingIntent)) } - private fun setupUpdatedRecommendationViewHolder() { - fakeFeatureFlag.set(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE, true) - whenever(recommendationViewHolder.mediaAppIcons) - .thenReturn(listOf(recAppIconItem, recAppIconItem, recAppIconItem)) - whenever(recommendationViewHolder.cardTitle).thenReturn(recCardTitle) - whenever(recommendationViewHolder.mediaCoverContainers) - .thenReturn(listOf(coverContainer1, coverContainer2, coverContainer3)) - whenever(recommendationViewHolder.mediaCoverItems) - .thenReturn(listOf(coverItem, coverItem, coverItem)) - whenever(recommendationViewHolder.mediaProgressBars) - .thenReturn(listOf(recProgressBar1, recProgressBar2, recProgressBar3)) - whenever(recommendationViewHolder.mediaSubtitles) - .thenReturn(listOf(recSubtitle1, recSubtitle2, recSubtitle3)) - whenever(coverItem.imageMatrix).thenReturn(matrix) - - // set ids for recommendation containers - whenever(coverContainer1.id).thenReturn(1) - whenever(coverContainer2.id).thenReturn(2) - whenever(coverContainer3.id).thenReturn(3) - } - private fun getColorIcon(color: Int): Icon { val bmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888) val canvas = Canvas(bmp) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt index c9956f36dbeb..ba97df910e43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaViewControllerTest.kt @@ -201,8 +201,8 @@ class MediaViewControllerTest : SysuiTestCase() { whenever(mockCopiedState.widgetStates) .thenReturn( mutableMapOf( - R.id.media_title1 to mediaTitleWidgetState, - R.id.media_subtitle1 to mediaSubTitleWidgetState, + R.id.media_title to mediaTitleWidgetState, + R.id.media_subtitle to mediaSubTitleWidgetState, R.id.media_cover1_container to mediaContainerWidgetState ) ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt index a0d8f98a4ad1..9d9d0c7de2ad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt @@ -154,6 +154,21 @@ class QSPanelControllerTest : SysuiTestCase() { verify(qsPanel).setCanCollapse(true) } + @Test + fun multipleListeningOnlyCallsBrightnessControllerOnce() { + controller.setListening(true, true) + controller.setListening(true, false) + controller.setListening(true, true) + + verify(brightnessController).registerCallbacks() + + controller.setListening(false, true) + controller.setListening(false, false) + controller.setListening(false, true) + + verify(brightnessController).unregisterCallbacks() + } + private fun setShouldUseSplitShade(shouldUse: Boolean) { testableResources.addOverride(R.bool.config_use_split_notification_shade, shouldUse) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt new file mode 100644 index 000000000000..2b7840533df2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessControllerTest.kt @@ -0,0 +1,105 @@ +/* + * 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.settings.brightness + +import android.hardware.display.DisplayManager +import android.os.Handler +import android.service.vr.IVrManager +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.testing.TestableLooper.RunWithLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.settings.DisplayTracker +import com.android.systemui.settings.UserTracker +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.settings.FakeSettings +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper +class BrightnessControllerTest : SysuiTestCase() { + + private val executor = FakeExecutor(FakeSystemClock()) + private val secureSettings = FakeSettings() + @Mock private lateinit var toggleSlider: ToggleSlider + @Mock private lateinit var userTracker: UserTracker + @Mock private lateinit var displayTracker: DisplayTracker + @Mock private lateinit var displayManager: DisplayManager + @Mock private lateinit var iVrManager: IVrManager + + private lateinit var testableLooper: TestableLooper + + private lateinit var underTest: BrightnessController + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + testableLooper = TestableLooper.get(this) + + underTest = + BrightnessController( + context, + toggleSlider, + userTracker, + displayTracker, + displayManager, + secureSettings, + iVrManager, + executor, + mock(), + Handler(testableLooper.looper) + ) + } + + @Test + fun registerCallbacksMultipleTimes_onlyOneRegistration() { + val repeats = 100 + repeat(repeats) { underTest.registerCallbacks() } + val messagesProcessed = testableLooper.processMessagesNonBlocking(repeats) + + verify(displayTracker).addBrightnessChangeCallback(any(), any()) + verify(iVrManager).registerListener(any()) + + assertThat(messagesProcessed).isEqualTo(1) + } + + @Test + fun unregisterCallbacksMultipleTimes_onlyOneUnregistration() { + val repeats = 100 + underTest.registerCallbacks() + testableLooper.processAllMessages() + + repeat(repeats) { underTest.unregisterCallbacks() } + val messagesProcessed = testableLooper.processMessagesNonBlocking(repeats) + + verify(displayTracker).removeCallback(any()) + verify(iVrManager).unregisterListener(any()) + + assertThat(messagesProcessed).isEqualTo(1) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt index 5c35913f6e20..ed1397ff7013 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.settings.brightness import android.content.Intent import android.graphics.Rect -import android.os.Handler import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View @@ -29,8 +28,6 @@ import androidx.test.rule.ActivityTestRule import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.activity.SingleActivityFactory -import com.android.systemui.settings.FakeDisplayTracker -import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.concurrency.FakeExecutor @@ -53,28 +50,24 @@ import org.mockito.MockitoAnnotations @TestableLooper.RunWithLooper class BrightnessDialogTest : SysuiTestCase() { - @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var brightnessSliderControllerFactory: BrightnessSliderController.Factory - @Mock private lateinit var backgroundHandler: Handler @Mock private lateinit var brightnessSliderController: BrightnessSliderController + @Mock private lateinit var brightnessControllerFactory: BrightnessController.Factory + @Mock private lateinit var brightnessController: BrightnessController @Mock private lateinit var accessibilityMgr: AccessibilityManagerWrapper private val clock = FakeSystemClock() private val mainExecutor = FakeExecutor(clock) - private var displayTracker = FakeDisplayTracker(mContext) - @Rule @JvmField var activityRule = ActivityTestRule( /* activityFactory= */ SingleActivityFactory { TestDialog( - userTracker, - displayTracker, brightnessSliderControllerFactory, + brightnessControllerFactory, mainExecutor, - backgroundHandler, accessibilityMgr ) }, @@ -88,6 +81,7 @@ class BrightnessDialogTest : SysuiTestCase() { `when`(brightnessSliderControllerFactory.create(any(), any())) .thenReturn(brightnessSliderController) `when`(brightnessSliderController.rootView).thenReturn(View(context)) + `when`(brightnessControllerFactory.create(any())).thenReturn(brightnessController) } @After @@ -178,19 +172,15 @@ class BrightnessDialogTest : SysuiTestCase() { } class TestDialog( - userTracker: UserTracker, - displayTracker: FakeDisplayTracker, brightnessSliderControllerFactory: BrightnessSliderController.Factory, + brightnessControllerFactory: BrightnessController.Factory, mainExecutor: DelayableExecutor, - backgroundHandler: Handler, accessibilityMgr: AccessibilityManagerWrapper ) : BrightnessDialog( - userTracker, - displayTracker, brightnessSliderControllerFactory, + brightnessControllerFactory, mainExecutor, - backgroundHandler, accessibilityMgr ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index 89f8bdbfe05b..479803e1dfac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -142,7 +142,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle, mAuthController, mStatusBarStateController, mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper, - mSystemClock + mSystemClock, + mStatusBarKeyguardViewManager ); mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager); mBiometricUnlockController.addListener(mBiometricUnlockEventsListener); @@ -464,6 +465,69 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { } @Test + public void onSideFingerprintSuccess_dreaming_unlockThenWake() { + when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true); + when(mWakefulnessLifecycle.getLastWakeReason()) + .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON); + final ArgumentCaptor<Runnable> afterKeyguardGoneRunnableCaptor = + ArgumentCaptor.forClass(Runnable.class); + givenDreamingLocked(); + mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT, true); + + // Make sure the BiometricUnlockController has registered a callback for when the keyguard + // is gone + verify(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable( + afterKeyguardGoneRunnableCaptor.capture()); + // Ensure that the power hasn't been told to wake up yet. + verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString()); + // Check that the keyguard has been told to unlock. + verify(mKeyguardViewMediator).onWakeAndUnlocking(); + + // Simulate the keyguard disappearing. + afterKeyguardGoneRunnableCaptor.getValue().run(); + // Verify that the power manager has been told to wake up now. + verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString()); + } + + @Test + public void onSideFingerprintSuccess_dreaming_unlockIfStillLockedNotDreaming() { + when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true); + when(mWakefulnessLifecycle.getLastWakeReason()) + .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON); + final ArgumentCaptor<Runnable> afterKeyguardGoneRunnableCaptor = + ArgumentCaptor.forClass(Runnable.class); + givenDreamingLocked(); + mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT, true); + + // Make sure the BiometricUnlockController has registered a callback for when the keyguard + // is gone + verify(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable( + afterKeyguardGoneRunnableCaptor.capture()); + // Ensure that the power hasn't been told to wake up yet. + verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString()); + // Check that the keyguard has been told to unlock. + verify(mKeyguardViewMediator).onWakeAndUnlocking(); + + when(mUpdateMonitor.isDreaming()).thenReturn(false); + when(mKeyguardStateController.isUnlocked()).thenReturn(false); + + // Simulate the keyguard disappearing. + afterKeyguardGoneRunnableCaptor.getValue().run(); + + final ArgumentCaptor<Runnable> dismissKeyguardRunnableCaptor = + ArgumentCaptor.forClass(Runnable.class); + verify(mHandler).post(dismissKeyguardRunnableCaptor.capture()); + + // Verify that the power manager was not told to wake up. + verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString()); + + dismissKeyguardRunnableCaptor.getValue().run(); + // Verify that the keyguard controller is told to unlock. + verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false)); + } + + + @Test public void onSideFingerprintSuccess_oldPowerButtonPress_playHaptic() { // GIVEN side fingerprint enrolled, last wake reason was power button when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true); @@ -537,6 +601,11 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean()); } + private void givenDreamingLocked() { + when(mUpdateMonitor.isDreaming()).thenReturn(true); + when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); + } + private void givenFingerprintModeUnlockCollapsing() { when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt index 2d96e594592c..c8ec1bf4af9f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt @@ -204,6 +204,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { userChipViewModel, centralSurfacesImpl, shadeControllerImpl, + shadeViewController, shadeLogger, viewUtil, configurationController diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index 5bd6ff4e73f2..cd8aaa2685c2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -42,7 +42,6 @@ import com.android.systemui.settings.FakeDisplayTracker; import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.shade.QuickSettingsController; import com.android.systemui.shade.ShadeController; -import com.android.systemui.shade.ShadeNotificationPresenter; import com.android.systemui.shade.ShadeViewController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.LockscreenShadeTransitionController; @@ -52,7 +51,6 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; @@ -86,14 +84,11 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mock(NotificationsInteractor.class); private final KeyguardStateController mKeyguardStateController = mock(KeyguardStateController.class); - private final NotifPipelineFlags mNotifPipelineFlags = mock(NotifPipelineFlags.class); private final InitController mInitController = new InitController(); @Before public void setup() { mMetricsLogger = new FakeMetricsLogger(); - LockscreenGestureLogger lockscreenGestureLogger = new LockscreenGestureLogger( - mMetricsLogger); mCommandQueue = new CommandQueue(mContext, new FakeDisplayTracker(mContext)); mDependency.injectTestDependency(StatusBarStateController.class, mock(SysuiStatusBarStateController.class)); @@ -111,8 +106,6 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources()); ShadeViewController shadeViewController = mock(ShadeViewController.class); - when(shadeViewController.getShadeNotificationPresenter()) - .thenReturn(mock(ShadeNotificationPresenter.class)); mStatusBarNotificationPresenter = new StatusBarNotificationPresenter( mContext, shadeViewController, @@ -135,11 +128,9 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mock(NotifShadeEventSource.class), mock(NotificationMediaManager.class), mock(NotificationGutsManager.class), - lockscreenGestureLogger, mInitController, mNotificationInterruptStateProvider, mock(NotificationRemoteInputManager.class), - mNotifPipelineFlags, mock(NotificationRemoteInputManager.Callback.class), mock(NotificationListContainer.class)); mInitController.executePostInitTasks(); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index c1239d53058c..502e0ecac6fb 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -256,7 +256,7 @@ import java.util.function.Predicate; public final class ActiveServices { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActiveServices" : TAG_AM; private static final String TAG_MU = TAG + POSTFIX_MU; - private static final String TAG_SERVICE = TAG + POSTFIX_SERVICE; + static final String TAG_SERVICE = TAG + POSTFIX_SERVICE; private static final String TAG_SERVICE_EXECUTING = TAG + POSTFIX_SERVICE_EXECUTING; private static final boolean DEBUG_DELAYED_SERVICE = DEBUG_SERVICE; @@ -850,8 +850,7 @@ public final class ActiveServices { // Service.startForeground()), at that point we will consult the BFSL check and the timeout // and make the necessary decisions. setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, r, userId, - backgroundStartPrivileges, false /* isBindService */, - !fgRequired /* isStartService */); + backgroundStartPrivileges, false /* isBindService */); if (!mAm.mUserController.exists(r.userId)) { Slog.w(TAG, "Trying to start service with non-existent user! " + r.userId); @@ -894,7 +893,7 @@ public final class ActiveServices { if (fgRequired) { logFgsBackgroundStart(r); - if (r.mAllowStartForeground == REASON_DENIED && isBgFgsRestrictionEnabled(r)) { + if (!r.isFgsAllowedStart() && isBgFgsRestrictionEnabled(r)) { String msg = "startForegroundService() not allowed due to " + "mAllowStartForeground false: service " + r.shortInstanceName; @@ -1060,7 +1059,7 @@ public final class ActiveServices { // Use that as a shortcut if possible to avoid having to recheck all the conditions. final boolean whileInUseAllowsUiJobScheduling = ActivityManagerService.doesReasonCodeAllowSchedulingUserInitiatedJobs( - r.mAllowWhileInUsePermissionInFgsReason); + r.getFgsAllowWIU()); r.updateAllowUiJobScheduling(whileInUseAllowsUiJobScheduling || mAm.canScheduleUserInitiatedJobs(callingUid, callingPid, callingPackage)); } else { @@ -2178,12 +2177,12 @@ public final class ActiveServices { // on a SHORT_SERVICE FGS. // See if the app could start an FGS or not. - r.mAllowStartForeground = REASON_DENIED; + r.clearFgsAllowStart(); setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(), r.appInfo.uid, r.intent.getIntent(), r, r.userId, BackgroundStartPrivileges.NONE, - false /* isBindService */, false /* isStartService */); - if (r.mAllowStartForeground == REASON_DENIED) { + false /* isBindService */); + if (!r.isFgsAllowedStart()) { Slog.w(TAG_SERVICE, "FGS type change to/from SHORT_SERVICE: " + " BFSL DENIED."); } else { @@ -2191,13 +2190,13 @@ public final class ActiveServices { Slog.w(TAG_SERVICE, "FGS type change to/from SHORT_SERVICE: " + " BFSL Allowed: " + PowerExemptionManager.reasonCodeToString( - r.mAllowStartForeground)); + r.getFgsAllowStart())); } } final boolean fgsStartAllowed = !isBgFgsRestrictionEnabledForService - || (r.mAllowStartForeground != REASON_DENIED); + || r.isFgsAllowedStart(); if (fgsStartAllowed) { if (isNewTypeShortFgs) { @@ -2246,7 +2245,7 @@ public final class ActiveServices { setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(), r.appInfo.uid, r.intent.getIntent(), r, r.userId, BackgroundStartPrivileges.NONE, - false /* isBindService */, false /* isStartService */); + false /* isBindService */); final String temp = "startForegroundDelayMs:" + delayMs; if (r.mInfoAllowStartForeground != null) { r.mInfoAllowStartForeground += "; " + temp; @@ -2266,20 +2265,21 @@ public final class ActiveServices { setFgsRestrictionLocked(r.serviceInfo.packageName, r.app.getPid(), r.appInfo.uid, r.intent.getIntent(), r, r.userId, BackgroundStartPrivileges.NONE, - false /* isBindService */, false /* isStartService */); + false /* isBindService */); } // If the foreground service is not started from TOP process, do not allow it to // have while-in-use location/camera/microphone access. - if (!r.mAllowWhileInUsePermissionInFgs) { + if (!r.isFgsAllowedWIU()) { Slog.w(TAG, "Foreground service started from background can not have " + "location/camera/microphone access: service " + r.shortInstanceName); } + r.maybeLogFgsLogicChange(); if (!bypassBfslCheck) { logFgsBackgroundStart(r); - if (r.mAllowStartForeground == REASON_DENIED + if (!r.isFgsAllowedStart() && isBgFgsRestrictionEnabledForService) { final String msg = "Service.startForeground() not allowed due to " + "mAllowStartForeground false: service " @@ -2378,9 +2378,9 @@ public final class ActiveServices { // The logging of FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER event could // be deferred, make a copy of mAllowStartForeground and // mAllowWhileInUsePermissionInFgs. - r.mAllowStartForegroundAtEntering = r.mAllowStartForeground; + r.mAllowStartForegroundAtEntering = r.getFgsAllowStart(); r.mAllowWhileInUsePermissionInFgsAtEntering = - r.mAllowWhileInUsePermissionInFgs; + r.isFgsAllowedWIU(); r.mStartForegroundCount++; r.mFgsEnterTime = SystemClock.uptimeMillis(); if (!stopProcStatsOp) { @@ -2558,7 +2558,7 @@ public final class ActiveServices { policy.getForegroundServiceTypePolicyInfo(type, defaultToType); final @ForegroundServicePolicyCheckCode int code = policy.checkForegroundServiceTypePolicy( mAm.mContext, r.packageName, r.app.uid, r.app.getPid(), - r.mAllowWhileInUsePermissionInFgs, policyInfo); + r.isFgsAllowedWIU(), policyInfo); RuntimeException exception = null; switch (code) { case FGS_TYPE_POLICY_CHECK_DEPRECATED: { @@ -3744,8 +3744,7 @@ public final class ActiveServices { } } setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, userId, - BackgroundStartPrivileges.NONE, true /* isBindService */, - false /* isStartService */); + BackgroundStartPrivileges.NONE, true /* isBindService */); if (s.app != null) { ProcessServiceRecord servicePsr = s.app.mServices; @@ -7443,54 +7442,80 @@ public final class ActiveServices { * @param callingUid caller app's uid. * @param intent intent to start/bind service. * @param r the service to start. - * @param isStartService True if it's called from Context.startService(). - * False if it's called from Context.startForegroundService() or - * Service.startForeground(). + * @param isBindService True if it's called from bindService(). * @return true if allow, false otherwise. */ private void setFgsRestrictionLocked(String callingPackage, int callingPid, int callingUid, Intent intent, ServiceRecord r, int userId, - BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService, - boolean isStartService) { + BackgroundStartPrivileges backgroundStartPrivileges, boolean isBindService) { + + @ReasonCode int allowWIU; + @ReasonCode int allowStart; + + // If called from bindService(), do not update the actual fields, but instead + // keep it in a separate set of fields. + if (isBindService) { + allowWIU = r.mAllowWIUInBindService; + allowStart = r.mAllowStartInBindService; + } else { + allowWIU = r.mAllowWhileInUsePermissionInFgsReasonNoBinding; + allowStart = r.mAllowStartForegroundNoBinding; + } + // Check DeviceConfig flag. if (!mAm.mConstants.mFlagBackgroundFgsStartRestrictionEnabled) { - if (!r.mAllowWhileInUsePermissionInFgs) { + if (allowWIU == REASON_DENIED) { // BGFGS start restrictions are disabled. We're allowing while-in-use permissions. // Note REASON_OTHER since there's no other suitable reason. - r.mAllowWhileInUsePermissionInFgsReason = REASON_OTHER; + allowWIU = REASON_OTHER; } - r.mAllowWhileInUsePermissionInFgs = true; } - if (!r.mAllowWhileInUsePermissionInFgs - || (r.mAllowStartForeground == REASON_DENIED)) { + if ((allowWIU == REASON_DENIED) + || (allowStart == REASON_DENIED)) { @ReasonCode final int allowWhileInUse = shouldAllowFgsWhileInUsePermissionLocked( callingPackage, callingPid, callingUid, r.app, backgroundStartPrivileges); // We store them to compare the old and new while-in-use logics to each other. // (They're not used for any other purposes.) - if (!r.mAllowWhileInUsePermissionInFgs) { - r.mAllowWhileInUsePermissionInFgs = (allowWhileInUse != REASON_DENIED); - r.mAllowWhileInUsePermissionInFgsReason = allowWhileInUse; + if (allowWIU == REASON_DENIED) { + allowWIU = allowWhileInUse; } - if (r.mAllowStartForeground == REASON_DENIED) { - r.mAllowStartForeground = shouldAllowFgsStartForegroundWithBindingCheckLocked( + if (allowStart == REASON_DENIED) { + allowStart = shouldAllowFgsStartForegroundWithBindingCheckLocked( allowWhileInUse, callingPackage, callingPid, callingUid, intent, r, backgroundStartPrivileges, isBindService); } } + + if (isBindService) { + r.mAllowWIUInBindService = allowWIU; + r.mAllowStartInBindService = allowStart; + } else { + r.mAllowWhileInUsePermissionInFgsReasonNoBinding = allowWIU; + r.mAllowStartForegroundNoBinding = allowStart; + + // Also do a binding client check, unless called from bindService(). + if (r.mAllowWIUByBindings == REASON_DENIED) { + r.mAllowWIUByBindings = + shouldAllowFgsWhileInUsePermissionByBindingsLocked(callingUid); + } + if (r.mAllowStartByBindings == REASON_DENIED) { + r.mAllowStartByBindings = r.mAllowWIUByBindings; + } + } } /** * Reset various while-in-use and BFSL related information. */ void resetFgsRestrictionLocked(ServiceRecord r) { - r.mAllowWhileInUsePermissionInFgs = false; - r.mAllowWhileInUsePermissionInFgsReason = REASON_DENIED; - r.mAllowStartForeground = REASON_DENIED; + r.clearFgsAllowWIU(); + r.clearFgsAllowStart(); + r.mInfoAllowStartForeground = null; r.mInfoTempFgsAllowListReason = null; r.mLoggedInfoAllowStartForeground = false; - r.updateAllowUiJobScheduling(r.mAllowWhileInUsePermissionInFgs); + r.updateAllowUiJobScheduling(r.isFgsAllowedWIU()); } boolean canStartForegroundServiceLocked(int callingPid, int callingUid, String callingPackage) { @@ -8062,10 +8087,10 @@ public final class ActiveServices { */ if (!r.mLoggedInfoAllowStartForeground) { final String msg = "Background started FGS: " - + ((r.mAllowStartForeground != REASON_DENIED) ? "Allowed " : "Disallowed ") + + (r.isFgsAllowedStart() ? "Allowed " : "Disallowed ") + r.mInfoAllowStartForeground + (r.isShortFgs() ? " (Called on SHORT_SERVICE)" : ""); - if (r.mAllowStartForeground != REASON_DENIED) { + if (r.isFgsAllowedStart()) { if (ActivityManagerUtils.shouldSamplePackageForAtom(r.packageName, mAm.mConstants.mFgsStartAllowedLogSampleRate)) { Slog.wtfQuiet(TAG, msg); @@ -8105,8 +8130,8 @@ public final class ActiveServices { allowWhileInUsePermissionInFgs = r.mAllowWhileInUsePermissionInFgsAtEntering; fgsStartReasonCode = r.mAllowStartForegroundAtEntering; } else { - allowWhileInUsePermissionInFgs = r.mAllowWhileInUsePermissionInFgs; - fgsStartReasonCode = r.mAllowStartForeground; + allowWhileInUsePermissionInFgs = r.isFgsAllowedWIU(); + fgsStartReasonCode = r.getFgsAllowStart(); } final int callerTargetSdkVersion = r.mRecentCallerApplicationInfo != null ? r.mRecentCallerApplicationInfo.targetSdkVersion : 0; @@ -8295,8 +8320,7 @@ public final class ActiveServices { r.mFgsEnterTime = SystemClock.uptimeMillis(); r.foregroundServiceType = options.mForegroundServiceTypes; setFgsRestrictionLocked(callingPackage, callingPid, callingUid, intent, r, userId, - BackgroundStartPrivileges.NONE, false /* isBindService */, - false /* isStartService */); + BackgroundStartPrivileges.NONE, false /* isBindService */); final ProcessServiceRecord psr = callerApp.mServices; final boolean newService = psr.startService(r); // updateOomAdj. diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 7ba720eedfba..81858ee6e2c9 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -559,6 +559,10 @@ public class ActivityManagerService extends IActivityManager.Stub // How long we wait for a launched process to attach to the activity manager // before we decide it's never going to come up for real. static final int PROC_START_TIMEOUT = 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER; + + // How long we wait for a launched process to complete its app startup before we ANR. + static final int BIND_APPLICATION_TIMEOUT = 10 * 1000 * Build.HW_TIMEOUT_MULTIPLIER; + // How long we wait to kill an application zygote, after the last process using // it has gone away. static final int KILL_APP_ZYGOTE_DELAY_MS = 5 * 1000; @@ -1624,6 +1628,7 @@ public class ActivityManagerService extends IActivityManager.Stub static final int UPDATE_CACHED_APP_HIGH_WATERMARK = 79; static final int ADD_UID_TO_OBSERVER_MSG = 80; static final int REMOVE_UID_FROM_OBSERVER_MSG = 81; + static final int BIND_APPLICATION_TIMEOUT_MSG = 82; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -1976,6 +1981,16 @@ public class ActivityManagerService extends IActivityManager.Stub case UPDATE_CACHED_APP_HIGH_WATERMARK: { mAppProfiler.mCachedAppsWatermarkData.updateCachedAppsSnapshot((long) msg.obj); } break; + case BIND_APPLICATION_TIMEOUT_MSG: { + ProcessRecord app = (ProcessRecord) msg.obj; + + final String anrMessage; + synchronized (app) { + anrMessage = "Process " + app + " failed to complete startup"; + } + + mAnrHelper.appNotResponding(app, TimeoutRecord.forAppStart(anrMessage)); + } break; } } } @@ -4734,6 +4749,12 @@ public class ActivityManagerService extends IActivityManager.Stub app.getDisabledCompatChanges(), serializedSystemFontMap, app.getStartElapsedTime(), app.getStartUptime()); } + + Message msg = mHandler.obtainMessage(BIND_APPLICATION_TIMEOUT_MSG); + msg.obj = app; + mHandler.sendMessageDelayed(msg, BIND_APPLICATION_TIMEOUT); + mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); + if (profilerInfo != null) { profilerInfo.closeFd(); profilerInfo = null; @@ -4808,7 +4829,7 @@ public class ActivityManagerService extends IActivityManager.Stub } if (app != null && app.getStartUid() == uid && app.getStartSeq() == startSeq) { - mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app); + mHandler.removeMessages(BIND_APPLICATION_TIMEOUT_MSG, app); } else { Slog.wtf(TAG, "Mismatched or missing ProcessRecord: " + app + ". Pid: " + pid + ". Uid: " + uid); diff --git a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java index 38e7371e7075..4f5b5e1fbd68 100644 --- a/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java +++ b/services/core/java/com/android/server/am/ForegroundServiceTypeLoggerModule.java @@ -479,8 +479,8 @@ public class ForegroundServiceTypeLoggerModule { r.appInfo.uid, r.shortInstanceName, fgsState, // FGS State - r.mAllowWhileInUsePermissionInFgs, // allowWhileInUsePermissionInFgs - r.mAllowStartForeground, // fgsStartReasonCode + r.isFgsAllowedWIU(), // allowWhileInUsePermissionInFgs + r.getFgsAllowStart(), // fgsStartReasonCode r.appInfo.targetSdkVersion, r.mRecentCallingUid, 0, // callerTargetSdkVersion diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index a682c85f03b2..459c6ff3504a 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -2222,7 +2222,7 @@ public class OomAdjuster { if (s.isForeground) { final int fgsType = s.foregroundServiceType; - if (s.mAllowWhileInUsePermissionInFgs) { + if (s.isFgsAllowedWIU()) { capabilityFromFGS |= (fgsType & FOREGROUND_SERVICE_TYPE_LOCATION) != 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0; diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 50fe6d71d26e..aabab61c36f4 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -21,9 +21,11 @@ import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_BOUND_SERVICE; import static android.os.PowerExemptionManager.REASON_DENIED; +import static android.os.PowerExemptionManager.reasonCodeToString; import static android.os.Process.INVALID_UID; import static com.android.internal.util.Preconditions.checkArgument; +import static com.android.server.am.ActiveServices.TAG_SERVICE; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOREGROUND_SERVICE; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -172,11 +174,11 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN private BackgroundStartPrivileges mBackgroundStartPrivilegesByStartMerged = BackgroundStartPrivileges.NONE; - // allow while-in-use permissions in foreground service or not. + // Reason code for allow while-in-use permissions in foreground service. + // If it's not DENIED, while-in-use permissions are allowed. // while-in-use permissions in FGS started from background might be restricted. - boolean mAllowWhileInUsePermissionInFgs; @PowerExemptionManager.ReasonCode - int mAllowWhileInUsePermissionInFgsReason = REASON_DENIED; + int mAllowWhileInUsePermissionInFgsReasonNoBinding = REASON_DENIED; // A copy of mAllowWhileInUsePermissionInFgs's value when the service is entering FGS state. boolean mAllowWhileInUsePermissionInFgsAtEntering; @@ -205,15 +207,114 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN // allow the service becomes foreground service? Service started from background may not be // allowed to become a foreground service. - @PowerExemptionManager.ReasonCode int mAllowStartForeground = REASON_DENIED; + @PowerExemptionManager.ReasonCode + int mAllowStartForegroundNoBinding = REASON_DENIED; // A copy of mAllowStartForeground's value when the service is entering FGS state. - @PowerExemptionManager.ReasonCode int mAllowStartForegroundAtEntering = REASON_DENIED; + @PowerExemptionManager.ReasonCode + int mAllowStartForegroundAtEntering = REASON_DENIED; // Debug info why mAllowStartForeground is allowed or denied. String mInfoAllowStartForeground; // Debug info if mAllowStartForeground is allowed because of a temp-allowlist. ActivityManagerService.FgsTempAllowListItem mInfoTempFgsAllowListReason; // Is the same mInfoAllowStartForeground string has been logged before? Used for dedup. boolean mLoggedInfoAllowStartForeground; + + @PowerExemptionManager.ReasonCode + int mAllowWIUInBindService = REASON_DENIED; + + @PowerExemptionManager.ReasonCode + int mAllowWIUByBindings = REASON_DENIED; + + @PowerExemptionManager.ReasonCode + int mAllowStartInBindService = REASON_DENIED; + + @PowerExemptionManager.ReasonCode + int mAllowStartByBindings = REASON_DENIED; + + @PowerExemptionManager.ReasonCode + int getFgsAllowWIU() { + return mAllowWhileInUsePermissionInFgsReasonNoBinding != REASON_DENIED + ? mAllowWhileInUsePermissionInFgsReasonNoBinding + : mAllowWIUInBindService; + } + + boolean isFgsAllowedWIU() { + return getFgsAllowWIU() != REASON_DENIED; + } + + @PowerExemptionManager.ReasonCode + int getFgsAllowStart() { + return mAllowStartForegroundNoBinding != REASON_DENIED + ? mAllowStartForegroundNoBinding + : mAllowStartInBindService; + } + + boolean isFgsAllowedStart() { + return getFgsAllowStart() != REASON_DENIED; + } + + void clearFgsAllowWIU() { + mAllowWhileInUsePermissionInFgsReasonNoBinding = REASON_DENIED; + mAllowWIUInBindService = REASON_DENIED; + mAllowWIUByBindings = REASON_DENIED; + } + + void clearFgsAllowStart() { + mAllowStartForegroundNoBinding = REASON_DENIED; + mAllowStartInBindService = REASON_DENIED; + mAllowStartByBindings = REASON_DENIED; + } + + @PowerExemptionManager.ReasonCode + int reasonOr(@PowerExemptionManager.ReasonCode int first, + @PowerExemptionManager.ReasonCode int second) { + return first != REASON_DENIED ? first : second; + } + + boolean allowedChanged(@PowerExemptionManager.ReasonCode int first, + @PowerExemptionManager.ReasonCode int second) { + return (first == REASON_DENIED) != (second == REASON_DENIED); + } + + String changeMessage(@PowerExemptionManager.ReasonCode int first, + @PowerExemptionManager.ReasonCode int second) { + return reasonOr(first, second) == REASON_DENIED ? "DENIED" + : ("ALLOWED (" + + reasonCodeToString(first) + + "+" + + reasonCodeToString(second) + + ")"); + } + + void maybeLogFgsLogicChange() { + final int origWiu = reasonOr(mAllowWhileInUsePermissionInFgsReasonNoBinding, + mAllowWIUInBindService); + final int newWiu = reasonOr(mAllowWhileInUsePermissionInFgsReasonNoBinding, + mAllowWIUByBindings); + final int origStart = reasonOr(mAllowStartForegroundNoBinding, mAllowStartInBindService); + final int newStart = reasonOr(mAllowStartForegroundNoBinding, mAllowStartByBindings); + + final boolean wiuChanged = allowedChanged(origWiu, newWiu); + final boolean startChanged = allowedChanged(origStart, newStart); + + if (!wiuChanged && !startChanged) { + return; + } + final String message = "FGS logic changed:" + + (wiuChanged ? " [WIU changed]" : "") + + (startChanged ? " [BFSL changed]" : "") + + " OW:" // Orig-WIU + + changeMessage(mAllowWhileInUsePermissionInFgsReasonNoBinding, + mAllowWIUInBindService) + + " NW:" // New-WIU + + changeMessage(mAllowWhileInUsePermissionInFgsReasonNoBinding, mAllowWIUByBindings) + + " OS:" // Orig-start + + changeMessage(mAllowStartForegroundNoBinding, mAllowStartInBindService) + + " NS:" // New-start + + changeMessage(mAllowStartForegroundNoBinding, mAllowStartByBindings); + Slog.wtf(TAG_SERVICE, message); + } + // The number of times Service.startForeground() is called, after this service record is // created. (i.e. due to "bound" or "start".) It never decreases, even when stopForeground() // is called. @@ -502,7 +603,7 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN ProtoUtils.toDuration(proto, ServiceRecordProto.RESTART_TIME, restartTime, now); proto.write(ServiceRecordProto.CREATED_FROM_FG, createdFromFg); proto.write(ServiceRecordProto.ALLOW_WHILE_IN_USE_PERMISSION_IN_FGS, - mAllowWhileInUsePermissionInFgs); + isFgsAllowedWIU()); if (startRequested || delayedStop || lastStartId != 0) { long startToken = proto.start(ServiceRecordProto.START); @@ -618,7 +719,13 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN pw.println(mBackgroundStartPrivilegesByStartMerged); } pw.print(prefix); pw.print("mAllowWhileInUsePermissionInFgsReason="); - pw.println(PowerExemptionManager.reasonCodeToString(mAllowWhileInUsePermissionInFgsReason)); + pw.println(PowerExemptionManager.reasonCodeToString( + mAllowWhileInUsePermissionInFgsReasonNoBinding)); + + pw.print(prefix); pw.print("mAllowWIUInBindService="); + pw.println(PowerExemptionManager.reasonCodeToString(mAllowWIUInBindService)); + pw.print(prefix); pw.print("mAllowWIUByBindings="); + pw.println(PowerExemptionManager.reasonCodeToString(mAllowWIUByBindings)); pw.print(prefix); pw.print("allowUiJobScheduling="); pw.println(mAllowUiJobScheduling); pw.print(prefix); pw.print("recentCallingPackage="); @@ -626,7 +733,12 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN pw.print(prefix); pw.print("recentCallingUid="); pw.println(mRecentCallingUid); pw.print(prefix); pw.print("allowStartForeground="); - pw.println(PowerExemptionManager.reasonCodeToString(mAllowStartForeground)); + pw.println(PowerExemptionManager.reasonCodeToString(mAllowStartForegroundNoBinding)); + pw.print(prefix); pw.print("mAllowStartInBindService="); + pw.println(PowerExemptionManager.reasonCodeToString(mAllowStartInBindService)); + pw.print(prefix); pw.print("mAllowStartByBindings="); + pw.println(PowerExemptionManager.reasonCodeToString(mAllowStartByBindings)); + pw.print(prefix); pw.print("startForegroundCount="); pw.println(mStartForegroundCount); pw.print(prefix); pw.print("infoAllowStartForeground="); diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index 0b04159194d1..f8f0088ac047 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -613,16 +613,26 @@ public class CameraServiceProxy extends SystemService @Override public boolean isCameraDisabled(int userId) { - DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); - if (dpm == null) { - Slog.e(TAG, "Failed to get the device policy manager service"); + if (Binder.getCallingUid() != Process.CAMERASERVER_UID) { + Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + + " doesn't match expected camera service UID!"); return false; } + final long ident = Binder.clearCallingIdentity(); try { - return dpm.getCameraDisabled(null, userId); - } catch (Exception e) { - e.printStackTrace(); - return false; + DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); + if (dpm == null) { + Slog.e(TAG, "Failed to get the device policy manager service"); + return false; + } + try { + return dpm.getCameraDisabled(null, userId); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } finally { + Binder.restoreCallingIdentity(ident); } } }; diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index ffecf2b7018d..c5d0c177a46d 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -141,6 +141,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private static final int MSG_STATSD_HBM_BRIGHTNESS = 13; private static final int MSG_SWITCH_USER = 14; private static final int MSG_BOOT_COMPLETED = 15; + private static final int MSG_SET_DWBC_STRONG_MODE = 16; + private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 17; + private static final int MSG_SET_DWBC_LOGGING_ENABLED = 18; private static final int PROXIMITY_UNKNOWN = -1; private static final int PROXIMITY_NEGATIVE = 0; @@ -436,6 +439,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call private final boolean mSkipScreenOnBrightnessRamp; // Display white balance components. + // Critical methods must be called on DPC handler thread. @Nullable private final DisplayWhiteBalanceSettings mDisplayWhiteBalanceSettings; @Nullable @@ -680,9 +684,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call DisplayWhiteBalanceController displayWhiteBalanceController = null; if (mDisplayId == Display.DEFAULT_DISPLAY) { try { + displayWhiteBalanceController = injector.getDisplayWhiteBalanceController( + mHandler, mSensorManager, resources); displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler); - displayWhiteBalanceController = DisplayWhiteBalanceFactory.create(mHandler, - mSensorManager, resources); displayWhiteBalanceSettings.setCallbacks(this); displayWhiteBalanceController.setCallbacks(this); } catch (Exception e) { @@ -1025,10 +1029,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call Message msg = mHandler.obtainMessage(MSG_STOP); mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - if (mDisplayWhiteBalanceController != null) { - mDisplayWhiteBalanceController.setEnabled(false); - } - if (mAutomaticBrightnessController != null) { mAutomaticBrightnessController.stop(); } @@ -1334,9 +1334,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mAutomaticBrightnessController.switchToInteractiveScreenBrightnessMode(); } } - if (mDisplayWhiteBalanceController != null) { - mDisplayWhiteBalanceController.setStrongModeEnabled(isIdle); - } + + Message msg = mHandler.obtainMessage(); + msg.what = MSG_SET_DWBC_STRONG_MODE; + msg.arg1 = isIdle ? 1 : 0; + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); } private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() { @@ -1405,8 +1407,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mScreenOffBrightnessSensorController != null) { mScreenOffBrightnessSensorController.stop(); } + + if (mDisplayWhiteBalanceController != null) { + mDisplayWhiteBalanceController.setEnabled(false); + } } + // Call from handler thread private void updatePowerState() { Trace.traceBegin(Trace.TRACE_TAG_POWER, "DisplayPowerController#updatePowerState"); @@ -2058,6 +2065,32 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } + private void setDwbcOverride(float cct) { + if (mDisplayWhiteBalanceController != null) { + mDisplayWhiteBalanceController.setAmbientColorTemperatureOverride(cct); + // The ambient color temperature override is only applied when the ambient color + // temperature changes or is updated, so it doesn't necessarily change the screen color + // temperature immediately. So, let's make it! + // We can call this directly, since we're already on the handler thread. + updatePowerState(); + } + } + + private void setDwbcStrongMode(int arg) { + if (mDisplayWhiteBalanceController != null) { + final boolean isIdle = (arg == 1); + mDisplayWhiteBalanceController.setStrongModeEnabled(isIdle); + } + } + + private void setDwbcLoggingEnabled(int arg) { + if (mDisplayWhiteBalanceController != null) { + final boolean shouldEnable = (arg == 1); + mDisplayWhiteBalanceController.setLoggingEnabled(shouldEnable); + mDisplayWhiteBalanceSettings.setLoggingEnabled(shouldEnable); + } + } + @Override public void updateBrightness() { sendUpdatePowerState(); @@ -3331,6 +3364,19 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBootCompleted = true; updatePowerState(); break; + + case MSG_SET_DWBC_STRONG_MODE: + setDwbcStrongMode(msg.arg1); + break; + + case MSG_SET_DWBC_COLOR_OVERRIDE: + final float cct = Float.intBitsToFloat(msg.arg1); + setDwbcOverride(cct); + break; + + case MSG_SET_DWBC_LOGGING_ENABLED: + setDwbcLoggingEnabled(msg.arg1); + break; } } } @@ -3398,21 +3444,18 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Override public void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) { - if (mDisplayWhiteBalanceController != null) { - mDisplayWhiteBalanceController.setLoggingEnabled(enabled); - mDisplayWhiteBalanceSettings.setLoggingEnabled(enabled); - } + Message msg = mHandler.obtainMessage(); + msg.what = MSG_SET_DWBC_LOGGING_ENABLED; + msg.arg1 = enabled ? 1 : 0; + msg.sendToTarget(); } @Override public void setAmbientColorTemperatureOverride(float cct) { - if (mDisplayWhiteBalanceController != null) { - mDisplayWhiteBalanceController.setAmbientColorTemperatureOverride(cct); - // The ambient color temperature override is only applied when the ambient color - // temperature changes or is updated, so it doesn't necessarily change the screen color - // temperature immediately. So, let's make it! - sendUpdatePowerState(); - } + Message msg = mHandler.obtainMessage(); + msg.what = MSG_SET_DWBC_COLOR_OVERRIDE; + msg.arg1 = Float.floatToIntBits(cct); + msg.sendToTarget(); } @VisibleForTesting @@ -3543,6 +3586,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call displayUniqueId, brightnessMin, brightnessMax, hbmData, hdrBrightnessCfg, hbmChangeCallback, hbmMetadata, context); } + + DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler, + SensorManager sensorManager, Resources resources) { + return DisplayWhiteBalanceFactory.create(handler, + sensorManager, resources); + } } static class CachedBrightnessInfo { diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index 7417aeb22a64..c2c0c0a496b6 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -142,6 +142,9 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal private static final int MSG_STATSD_HBM_BRIGHTNESS = 11; private static final int MSG_SWITCH_USER = 12; private static final int MSG_BOOT_COMPLETED = 13; + private static final int MSG_SET_DWBC_STRONG_MODE = 14; + private static final int MSG_SET_DWBC_COLOR_OVERRIDE = 15; + private static final int MSG_SET_DWBC_LOGGING_ENABLED = 16; private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500; @@ -368,6 +371,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal private final boolean mSkipScreenOnBrightnessRamp; // Display white balance components. + // Critical methods must be called on DPC2 handler thread. @Nullable private final DisplayWhiteBalanceSettings mDisplayWhiteBalanceSettings; @Nullable @@ -572,9 +576,9 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal DisplayWhiteBalanceController displayWhiteBalanceController = null; if (mDisplayId == Display.DEFAULT_DISPLAY) { try { + displayWhiteBalanceController = mInjector.getDisplayWhiteBalanceController( + mHandler, mSensorManager, resources); displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler); - displayWhiteBalanceController = DisplayWhiteBalanceFactory.create(mHandler, - mSensorManager, resources); displayWhiteBalanceSettings.setCallbacks(this); displayWhiteBalanceController.setCallbacks(this); } catch (Exception e) { @@ -850,10 +854,6 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal Message msg = mHandler.obtainMessage(MSG_STOP); mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); - if (mDisplayWhiteBalanceController != null) { - mDisplayWhiteBalanceController.setEnabled(false); - } - if (mAutomaticBrightnessController != null) { mAutomaticBrightnessController.stop(); } @@ -1164,9 +1164,10 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mAutomaticBrightnessController.switchToInteractiveScreenBrightnessMode(); } } - if (mDisplayWhiteBalanceController != null) { - mDisplayWhiteBalanceController.setStrongModeEnabled(isIdle); - } + Message msg = mHandler.obtainMessage(); + msg.what = MSG_SET_DWBC_STRONG_MODE; + msg.arg1 = isIdle ? 1 : 0; + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); } private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() { @@ -1221,8 +1222,13 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal if (mScreenOffBrightnessSensorController != null) { mScreenOffBrightnessSensorController.stop(); } + + if (mDisplayWhiteBalanceController != null) { + mDisplayWhiteBalanceController.setEnabled(false); + } } + // Call from handler thread private void updatePowerState() { Trace.traceBegin(Trace.TRACE_TAG_POWER, "DisplayPowerController#updatePowerState"); @@ -1726,6 +1732,32 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal } } + private void setDwbcOverride(float cct) { + if (mDisplayWhiteBalanceController != null) { + mDisplayWhiteBalanceController.setAmbientColorTemperatureOverride(cct); + // The ambient color temperature override is only applied when the ambient color + // temperature changes or is updated, so it doesn't necessarily change the screen color + // temperature immediately. So, let's make it! + // We can call this directly, since we're already on the handler thread. + updatePowerState(); + } + } + + private void setDwbcStrongMode(int arg) { + if (mDisplayWhiteBalanceController != null) { + final boolean isIdle = (arg == 1); + mDisplayWhiteBalanceController.setStrongModeEnabled(isIdle); + } + } + + private void setDwbcLoggingEnabled(int arg) { + if (mDisplayWhiteBalanceController != null) { + final boolean enabled = (arg == 1); + mDisplayWhiteBalanceController.setLoggingEnabled(enabled); + mDisplayWhiteBalanceSettings.setLoggingEnabled(enabled); + } + } + @Override public void updateBrightness() { sendUpdatePowerState(); @@ -2755,6 +2787,19 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mBootCompleted = true; updatePowerState(); break; + + case MSG_SET_DWBC_STRONG_MODE: + setDwbcStrongMode(msg.arg1); + break; + + case MSG_SET_DWBC_COLOR_OVERRIDE: + final float cct = Float.intBitsToFloat(msg.arg1); + setDwbcOverride(cct); + break; + + case MSG_SET_DWBC_LOGGING_ENABLED: + setDwbcLoggingEnabled(msg.arg1); + break; } } } @@ -2805,21 +2850,18 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal @Override public void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) { - if (mDisplayWhiteBalanceController != null) { - mDisplayWhiteBalanceController.setLoggingEnabled(enabled); - mDisplayWhiteBalanceSettings.setLoggingEnabled(enabled); - } + Message msg = mHandler.obtainMessage(); + msg.what = MSG_SET_DWBC_LOGGING_ENABLED; + msg.arg1 = enabled ? 1 : 0; + msg.sendToTarget(); } @Override public void setAmbientColorTemperatureOverride(float cct) { - if (mDisplayWhiteBalanceController != null) { - mDisplayWhiteBalanceController.setAmbientColorTemperatureOverride(cct); - // The ambient color temperature override is only applied when the ambient color - // temperature changes or is updated, so it doesn't necessarily change the screen color - // temperature immediately. So, let's make it! - sendUpdatePowerState(); - } + Message msg = mHandler.obtainMessage(); + msg.what = MSG_SET_DWBC_COLOR_OVERRIDE; + msg.arg1 = Float.floatToIntBits(cct); + msg.sendToTarget(); } /** Functional interface for providing time. */ @@ -2946,6 +2988,12 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal boolean isColorFadeEnabled() { return !ActivityManager.isLowRamDeviceStatic(); } + + DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler, + SensorManager sensorManager, Resources resources) { + return DisplayWhiteBalanceFactory.create(handler, + sensorManager, resources); + } } static class CachedBrightnessInfo { diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java index 5b772fc917c5..4ad26c46d7ed 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java @@ -37,8 +37,11 @@ import java.util.Objects; * - Uses the AmbientColorTemperatureSensor to detect changes in the ambient color temperature; * - Uses the AmbientColorTemperatureFilter to average these changes over time, filter out the * noise, and arrive at an estimate of the actual ambient color temperature; - * - Uses the DisplayWhiteBalanceThrottler to decide whether the display color tempearture should + * - Uses the DisplayWhiteBalanceThrottler to decide whether the display color temperature should * be updated, suppressing changes that are too frequent or too minor. + * + * Calls to this class must happen on the DisplayPowerController(2) handler, to ensure + * values do not get out of sync. */ public class DisplayWhiteBalanceController implements AmbientSensor.AmbientBrightnessSensor.Callbacks, diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index a96e4adf1fee..0616f4e9d5ac 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -844,6 +844,10 @@ public class LockSettingsService extends ILockSettings.Stub { getAuthSecretHal(); mDeviceProvisionedObserver.onSystemReady(); + // Work around an issue in PropertyInvalidatedCache where the cache doesn't work until the + // first invalidation. This can be removed if PropertyInvalidatedCache is fixed. + LockPatternUtils.invalidateCredentialTypeCache(); + // TODO: maybe skip this for split system user mode. mStorage.prefetchUser(UserHandle.USER_SYSTEM); mBiometricDeferredQueue.systemReady(mInjector.getFingerprintManager(), diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java index c710d1c3885d..56f650ee9084 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java @@ -121,7 +121,8 @@ public final class DisplayPowerController2Test { private PowerManager mPowerManagerMock; @Mock private ColorDisplayService.ColorDisplayServiceInternal mCdsiMock; - + @Mock + private DisplayWhiteBalanceController mDisplayWhiteBalanceControllerMock; @Captor private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor; @@ -1089,6 +1090,18 @@ public final class DisplayPowerController2Test { .getThermalBrightnessThrottlingDataMapByThrottlingId(); } + @Test + public void testDwbcCallsHappenOnHandler() { + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + + mHolder.dpc.setAutomaticScreenBrightnessMode(true); + verify(mDisplayWhiteBalanceControllerMock, never()).setStrongModeEnabled(true); + + // dispatch handler looper + advanceTime(1); + verify(mDisplayWhiteBalanceControllerMock, times(1)).setStrongModeEnabled(true); + } + /** * Creates a mock and registers it to {@link LocalServices}. */ @@ -1378,5 +1391,11 @@ public final class DisplayPowerController2Test { Context context) { return mHighBrightnessModeController; } + + @Override + DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler, + SensorManager sensorManager, Resources resources) { + return mDisplayWhiteBalanceControllerMock; + } } } diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java index 7d26913bd390..e2aeea3bedba 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -121,7 +121,8 @@ public final class DisplayPowerControllerTest { private PowerManager mPowerManagerMock; @Mock private ColorDisplayService.ColorDisplayServiceInternal mCdsiMock; - + @Mock + private DisplayWhiteBalanceController mDisplayWhiteBalanceControllerMock; @Captor private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor; @@ -1092,6 +1093,18 @@ public final class DisplayPowerControllerTest { .getThermalBrightnessThrottlingDataMapByThrottlingId(); } + @Test + public void testDwbcCallsHappenOnHandler() { + mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID); + + mHolder.dpc.setAutomaticScreenBrightnessMode(true); + verify(mDisplayWhiteBalanceControllerMock, never()).setStrongModeEnabled(true); + + // dispatch handler looper + advanceTime(1); + verify(mDisplayWhiteBalanceControllerMock, times(1)).setStrongModeEnabled(true); + } + /** * Creates a mock and registers it to {@link LocalServices}. */ @@ -1351,5 +1364,11 @@ public final class DisplayPowerControllerTest { Context context) { return mHighBrightnessModeController; } + + @Override + DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler, + SensorManager sensorManager, Resources resources) { + return mDisplayWhiteBalanceControllerMock; + } } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java index 2d4bf144155c..32d0c98d4481 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java @@ -44,8 +44,10 @@ import android.annotation.NonNull; import android.graphics.PointF; import android.os.Handler; import android.os.Message; +import android.os.UserHandle; import android.os.VibrationEffect; import android.os.Vibrator; +import android.provider.Settings; import android.testing.TestableContext; import android.util.DebugUtils; import android.view.InputDevice; @@ -140,8 +142,6 @@ public class FullScreenMagnificationGestureHandlerTest { @Mock WindowMagnificationPromptController mWindowMagnificationPromptController; @Mock - AccessibilityManagerService mMockAccessibilityManagerService; - @Mock AccessibilityTraceManager mMockTraceManager; @Rule @@ -153,6 +153,8 @@ public class FullScreenMagnificationGestureHandlerTest { private long mLastDownTime = Integer.MIN_VALUE; + private float mOriginalMagnificationPersistedScale; + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -166,6 +168,13 @@ public class FullScreenMagnificationGestureHandlerTest { when(mockController.newValueAnimator()).thenReturn(new ValueAnimator()); when(mockController.getAnimationDuration()).thenReturn(1000L); when(mockWindowManager.setMagnificationCallbacks(eq(DISPLAY_0), any())).thenReturn(true); + mOriginalMagnificationPersistedScale = Settings.Secure.getFloatForUser( + mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 2.0f, + UserHandle.USER_SYSTEM); + Settings.Secure.putFloatForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, 2.0f, + UserHandle.USER_SYSTEM); mFullScreenMagnificationController = new FullScreenMagnificationController( mockController, new Object(), @@ -192,6 +201,10 @@ public class FullScreenMagnificationGestureHandlerTest { mMgh.onDestroy(); mFullScreenMagnificationController.unregister(DISPLAY_0); verify(mWindowMagnificationPromptController).onDestroy(); + Settings.Secure.putFloatForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, + mOriginalMagnificationPersistedScale, + UserHandle.USER_SYSTEM); } @NonNull @@ -525,10 +538,9 @@ public class FullScreenMagnificationGestureHandlerTest { final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState .CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD; final float persistedScale = (1.0f + threshold) * scale + 1.0f; - mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X, - DEFAULT_Y, /* animate= */ false, - AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); - mFullScreenMagnificationController.persistScale(DISPLAY_0); + Settings.Secure.putFloatForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, persistedScale, + UserHandle.USER_SYSTEM); mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X, DEFAULT_Y, /* animate= */ false, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); @@ -547,10 +559,9 @@ public class FullScreenMagnificationGestureHandlerTest { final float threshold = FullScreenMagnificationGestureHandler.PanningScalingState .CHECK_DETECTING_PASS_PERSISTED_SCALE_THRESHOLD; final float persistedScale = (1.0f + threshold) * scale - 0.1f; - mFullScreenMagnificationController.setScale(DISPLAY_0, persistedScale, DEFAULT_X, - DEFAULT_Y, /* animate= */ false, - AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); - mFullScreenMagnificationController.persistScale(DISPLAY_0); + Settings.Secure.putFloatForUser(mContext.getContentResolver(), + Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE, persistedScale, + UserHandle.USER_SYSTEM); mFullScreenMagnificationController.setScale(DISPLAY_0, scale, DEFAULT_X, DEFAULT_Y, /* animate= */ false, AccessibilityManagerService.MAGNIFICATION_GESTURE_HANDLER_ID); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 3c882dc871fd..a109d5cddd21 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -73,6 +73,7 @@ import static android.service.notification.Adjustment.KEY_USER_SENTIMENT; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS; import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING; +import static android.service.notification.NotificationListenerService.REASON_CANCEL; import static android.service.notification.NotificationListenerService.REASON_LOCKDOWN; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL; @@ -2009,10 +2010,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final StatusBarNotification sbn = generateNotificationRecord(null).getSbn(); mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", sbn.getId(), sbn.getNotification(), sbn.getUserId()); - Thread.sleep(1); // make sure the system clock advances before the next step + mTestableLooper.moveTimeForward(1); // THEN it is canceled mBinderService.cancelNotificationWithTag(PKG, PKG, "tag", sbn.getId(), sbn.getUserId()); - Thread.sleep(1); // here too + mTestableLooper.moveTimeForward(1); // THEN it is posted again (before the cancel has a chance to finish) mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", sbn.getId(), sbn.getNotification(), sbn.getUserId()); @@ -2303,7 +2304,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { notif.getNotification().flags |= Notification.FLAG_NO_CLEAR; mService.addNotification(notif); mService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0, 0, - notif.getUserId(), 0); + notif.getUserId(), REASON_CANCEL); waitForIdle(); StatusBarNotification[] notifs = mBinderService.getActiveNotifications(notif.getSbn().getPackageName()); @@ -3041,7 +3042,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { notif.getNotification().flags |= Notification.FLAG_NO_CLEAR; mService.addNotification(notif); mService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0, - Notification.FLAG_ONGOING_EVENT, notif.getUserId(), 0); + Notification.FLAG_ONGOING_EVENT, notif.getUserId(), REASON_CANCEL); waitForIdle(); StatusBarNotification[] notifs = mBinderService.getActiveNotifications(notif.getSbn().getPackageName()); @@ -3069,7 +3070,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { notif.getNotification().flags |= Notification.FLAG_ONGOING_EVENT; mService.addNotification(notif); mService.cancelAllNotificationsInt(mUid, 0, PKG, null, 0, 0, - notif.getUserId(), 0); + notif.getUserId(), REASON_CANCEL); waitForIdle(); StatusBarNotification[] notifs = mBinderService.getActiveNotifications(notif.getSbn().getPackageName()); @@ -12208,7 +12209,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mOnPermissionChangeListener.onOpChanged( AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0); waitForIdle(); - Thread.sleep(600); + mTestableLooper.moveTimeForward(500); waitForIdle(); ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); @@ -12227,7 +12228,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mOnPermissionChangeListener.onOpChanged( AppOpsManager.OPSTR_POST_NOTIFICATION, PKG, 0); waitForIdle(); - Thread.sleep(600); + mTestableLooper.moveTimeForward(500); waitForIdle(); ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); @@ -12261,7 +12262,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertThat(mService.mNotificationList).hasSize(0); - Thread.sleep(600); + mTestableLooper.moveTimeForward(500); waitForIdle(); verify(mContext).sendBroadcastAsUser(any(), eq(UserHandle.of(0)), eq(null)); } diff --git a/tests/Internal/src/android/service/wallpaper/OWNERS b/tests/Internal/src/android/service/wallpaper/OWNERS new file mode 100644 index 000000000000..5a26d0e1f62b --- /dev/null +++ b/tests/Internal/src/android/service/wallpaper/OWNERS @@ -0,0 +1,4 @@ +dupin@google.com +santie@google.com +pomini@google.com +poultney@google.com
\ No newline at end of file diff --git a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java index 153ca79e346b..0c5e8d481131 100644 --- a/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java +++ b/tests/Internal/src/android/service/wallpaper/WallpaperServiceTest.java @@ -85,4 +85,17 @@ public class WallpaperServiceTest { assertEquals("onAmbientModeChanged should have been called", 2, zoomChangedCount[0]); } + @Test + public void testNotifyColorsOfDestroyedEngine_doesntCrash() { + WallpaperService service = new WallpaperService() { + @Override + public Engine onCreateEngine() { + return new Engine(); + } + }; + WallpaperService.Engine engine = service.onCreateEngine(); + engine.detach(); + + engine.notifyColorsChanged(); + } } diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java index edd6dd3468ef..82e40b1eee6b 100644 --- a/tests/testables/src/android/testing/TestableLooper.java +++ b/tests/testables/src/android/testing/TestableLooper.java @@ -32,6 +32,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; /** * This is a wrapper around {@link TestLooperManager} to make it easier to manage @@ -55,7 +56,6 @@ public class TestableLooper { private MessageHandler mMessageHandler; private Handler mHandler; - private Runnable mEmptyMessage; private TestLooperManager mQueueWrapper; static { @@ -121,8 +121,12 @@ public class TestableLooper { * @param num Number of messages to parse */ public int processMessages(int num) { + return processMessagesInternal(num, null); + } + + private int processMessagesInternal(int num, Runnable barrierRunnable) { for (int i = 0; i < num; i++) { - if (!parseMessageInt()) { + if (!processSingleMessage(barrierRunnable)) { return i + 1; } } @@ -130,6 +134,27 @@ public class TestableLooper { } /** + * Process up to a certain number of messages, not blocking if the queue has less messages than + * that + * @param num the maximum number of messages to process + * @return the number of messages processed. This will be at most {@code num}. + */ + + public int processMessagesNonBlocking(int num) { + final AtomicBoolean reachedBarrier = new AtomicBoolean(false); + Runnable barrierRunnable = () -> { + reachedBarrier.set(true); + }; + mHandler.post(barrierRunnable); + waitForMessage(mQueueWrapper, mHandler, barrierRunnable); + try { + return processMessagesInternal(num, barrierRunnable) + (reachedBarrier.get() ? -1 : 0); + } finally { + mHandler.removeCallbacks(barrierRunnable); + } + } + + /** * Process messages in the queue until no more are found. */ public void processAllMessages() { @@ -165,19 +190,20 @@ public class TestableLooper { private int processQueuedMessages() { int count = 0; - mEmptyMessage = () -> { }; - mHandler.post(mEmptyMessage); - waitForMessage(mQueueWrapper, mHandler, mEmptyMessage); - while (parseMessageInt()) count++; + Runnable barrierRunnable = () -> { }; + mHandler.post(barrierRunnable); + waitForMessage(mQueueWrapper, mHandler, barrierRunnable); + while (processSingleMessage(barrierRunnable)) count++; return count; } - private boolean parseMessageInt() { + private boolean processSingleMessage(Runnable barrierRunnable) { try { Message result = mQueueWrapper.next(); if (result != null) { // This is a break message. - if (result.getCallback() == mEmptyMessage) { + if (result.getCallback() == barrierRunnable) { + mQueueWrapper.execute(result); mQueueWrapper.recycle(result); return false; } diff --git a/tests/testables/tests/src/android/testing/TestableLooperTest.java b/tests/testables/tests/src/android/testing/TestableLooperTest.java index 0f491b86626c..a02eb6b176dc 100644 --- a/tests/testables/tests/src/android/testing/TestableLooperTest.java +++ b/tests/testables/tests/src/android/testing/TestableLooperTest.java @@ -27,12 +27,6 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InOrder; - import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -40,6 +34,11 @@ import android.test.suitebuilder.annotation.SmallTest; import android.testing.TestableLooper.MessageHandler; import android.testing.TestableLooper.RunWithLooper; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InOrder; + @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper @@ -240,4 +239,33 @@ public class TestableLooperTest { inOrder.verify(handler).dispatchMessage(messageC); } + @Test + public void testProcessMessagesNonBlocking_onlyArgNumber() { + Handler h = new Handler(mTestableLooper.getLooper()); + Runnable r = mock(Runnable.class); + + h.post(r); + h.post(r); + h.post(r); + + int processed = mTestableLooper.processMessagesNonBlocking(2); + + verify(r, times(2)).run(); + assertEquals(2, processed); + } + + @Test + public void testProcessMessagesNonBlocking_lessMessagesThanArg() { + Handler h = new Handler(mTestableLooper.getLooper()); + Runnable r = mock(Runnable.class); + + h.post(r); + h.post(r); + h.post(r); + + int processed = mTestableLooper.processMessagesNonBlocking(5); + + verify(r, times(3)).run(); + assertEquals(3, processed); + } } |