diff options
51 files changed, 1265 insertions, 453 deletions
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 3312294865d6..9e59ee496de1 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -2547,7 +2547,7 @@ public class AppOpsManager { .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(), new AppOpInfo.Builder(OP_TURN_SCREEN_ON, OPSTR_TURN_SCREEN_ON, "TURN_SCREEN_ON") .setPermission(Manifest.permission.TURN_SCREEN_ON) - .setDefaultMode(AppOpsManager.MODE_ERRORED).build(), + .setDefaultMode(AppOpsManager.MODE_DEFAULT).build(), new AppOpInfo.Builder(OP_GET_ACCOUNTS, OPSTR_GET_ACCOUNTS, "GET_ACCOUNTS") .setPermission(Manifest.permission.GET_ACCOUNTS) .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(), diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 746dcb6f2e13..d8cedb8fa5d2 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -44,6 +44,7 @@ import org.json.JSONObject; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; @@ -246,6 +247,7 @@ public final class NotificationChannel implements Parcelable { private boolean mBypassDnd; private int mLockscreenVisibility = DEFAULT_VISIBILITY; private Uri mSound = Settings.System.DEFAULT_NOTIFICATION_URI; + private boolean mSoundRestored = false; private boolean mLights; private int mLightColor = DEFAULT_LIGHT_COLOR; private long[] mVibration; @@ -929,8 +931,9 @@ public final class NotificationChannel implements Parcelable { /** * @hide */ - public void populateFromXmlForRestore(XmlPullParser parser, Context context) { - populateFromXml(XmlUtils.makeTyped(parser), true, context); + public void populateFromXmlForRestore(XmlPullParser parser, boolean pkgInstalled, + Context context) { + populateFromXml(XmlUtils.makeTyped(parser), true, pkgInstalled, context); } /** @@ -938,14 +941,14 @@ public final class NotificationChannel implements Parcelable { */ @SystemApi public void populateFromXml(XmlPullParser parser) { - populateFromXml(XmlUtils.makeTyped(parser), false, null); + populateFromXml(XmlUtils.makeTyped(parser), false, true, null); } /** * If {@param forRestore} is true, {@param Context} MUST be non-null. */ private void populateFromXml(TypedXmlPullParser parser, boolean forRestore, - @Nullable Context context) { + boolean pkgInstalled, @Nullable Context context) { Preconditions.checkArgument(!forRestore || context != null, "forRestore is true but got null context"); @@ -956,7 +959,8 @@ public final class NotificationChannel implements Parcelable { setLockscreenVisibility(safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY)); Uri sound = safeUri(parser, ATT_SOUND); - setSound(forRestore ? restoreSoundUri(context, sound) : sound, safeAudioAttributes(parser)); + setSound(forRestore ? restoreSoundUri(context, sound, pkgInstalled) : sound, + safeAudioAttributes(parser)); enableLights(safeBool(parser, ATT_LIGHTS, false)); setLightColor(safeInt(parser, ATT_LIGHT_COLOR, DEFAULT_LIGHT_COLOR)); @@ -978,8 +982,58 @@ public final class NotificationChannel implements Parcelable { setImportantConversation(safeBool(parser, ATT_IMP_CONVERSATION, false)); } + /** + * Returns whether the sound for this channel was successfully restored + * from backup. + * @return false if the sound was not restored successfully. true otherwise (default value) + * @hide + */ + public boolean isSoundRestored() { + return mSoundRestored; + } + @Nullable - private Uri restoreSoundUri(Context context, @Nullable Uri uri) { + private Uri getCanonicalizedSoundUri(ContentResolver contentResolver, @NonNull Uri uri) { + if (Settings.System.DEFAULT_NOTIFICATION_URI.equals(uri)) { + return uri; + } + + if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme())) { + try { + contentResolver.getResourceId(uri); + return uri; + } catch (FileNotFoundException e) { + return null; + } + } + + if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { + return uri; + } + + return contentResolver.canonicalize(uri); + } + + @Nullable + private Uri getUncanonicalizedSoundUri(ContentResolver contentResolver, @NonNull Uri uri) { + if (Settings.System.DEFAULT_NOTIFICATION_URI.equals(uri) + || ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme()) + || ContentResolver.SCHEME_FILE.equals(uri.getScheme())) { + return uri; + } + return contentResolver.uncanonicalize(uri); + } + + /** + * Restore/validate sound Uri from backup + * @param context The Context + * @param uri The sound Uri to restore + * @param pkgInstalled If the parent package is installed + * @return restored and validated Uri + * @hide + */ + @Nullable + public Uri restoreSoundUri(Context context, @Nullable Uri uri, boolean pkgInstalled) { if (uri == null || Uri.EMPTY.equals(uri)) { return null; } @@ -991,12 +1045,22 @@ public final class NotificationChannel implements Parcelable { // the uri and in the case of not having the resource we end up with the default - better // than broken. As a side effect we'll canonicalize already canonicalized uris, this is fine // according to the docs because canonicalize method has to handle canonical uris as well. - Uri canonicalizedUri = contentResolver.canonicalize(uri); + Uri canonicalizedUri = getCanonicalizedSoundUri(contentResolver, uri); if (canonicalizedUri == null) { - // We got a null because the uri in the backup does not exist here, so we return default - return Settings.System.DEFAULT_NOTIFICATION_URI; + // Uri failed to restore with package installed + if (!mSoundRestored && pkgInstalled) { + mSoundRestored = true; + // We got a null because the uri in the backup does not exist here, so we return + // default + return Settings.System.DEFAULT_NOTIFICATION_URI; + } else { + // Flag as unrestored and try again later (on package install) + mSoundRestored = false; + return uri; + } } - return contentResolver.uncanonicalize(canonicalizedUri); + mSoundRestored = true; + return getUncanonicalizedSoundUri(contentResolver, canonicalizedUri); } /** @@ -1019,7 +1083,7 @@ public final class NotificationChannel implements Parcelable { if (sound == null || Uri.EMPTY.equals(sound)) { return null; } - Uri canonicalSound = context.getContentResolver().canonicalize(sound); + Uri canonicalSound = getCanonicalizedSoundUri(context.getContentResolver(), sound); if (canonicalSound == null) { // The content provider does not support canonical uris so we backup the default return Settings.System.DEFAULT_NOTIFICATION_URI; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index a8a2ad1bb8df..7b6835792370 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -8394,8 +8394,7 @@ public class DevicePolicyManager { * <p> * The calling device admin must have requested * {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA} to be able to call this method; if it has - * not, a security exception will be thrown, or the caller must hold the permission - * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_CAMERA}. + * not, a security exception will be thrown. * <p> * <b>Note</b>, this policy type is deprecated for legacy device admins since * {@link android.os.Build.VERSION_CODES#Q}. On Android @@ -8411,8 +8410,7 @@ public class DevicePolicyManager { the caller is not a device admin * @param disabled Whether or not the camera should be disabled. * @throws SecurityException if {@code admin} is not an active administrator or does not use - * {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA} and the caller does not hold - * the permisisons {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_CAMERA}. + * {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA}. */ @RequiresPermission(value = MANAGE_DEVICE_POLICY_CAMERA, conditional = true) public void setCameraDisabled(@Nullable ComponentName admin, boolean disabled) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 2efb26593780..3487b013fa0b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4677,22 +4677,16 @@ public final class Settings { "display_color_mode_vendor_hint"; /** - * The user selected min refresh rate in frames per second. - * - * If this isn't set, 0 will be used. + * Whether or not the peak refresh rate should be forced. 0=no, 1=yes * @hide */ - @Readable - public static final String MIN_REFRESH_RATE = "min_refresh_rate"; + public static final String FORCE_PEAK_REFRESH_RATE = "force_peak_refresh_rate"; /** - * The user selected peak refresh rate in frames per second. - * - * If this isn't set, the system falls back to a device specific default. + * Whether or not the peak refresh rate should be used for some content. 0=no, 1=yes * @hide */ - @Readable - public static final String PEAK_REFRESH_RATE = "peak_refresh_rate"; + public static final String SMOOTH_DISPLAY = "smooth_display"; /** * The amount of time in milliseconds before the device goes to sleep or begins diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index e31adcfd699e..f2373fbfa858 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -341,6 +341,9 @@ public final class DisplayInfo implements Parcelable { @Nullable public DisplayShape displayShape; + /** + * Refresh rate range limitation based on the current device layout + */ @Nullable public SurfaceControl.RefreshRateRange layoutLimitedRefreshRate; @@ -354,7 +357,7 @@ public final class DisplayInfo implements Parcelable { * RefreshRateRange limitation for @Temperature.ThrottlingStatus */ @NonNull - public SparseArray<SurfaceControl.RefreshRateRange> refreshRateThermalThrottling = + public SparseArray<SurfaceControl.RefreshRateRange> thermalRefreshRateThrottling = new SparseArray<>(); public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() { @@ -434,7 +437,7 @@ public final class DisplayInfo implements Parcelable { && Objects.equals(displayShape, other.displayShape) && Objects.equals(layoutLimitedRefreshRate, other.layoutLimitedRefreshRate) && BrightnessSynchronizer.floatEquals(hdrSdrRatio, other.hdrSdrRatio) - && refreshRateThermalThrottling.contentEquals(other.refreshRateThermalThrottling); + && thermalRefreshRateThrottling.contentEquals(other.thermalRefreshRateThrottling); } @Override @@ -491,7 +494,7 @@ public final class DisplayInfo implements Parcelable { displayShape = other.displayShape; layoutLimitedRefreshRate = other.layoutLimitedRefreshRate; hdrSdrRatio = other.hdrSdrRatio; - refreshRateThermalThrottling = other.refreshRateThermalThrottling; + thermalRefreshRateThrottling = other.thermalRefreshRateThrottling; } public void readFromParcel(Parcel source) { @@ -554,7 +557,7 @@ public final class DisplayInfo implements Parcelable { displayShape = source.readTypedObject(DisplayShape.CREATOR); layoutLimitedRefreshRate = source.readTypedObject(SurfaceControl.RefreshRateRange.CREATOR); hdrSdrRatio = source.readFloat(); - refreshRateThermalThrottling = source.readSparseArray(null, + thermalRefreshRateThrottling = source.readSparseArray(null, SurfaceControl.RefreshRateRange.class); } @@ -616,7 +619,7 @@ public final class DisplayInfo implements Parcelable { dest.writeTypedObject(displayShape, flags); dest.writeTypedObject(layoutLimitedRefreshRate, flags); dest.writeFloat(hdrSdrRatio); - dest.writeSparseArray(refreshRateThermalThrottling); + dest.writeSparseArray(thermalRefreshRateThrottling); } @Override @@ -884,8 +887,8 @@ public final class DisplayInfo implements Parcelable { } else { sb.append(hdrSdrRatio); } - sb.append(", refreshRateThermalThrottling "); - sb.append(refreshRateThermalThrottling); + sb.append(", thermalRefreshRateThrottling "); + sb.append(thermalRefreshRateThrottling); sb.append("}"); return sb.toString(); } diff --git a/core/java/com/android/internal/display/RefreshRateSettingsUtils.java b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java new file mode 100644 index 000000000000..39d8380c7e95 --- /dev/null +++ b/core/java/com/android/internal/display/RefreshRateSettingsUtils.java @@ -0,0 +1,92 @@ +/* + * 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.internal.display; + +import android.content.ContentResolver; +import android.content.Context; +import android.hardware.display.DisplayManager; +import android.provider.Settings; +import android.util.Log; +import android.view.Display; + +/** + * Constants and utility methods for refresh rate settings. + */ +public class RefreshRateSettingsUtils { + + private static final String TAG = "RefreshRateSettingsUtils"; + + public static final float DEFAULT_REFRESH_RATE = 60f; + + /** + * Find the highest refresh rate among all the modes of the default display. + * @param context The context + * @return The highest refresh rate + */ + public static float findHighestRefreshRateForDefaultDisplay(Context context) { + final DisplayManager dm = context.getSystemService(DisplayManager.class); + final Display display = dm.getDisplay(Display.DEFAULT_DISPLAY); + + if (display == null) { + Log.w(TAG, "No valid default display device"); + return DEFAULT_REFRESH_RATE; + } + + float maxRefreshRate = DEFAULT_REFRESH_RATE; + for (Display.Mode mode : display.getSupportedModes()) { + if (Math.round(mode.getRefreshRate()) > maxRefreshRate) { + maxRefreshRate = mode.getRefreshRate(); + } + } + return maxRefreshRate; + } + + /** + * Get the min refresh rate which is determined by + * {@link Settings.System.FORCE_PEAK_REFRESH_RATE}. + * @param context The context + * @return The min refresh rate + */ + public static float getMinRefreshRate(Context context) { + final ContentResolver cr = context.getContentResolver(); + int forcePeakRefreshRateSetting = Settings.System.getIntForUser(cr, + Settings.System.FORCE_PEAK_REFRESH_RATE, -1, cr.getUserId()); + return forcePeakRefreshRateSetting == 1 + ? findHighestRefreshRateForDefaultDisplay(context) + : 0; + } + + /** + * Get the peak refresh rate which is determined by {@link Settings.System.SMOOTH_DISPLAY}. + * @param context The context + * @param defaultPeakRefreshRate The refresh rate to return if the setting doesn't have a value + * @return The peak refresh rate + */ + public static float getPeakRefreshRate(Context context, float defaultPeakRefreshRate) { + final ContentResolver cr = context.getContentResolver(); + int smoothDisplaySetting = Settings.System.getIntForUser(cr, + Settings.System.SMOOTH_DISPLAY, -1, cr.getUserId()); + switch (smoothDisplaySetting) { + case 0: + return DEFAULT_REFRESH_RATE; + case 1: + return findHighestRefreshRateForDefaultDisplay(context); + default: + return defaultPeakRefreshRate; + } + } +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 11fcd1e248db..997a0c9bf46d 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2542,7 +2542,7 @@ <permission android:name="android.permission.TURN_SCREEN_ON" android:label="@string/permlab_turnScreenOn" android:description="@string/permdesc_turnScreenOn" - android:protectionLevel="normal|appop" /> + android:protectionLevel="signature|privileged|appop" /> <!-- ==================================================== --> <!-- Permissions related to changing audio settings --> diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index 56c3068fe5e9..302c72ead52e 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -42,7 +42,6 @@ import android.media.MediaCodecInfo; import android.media.MediaCodecList; import android.net.Uri; import android.os.Build; -import android.os.SystemProperties; import android.os.Trace; import android.system.ErrnoException; import android.system.Os; @@ -2069,47 +2068,67 @@ public final class ImageDecoder implements AutoCloseable { } private static boolean sIsP010SupportedForAV1 = false; - private static boolean sIsP010SupportedForAV1Initialized = false; - private static final Object sIsP010SupportedForAV1Lock = new Object(); + private static boolean sIsP010SupportedForHEVC = false; + private static boolean sIsP010SupportedFlagsInitialized = false; + private static final Object sIsP010SupportedLock = new Object(); /** * Checks if the device supports decoding 10-bit AV1. */ @SuppressWarnings("AndroidFrameworkCompatChange") // This is not an app-visible API. private static boolean isP010SupportedForAV1() { - synchronized (sIsP010SupportedForAV1Lock) { - if (sIsP010SupportedForAV1Initialized) { + synchronized (sIsP010SupportedLock) { + if (sIsP010SupportedFlagsInitialized) { return sIsP010SupportedForAV1; } + checkP010SupportforAV1HEVC(); + return sIsP010SupportedForAV1; + } + } - sIsP010SupportedForAV1Initialized = true; - return sIsP010SupportedForAV1 = isP010SupportedforMime("video/av01"); + /** + * Checks if the device supports decoding 10-bit HEVC. + * This method is called by JNI. + */ + @SuppressWarnings("unused") + private static boolean isP010SupportedForHEVC() { + synchronized (sIsP010SupportedLock) { + if (sIsP010SupportedFlagsInitialized) { + return sIsP010SupportedForHEVC; + } + checkP010SupportforAV1HEVC(); + return sIsP010SupportedForHEVC; } } /** * Checks if the device supports decoding 10-bit for the given mime type. */ - private static boolean isP010SupportedforMime(String mime) { + private static void checkP010SupportforAV1HEVC() { MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS); for (MediaCodecInfo mediaCodecInfo : codecList.getCodecInfos()) { if (mediaCodecInfo.isEncoder()) { continue; } for (String mediaType : mediaCodecInfo.getSupportedTypes()) { - if (mediaType.equalsIgnoreCase(mime)) { + if (mediaType.equalsIgnoreCase("video/av01") + || mediaType.equalsIgnoreCase("video/hevc")) { MediaCodecInfo.CodecCapabilities codecCapabilities = mediaCodecInfo.getCapabilitiesForType(mediaType); for (int i = 0; i < codecCapabilities.colorFormats.length; ++i) { if (codecCapabilities.colorFormats[i] == MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010) { - return true; + if (mediaType.equalsIgnoreCase("video/av01")) { + sIsP010SupportedForAV1 = true; + } else { + sIsP010SupportedForHEVC = true; + } } } } } } - return false; + sIsP010SupportedFlagsInitialized = true; } /** diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp index c57e6f09347a..38d17de166e9 100644 --- a/libs/hwui/jni/BitmapFactory.cpp +++ b/libs/hwui/jni/BitmapFactory.cpp @@ -401,6 +401,14 @@ static jobject doDecode(JNIEnv* env, std::unique_ptr<SkStreamRewindable> stream, decodeColorType = kN32_SkColorType; } + // b/276879147, fallback to RGBA_8888 when decoding HEIF and P010 is not supported. + if (decodeColorType == kRGBA_1010102_SkColorType && + codec->getEncodedFormat() == SkEncodedImageFormat::kHEIF && + env->CallStaticBooleanMethod(gImageDecoder_class, + gImageDecoder_isP010SupportedForHEVCMethodID) == JNI_FALSE) { + decodeColorType = kN32_SkColorType; + } + sk_sp<SkColorSpace> decodeColorSpace = codec->computeOutputColorSpace( decodeColorType, prefColorSpace); diff --git a/libs/hwui/jni/BitmapFactory.h b/libs/hwui/jni/BitmapFactory.h index 45bffc44967d..a079cb4b513d 100644 --- a/libs/hwui/jni/BitmapFactory.h +++ b/libs/hwui/jni/BitmapFactory.h @@ -1,8 +1,9 @@ #ifndef _ANDROID_GRAPHICS_BITMAP_FACTORY_H_ #define _ANDROID_GRAPHICS_BITMAP_FACTORY_H_ +#include <SkEncodedImageFormat.h> + #include "GraphicsJNI.h" -#include "SkEncodedImageFormat.h" extern jclass gOptions_class; extern jfieldID gOptions_justBoundsFieldID; @@ -26,6 +27,9 @@ extern jfieldID gOptions_bitmapFieldID; extern jclass gBitmapConfig_class; extern jmethodID gBitmapConfig_nativeToConfigMethodID; +extern jclass gImageDecoder_class; +extern jmethodID gImageDecoder_isP010SupportedForHEVCMethodID; + jstring getMimeTypeAsJavaString(JNIEnv*, SkEncodedImageFormat); #endif // _ANDROID_GRAPHICS_BITMAP_FACTORY_H_ diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp index db1c188e425e..6744c6c0b9ec 100644 --- a/libs/hwui/jni/ImageDecoder.cpp +++ b/libs/hwui/jni/ImageDecoder.cpp @@ -25,6 +25,7 @@ #include <SkCodecAnimation.h> #include <SkColorSpace.h> #include <SkColorType.h> +#include <SkEncodedImageFormat.h> #include <SkImageInfo.h> #include <SkRect.h> #include <SkSize.h> @@ -48,7 +49,8 @@ using namespace android; -static jclass gImageDecoder_class; +jclass gImageDecoder_class; +jmethodID gImageDecoder_isP010SupportedForHEVCMethodID; static jclass gSize_class; static jclass gDecodeException_class; static jclass gCanvas_class; @@ -298,6 +300,14 @@ static jobject ImageDecoder_nDecodeBitmap(JNIEnv* env, jobject /*clazz*/, jlong colorType = kN32_SkColorType; } + // b/276879147, fallback to RGBA_8888 when decoding HEIF and P010 is not supported. + if (colorType == kRGBA_1010102_SkColorType && + decoder->mCodec->getEncodedFormat() == SkEncodedImageFormat::kHEIF && + env->CallStaticBooleanMethod(gImageDecoder_class, + gImageDecoder_isP010SupportedForHEVCMethodID) == JNI_FALSE) { + colorType = kN32_SkColorType; + } + if (!decoder->setOutColorType(colorType)) { doThrowISE(env, "Failed to set out color type!"); return nullptr; @@ -540,6 +550,8 @@ int register_android_graphics_ImageDecoder(JNIEnv* env) { gImageDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder")); gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JIIZZ)V"); gImageDecoder_postProcessMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "postProcessAndRelease", "(Landroid/graphics/Canvas;)I"); + gImageDecoder_isP010SupportedForHEVCMethodID = + GetStaticMethodIDOrDie(env, gImageDecoder_class, "isP010SupportedForHEVC", "()Z"); gSize_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/util/Size")); gSize_constructorMethodID = GetMethodIDOrDie(env, gSize_class, "<init>", "(II)V"); diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java index 6a5535d345db..e4cc9f15aea1 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java @@ -100,5 +100,6 @@ public class SystemSettings { Settings.System.CAMERA_FLASH_NOTIFICATION, Settings.System.SCREEN_FLASH_NOTIFICATION, Settings.System.SCREEN_FLASH_NOTIFICATION_COLOR, + Settings.System.SMOOTH_DISPLAY }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index 85623b26c589..4b720636c1d4 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -226,5 +226,6 @@ public class SystemSettingsValidators { VALIDATORS.put(System.CAMERA_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR); VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION, BOOLEAN_VALIDATOR); VALIDATORS.put(System.SCREEN_FLASH_NOTIFICATION_COLOR, ANY_INTEGER_VALIDATOR); + VALIDATORS.put(System.SMOOTH_DISPLAY, BOOLEAN_VALIDATOR); } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 284b06b86cb6..d1bd5e661072 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -34,6 +34,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OV import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; import static com.android.internal.accessibility.util.AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM; +import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE; import static com.android.providers.settings.SettingsState.FALLBACK_FILE_SUFFIX; import static com.android.providers.settings.SettingsState.getTypeFromKey; import static com.android.providers.settings.SettingsState.getUserIdFromKey; @@ -3748,7 +3749,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 218; + private static final int SETTINGS_VERSION = 219; private final int mUserId; @@ -5673,7 +5674,7 @@ public class SettingsProvider extends ContentProvider { providers.addAll(Arrays.asList(resources.getStringArray(resourceId))); } catch (Resources.NotFoundException e) { Slog.w(LOG_TAG, - "Get default array Cred Provider not found: " + e.toString()); + "Get default array Cred Provider not found: " + e.toString()); } try { final String storedValue = resources.getString(resourceId); @@ -5682,7 +5683,7 @@ public class SettingsProvider extends ContentProvider { } } catch (Resources.NotFoundException e) { Slog.w(LOG_TAG, - "Get default Cred Provider not found: " + e.toString()); + "Get default Cred Provider not found: " + e.toString()); } if (!providers.isEmpty()) { @@ -5731,8 +5732,8 @@ public class SettingsProvider extends ContentProvider { final Setting currentSetting = secureSettings .getSettingLocked(Settings.Secure.CREDENTIAL_SERVICE); if (currentSetting.isNull()) { - final int resourceId = - com.android.internal.R.array.config_defaultCredentialProviderService; + final int resourceId = com.android.internal.R.array + .config_defaultCredentialProviderService; final Resources resources = getContext().getResources(); // If the config has not be defined we might get an exception. final List<String> providers = new ArrayList<>(); @@ -5740,7 +5741,7 @@ public class SettingsProvider extends ContentProvider { providers.addAll(Arrays.asList(resources.getStringArray(resourceId))); } catch (Resources.NotFoundException e) { Slog.w(LOG_TAG, - "Get default array Cred Provider not found: " + e.toString()); + "Get default array Cred Provider not found: " + e.toString()); } if (!providers.isEmpty()) { @@ -5839,6 +5840,47 @@ public class SettingsProvider extends ContentProvider { currentVersion = 218; } + // v218: Convert Smooth Display and Force Peak Refresh Rate to a boolean + if (currentVersion == 218) { + final String peakRefreshRateSettingName = "peak_refresh_rate"; + final String minRefreshRateSettingName = "min_refresh_rate"; + + final SettingsState systemSettings = getSystemSettingsLocked(userId); + final Setting peakRefreshRateSetting = + systemSettings.getSettingLocked(peakRefreshRateSettingName); + final Setting minRefreshRateSetting = + systemSettings.getSettingLocked(minRefreshRateSettingName); + + float peakRefreshRate = DEFAULT_REFRESH_RATE; + float minRefreshRate = 0; + try { + if (!peakRefreshRateSetting.isNull()) { + peakRefreshRate = Float.parseFloat(peakRefreshRateSetting.getValue()); + } + } catch (NumberFormatException e) { + // Do nothing. Overwrite with default value. + } + try { + if (!minRefreshRateSetting.isNull()) { + minRefreshRate = Float.parseFloat(minRefreshRateSetting.getValue()); + } + } catch (NumberFormatException e) { + // Do nothing. Overwrite with default value. + } + + systemSettings.deleteSettingLocked(peakRefreshRateSettingName); + systemSettings.deleteSettingLocked(minRefreshRateSettingName); + + systemSettings.insertSettingLocked(Settings.System.SMOOTH_DISPLAY, + peakRefreshRate > DEFAULT_REFRESH_RATE ? "1" : "0", /* tag= */ null, + /* makeDefault= */ false, SettingsState.SYSTEM_PACKAGE_NAME); + systemSettings.insertSettingLocked(Settings.System.FORCE_PEAK_REFRESH_RATE, + minRefreshRate > 0 ? "1" : "0", /* tag= */ null, + /* makeDefault= */ false, SettingsState.SYSTEM_PACKAGE_NAME); + + currentVersion = 219; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 706666cbebab..36aa2ac74406 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -97,8 +97,7 @@ public class SettingsBackupTest { Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug? Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only Settings.System.DESKTOP_MODE, // developer setting for internal prototyping - Settings.System.MIN_REFRESH_RATE, // depends on hardware capabilities - Settings.System.PEAK_REFRESH_RATE, // depends on hardware capabilities + Settings.System.FORCE_PEAK_REFRESH_RATE, // depends on hardware capabilities Settings.System.SCREEN_BRIGHTNESS_FLOAT, Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, Settings.System.MULTI_AUDIO_FOCUS_ENABLED // form-factor/OEM specific diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 24de487945db..4652ef195a0c 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -982,6 +982,8 @@ </intent-filter> </activity> + <service android:name=".notetask.NoteTaskControllerUpdateService" /> + <activity android:name=".notetask.shortcut.LaunchNoteTaskActivity" android:exported="true" diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt index 86bd5f2bff5a..9d1dd1b67801 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt @@ -648,7 +648,7 @@ class AnimatableClockView @JvmOverloads constructor( private const val DIGITS_PER_LINE = 2 // How much of "fraction" to spend on canceling the animation, if needed - private const val ANIMATION_CANCELLATION_TIME = 0.4f + private const val ANIMATION_CANCELLATION_TIME = 0f // Delays. Each digit's animation should have a slight delay, so we get a nice // "stepping" effect. When moving right, the second digit of the hour should move first. diff --git a/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java b/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java index 12dd8f06de17..4c16d41c8672 100644 --- a/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java +++ b/packages/SystemUI/src/com/android/systemui/AutoReinflateContainer.java @@ -16,6 +16,7 @@ package com.android.systemui; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.content.res.TypedArray; import android.util.AttributeSet; @@ -23,21 +24,29 @@ import android.view.LayoutInflater; import android.view.View; import android.widget.FrameLayout; -import com.android.systemui.statusbar.policy.ConfigurationController; - import java.util.ArrayList; import java.util.List; +import java.util.Set; /** * Custom {@link FrameLayout} that re-inflates when changes to {@link Configuration} happen. * Currently supports changes to density, asset path, and locale. */ -public class AutoReinflateContainer extends FrameLayout implements - ConfigurationController.ConfigurationListener { +public class AutoReinflateContainer extends FrameLayout { + + private static final Set<Integer> SUPPORTED_CHANGES = Set.of( + ActivityInfo.CONFIG_LOCALE, + ActivityInfo.CONFIG_UI_MODE, + ActivityInfo.CONFIG_ASSETS_PATHS, + ActivityInfo.CONFIG_DENSITY, + ActivityInfo.CONFIG_FONT_SCALE + ); private final List<InflateListener> mInflateListeners = new ArrayList<>(); private final int mLayout; + private final Configuration mLastConfig = new Configuration(); + public AutoReinflateContainer(Context context, @Nullable AttributeSet attrs) { super(context, attrs); @@ -51,15 +60,14 @@ public class AutoReinflateContainer extends FrameLayout implements } @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - Dependency.get(ConfigurationController.class).addCallback(this); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - Dependency.get(ConfigurationController.class).removeCallback(this); + protected void onConfigurationChanged(Configuration newConfig) { + int diff = mLastConfig.updateFrom(newConfig); + for (int change: SUPPORTED_CHANGES) { + if ((diff & change) != 0) { + inflateLayout(); + return; + } + } } protected void inflateLayoutImpl() { @@ -80,26 +88,6 @@ public class AutoReinflateContainer extends FrameLayout implements listener.onInflated(getChildAt(0)); } - @Override - public void onDensityOrFontScaleChanged() { - inflateLayout(); - } - - @Override - public void onThemeChanged() { - inflateLayout(); - } - - @Override - public void onUiModeChanged() { - inflateLayout(); - } - - @Override - public void onLocaleListChanged() { - inflateLayout(); - } - public interface InflateListener { /** * Called whenever a new view is inflated. diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index ef16a3a3c63d..aade71a83c28 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -106,7 +106,6 @@ import com.android.systemui.statusbar.policy.AccessibilityController; import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.CastController; -import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DataSaverController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.ExtensionController; @@ -134,14 +133,14 @@ import com.android.systemui.util.leak.LeakDetector; import com.android.systemui.util.leak.LeakReporter; import com.android.systemui.util.sensors.AsyncSensorManager; +import dagger.Lazy; + import java.util.concurrent.Executor; import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Named; -import dagger.Lazy; - /** * Class to handle ugly dependencies throughout sysui until we determine the * long-term dependency injection solution. @@ -270,7 +269,6 @@ public class Dependency { @Inject Lazy<NotificationShadeWindowController> mNotificationShadeWindowController; @Inject Lazy<StatusBarWindowController> mTempStatusBarWindowController; @Inject Lazy<DarkIconDispatcher> mDarkIconDispatcher; - @Inject Lazy<ConfigurationController> mConfigurationController; @Inject Lazy<StatusBarIconController> mStatusBarIconController; @Inject Lazy<ScreenLifecycle> mScreenLifecycle; @Inject Lazy<WakefulnessLifecycle> mWakefulnessLifecycle; @@ -441,8 +439,6 @@ public class Dependency { mProviders.put(DarkIconDispatcher.class, mDarkIconDispatcher::get); - mProviders.put(ConfigurationController.class, mConfigurationController::get); - mProviders.put(StatusBarIconController.class, mStatusBarIconController::get); mProviders.put(ScreenLifecycle.class, mScreenLifecycle::get); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index 2d1b7ae610c0..2925d8dbd03a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -792,7 +792,8 @@ class KeyguardUnlockAnimationController @Inject constructor( // Translate up from the bottom. surfaceBehindMatrix.setTranslate( surfaceBehindRemoteAnimationTarget.localBounds.left.toFloat(), - surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount) + surfaceBehindRemoteAnimationTarget.localBounds.top.toFloat() + + surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount) ) // Scale up from a point at the center-bottom of the surface. diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 7a2013e2c612..93ddfba5b6fc 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1610,8 +1610,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } private void doKeyguardLaterForChildProfilesLocked() { - UserManager um = UserManager.get(mContext); - for (int profileId : um.getEnabledProfileIds(UserHandle.myUserId())) { + for (UserInfo profile : mUserTracker.getUserProfiles()) { + if (!profile.isEnabled()) continue; + final int profileId = profile.id; if (mLockPatternUtils.isSeparateProfileChallengeEnabled(profileId)) { long userTimeout = getLockTimeout(profileId); if (userTimeout == 0) { @@ -1634,8 +1635,9 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, } private void doKeyguardForChildProfilesLocked() { - UserManager um = UserManager.get(mContext); - for (int profileId : um.getEnabledProfileIds(UserHandle.myUserId())) { + for (UserInfo profile : mUserTracker.getUserProfiles()) { + if (!profile.isEnabled()) continue; + final int profileId = profile.id; if (mLockPatternUtils.isSeparateProfileChallengeEnabled(profileId)) { lockProfile(profileId); } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt index aab898e2efcf..8aec0c61c75c 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt @@ -284,7 +284,15 @@ constructor( /** @see OnRoleHoldersChangedListener */ fun onRoleHoldersChanged(roleName: String, user: UserHandle) { - if (roleName == ROLE_NOTES) updateNoteTaskAsUser(user) + if (roleName != ROLE_NOTES) return + + if (user == userTracker.userHandle) { + updateNoteTaskAsUser(user) + } else { + // TODO(b/278729185): Replace fire and forget service with a bounded service. + val intent = NoteTaskControllerUpdateService.createIntent(context) + context.startServiceAsUser(intent, user) + } } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt new file mode 100644 index 000000000000..26b35cc8ffd1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskControllerUpdateService.kt @@ -0,0 +1,55 @@ +/* + * 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.notetask + +import android.content.Context +import android.content.Intent +import androidx.lifecycle.LifecycleService +import javax.inject.Inject + +/** + * A fire & forget service for updating note task shortcuts. + * + * The main use is to update shortcuts in different user by launching it using `startServiceAsUser`. + * The service will open with access to a context from that user, trigger + * [NoteTaskController.updateNoteTaskAsUser] and [stopSelf] immediately. + * + * The fire and forget approach was created due to its simplicity but may use unnecessary resources + * by recreating the services. We will investigate its impacts and consider to move to a bounded + * services - the implementation is more complex as a bounded service is asynchronous by default. + * + * TODO(b/278729185): Replace fire and forget service with a bounded service. + */ +@InternalNoteTaskApi +class NoteTaskControllerUpdateService +@Inject +constructor( + val controller: NoteTaskController, +) : LifecycleService() { + + override fun onCreate() { + super.onCreate() + // TODO(b/278729185): Replace fire and forget service with a bounded service. + controller.updateNoteTaskAsUser(user) + stopSelf() + } + + companion object { + fun createIntent(context: Context): Intent = + Intent(context, NoteTaskControllerUpdateService::class.java) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt index 1839dfd3fe32..a166393ec29c 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt @@ -14,9 +14,12 @@ * limitations under the License. */ +@file:OptIn(InternalNoteTaskApi::class) + package com.android.systemui.notetask import android.app.Activity +import android.app.Service import android.app.role.RoleManager import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags @@ -34,6 +37,9 @@ import dagger.multibindings.IntoMap @Module(includes = [NoteTaskQuickAffordanceModule::class]) interface NoteTaskModule { + @[Binds IntoMap ClassKey(NoteTaskControllerUpdateService::class)] + fun NoteTaskControllerUpdateService.bindNoteTaskControllerUpdateService(): Service + @[Binds IntoMap ClassKey(LaunchNoteTaskActivity::class)] fun LaunchNoteTaskActivity.bindNoteTaskLauncherActivity(): Activity diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt index b445000c467d..5850a846eed6 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt @@ -47,19 +47,36 @@ constructor( viewsIdToTranslate = setOf( ViewIdToTranslate(R.id.quick_settings_panel, START, filterShade), - ViewIdToTranslate(R.id.notification_stack_scroller, END, filterShade), - ViewIdToTranslate(R.id.statusIcons, END, filterShade), - ViewIdToTranslate(R.id.privacy_container, END, filterShade), - ViewIdToTranslate(R.id.batteryRemainingIcon, END, filterShade), - ViewIdToTranslate(R.id.carrier_group, END, filterShade), - ViewIdToTranslate(R.id.clock, START, filterShade), - ViewIdToTranslate(R.id.date, START, filterShade)), + ViewIdToTranslate(R.id.notification_stack_scroller, END, filterShade)), progressProvider = progressProvider) } + private val translateAnimatorStatusBar by lazy { + UnfoldConstantTranslateAnimator( + viewsIdToTranslate = + setOf( + ViewIdToTranslate(R.id.statusIcons, END, filterShade), + ViewIdToTranslate(R.id.privacy_container, END, filterShade), + ViewIdToTranslate(R.id.batteryRemainingIcon, END, filterShade), + ViewIdToTranslate(R.id.carrier_group, END, filterShade), + ViewIdToTranslate(R.id.clock, START, filterShade), + ViewIdToTranslate(R.id.date, START, filterShade) + ), + progressProvider = progressProvider + ) + } + fun setup(root: ViewGroup) { val translationMax = context.resources.getDimensionPixelSize(R.dimen.notification_side_paddings).toFloat() translateAnimator.init(root, translationMax) + val splitShadeStatusBarViewGroup: ViewGroup? = + root.findViewById(R.id.split_shade_status_bar) + if (splitShadeStatusBarViewGroup != null) { + translateAnimatorStatusBar.init( + splitShadeStatusBarViewGroup, + translationMax + ) + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt index 7dc622b86b4e..55f221df1f0a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt @@ -620,6 +620,38 @@ internal class NoteTaskControllerTest : SysuiTestCase() { } } + // region onRoleHoldersChanged + @Test + fun onRoleHoldersChanged_notNotesRole_doNothing() { + val user = UserHandle.of(0) + + createNoteTaskController(isEnabled = true).onRoleHoldersChanged("NOT_NOTES", user) + + verifyZeroInteractions(context) + } + + @Test + fun onRoleHoldersChanged_notesRole_sameUser_shouldUpdateShortcuts() { + val user = userTracker.userHandle + val controller = spy(createNoteTaskController()) + doNothing().whenever(controller).updateNoteTaskAsUser(any()) + + controller.onRoleHoldersChanged(ROLE_NOTES, user) + + verify(controller).updateNoteTaskAsUser(user) + } + + @Test + fun onRoleHoldersChanged_notesRole_differentUser_shouldUpdateShortcutsInUserProcess() { + // FakeUserTracker will default to UserHandle.SYSTEM. + val user = UserHandle.CURRENT + + createNoteTaskController(isEnabled = true).onRoleHoldersChanged(ROLE_NOTES, user) + + verify(context).startServiceAsUser(any(), eq(user)) + } + // endregion + // region updateNoteTaskAsUser @Test fun updateNoteTaskAsUser_withNotesRole_withShortcuts_shouldUpdateShortcuts() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt index db6fc136e651..38a666eeb410 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationControllerTest.kt @@ -37,6 +37,7 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock +import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -54,10 +55,12 @@ class NotificationPanelUnfoldAnimationControllerTest : SysuiTestCase() { @Mock private lateinit var parent: ViewGroup + @Mock private lateinit var splitShadeStatusBar: ViewGroup + @Mock private lateinit var statusBarStateController: StatusBarStateController private lateinit var underTest: NotificationPanelUnfoldAnimationController - private lateinit var progressListener: TransitionProgressListener + private lateinit var progressListeners: List<TransitionProgressListener> private var xTranslationMax = 0f @Before @@ -73,10 +76,13 @@ class NotificationPanelUnfoldAnimationControllerTest : SysuiTestCase() { statusBarStateController, progressProvider ) + whenever(parent.findViewById<ViewGroup>(R.id.split_shade_status_bar)).thenReturn( + splitShadeStatusBar + ) underTest.setup(parent) - verify(progressProvider).addCallback(capture(progressListenerCaptor)) - progressListener = progressListenerCaptor.value + verify(progressProvider, atLeastOnce()).addCallback(capture(progressListenerCaptor)) + progressListeners = progressListenerCaptor.allValues } @Test @@ -86,16 +92,16 @@ class NotificationPanelUnfoldAnimationControllerTest : SysuiTestCase() { val view = View(context) whenever(parent.findViewById<View>(R.id.quick_settings_panel)).thenReturn(view) - progressListener.onTransitionStarted() + onTransitionStarted() assertThat(view.translationX).isZero() - progressListener.onTransitionProgress(0f) + onTransitionProgress(0f) assertThat(view.translationX).isZero() - progressListener.onTransitionProgress(0.5f) + onTransitionProgress(0.5f) assertThat(view.translationX).isZero() - progressListener.onTransitionFinished() + onTransitionFinished() assertThat(view.translationX).isZero() } @@ -106,16 +112,16 @@ class NotificationPanelUnfoldAnimationControllerTest : SysuiTestCase() { val view = View(context) whenever(parent.findViewById<View>(R.id.quick_settings_panel)).thenReturn(view) - progressListener.onTransitionStarted() + onTransitionStarted() assertThat(view.translationX).isZero() - progressListener.onTransitionProgress(0f) + onTransitionProgress(0f) assertThat(view.translationX).isEqualTo(xTranslationMax) - progressListener.onTransitionProgress(0.5f) + onTransitionProgress(0.5f) assertThat(view.translationX).isEqualTo(0.5f * xTranslationMax) - progressListener.onTransitionFinished() + onTransitionFinished() assertThat(view.translationX).isZero() } @@ -126,16 +132,88 @@ class NotificationPanelUnfoldAnimationControllerTest : SysuiTestCase() { val view = View(context) whenever(parent.findViewById<View>(R.id.quick_settings_panel)).thenReturn(view) - progressListener.onTransitionStarted() + onTransitionStarted() assertThat(view.translationX).isZero() - progressListener.onTransitionProgress(0f) + onTransitionProgress(0f) assertThat(view.translationX).isEqualTo(xTranslationMax) - progressListener.onTransitionProgress(0.5f) + onTransitionProgress(0.5f) assertThat(view.translationX).isEqualTo(0.5f * xTranslationMax) - progressListener.onTransitionFinished() + onTransitionFinished() assertThat(view.translationX).isZero() } + + @Test + fun whenInKeyguardState_statusBarViewDoesNotMove() { + whenever(statusBarStateController.getState()).thenReturn(KEYGUARD) + + val view = View(context) + whenever(splitShadeStatusBar.findViewById<View>(R.id.date)).thenReturn(view) + + onTransitionStarted() + assertThat(view.translationX).isZero() + + onTransitionProgress(0f) + assertThat(view.translationX).isZero() + + onTransitionProgress(0.5f) + assertThat(view.translationX).isZero() + + onTransitionFinished() + assertThat(view.translationX).isZero() + } + + @Test + fun whenInShadeState_statusBarViewDoesMove() { + whenever(statusBarStateController.getState()).thenReturn(SHADE) + + val view = View(context) + whenever(splitShadeStatusBar.findViewById<View>(R.id.date)).thenReturn(view) + + onTransitionStarted() + assertThat(view.translationX).isZero() + + onTransitionProgress(0f) + assertThat(view.translationX).isEqualTo(xTranslationMax) + + onTransitionProgress(0.5f) + assertThat(view.translationX).isEqualTo(0.5f * xTranslationMax) + + onTransitionFinished() + assertThat(view.translationX).isZero() + } + + @Test + fun whenInShadeLockedState_statusBarViewDoesMove() { + whenever(statusBarStateController.getState()).thenReturn(SHADE_LOCKED) + + val view = View(context) + whenever(splitShadeStatusBar.findViewById<View>(R.id.date)).thenReturn(view) + onTransitionStarted() + assertThat(view.translationX).isZero() + + onTransitionProgress(0f) + assertThat(view.translationX).isEqualTo(xTranslationMax) + + onTransitionProgress(0.5f) + assertThat(view.translationX).isEqualTo(0.5f * xTranslationMax) + + onTransitionFinished() + assertThat(view.translationX).isZero() + } + + private fun onTransitionStarted() { + progressListeners.forEach { it.onTransitionStarted() } + } + + private fun onTransitionProgress(progress: Float) { + progressListeners.forEach { it.onTransitionProgress(progress) } + } + + private fun onTransitionFinished() { + progressListeners.forEach { it.onTransitionFinished() } + } + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java index 780e0c56a239..6fda56c8717a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java @@ -116,7 +116,6 @@ public class HeadsUpManagerPhoneTest extends AlertingNotificationManagerTest { .thenReturn(TEST_AUTO_DISMISS_TIME); when(mVSProvider.isReorderingAllowed()).thenReturn(true); mDependency.injectMockDependency(NotificationShadeWindowController.class); - mDependency.injectMockDependency(ConfigurationController.class); super.setUp(); mHeadsUpManager = new TestableHeadsUpManagerPhone( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java index 71ac7c48d5ea..683136d7af0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java @@ -45,6 +45,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.util.Map; import java.util.function.Consumer; @@ -53,16 +55,18 @@ import java.util.function.Consumer; @SmallTest public class ExtensionControllerImplTest extends SysuiTestCase { + @Mock + private ConfigurationController mConfigurationController; + private PluginManager mPluginManager; private TunerService mTunerService; private ExtensionController mExtensionController; - private ConfigurationController mConfigurationController; @Before public void setup() { + MockitoAnnotations.initMocks(this); mPluginManager = mDependency.injectMockDependency(PluginManager.class); mTunerService = mDependency.injectMockDependency(TunerService.class); - mConfigurationController = mDependency.injectMockDependency(ConfigurationController.class); mExtensionController = new ExtensionControllerImpl( mContext, mock(LeakDetector.class), diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index a021174e3031..ca482dc41ce5 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -2396,7 +2396,6 @@ public class DisplayDeviceConfig { mHbmData.timeWindowMillis = hbmTiming.getTimeWindowSecs_all().longValue() * 1000; mHbmData.timeMaxMillis = hbmTiming.getTimeMaxSecs_all().longValue() * 1000; mHbmData.timeMinMillis = hbmTiming.getTimeMinSecs_all().longValue() * 1000; - mHbmData.thermalStatusLimit = convertThermalStatus(hbm.getThermalStatusLimit_all()); mHbmData.allowInLowPowerMode = hbm.getAllowInLowPowerMode_all(); final RefreshRateRange rr = hbm.getRefreshRate_all(); if (rr != null) { @@ -2972,9 +2971,6 @@ public class DisplayDeviceConfig { /** Brightness level at which we transition from normal to high-brightness. */ public float transitionPoint; - /** Enable HBM only if the thermal status is not higher than this. */ - public @PowerManager.ThermalStatus int thermalStatusLimit; - /** Whether HBM is allowed when {@code Settings.Global.LOW_POWER_MODE} is active. */ public boolean allowInLowPowerMode; @@ -2993,15 +2989,13 @@ public class DisplayDeviceConfig { HighBrightnessModeData() {} HighBrightnessModeData(float minimumLux, float transitionPoint, long timeWindowMillis, - long timeMaxMillis, long timeMinMillis, - @PowerManager.ThermalStatus int thermalStatusLimit, boolean allowInLowPowerMode, + long timeMaxMillis, long timeMinMillis, boolean allowInLowPowerMode, float minimumHdrPercentOfScreen) { this.minimumLux = minimumLux; this.transitionPoint = transitionPoint; this.timeWindowMillis = timeWindowMillis; this.timeMaxMillis = timeMaxMillis; this.timeMinMillis = timeMinMillis; - this.thermalStatusLimit = thermalStatusLimit; this.allowInLowPowerMode = allowInLowPowerMode; this.minimumHdrPercentOfScreen = minimumHdrPercentOfScreen; } @@ -3016,7 +3010,6 @@ public class DisplayDeviceConfig { other.timeMaxMillis = timeMaxMillis; other.timeMinMillis = timeMinMillis; other.transitionPoint = transitionPoint; - other.thermalStatusLimit = thermalStatusLimit; other.allowInLowPowerMode = allowInLowPowerMode; other.minimumHdrPercentOfScreen = minimumHdrPercentOfScreen; } @@ -3029,7 +3022,6 @@ public class DisplayDeviceConfig { + ", timeWindow: " + timeWindowMillis + "ms" + ", timeMax: " + timeMaxMillis + "ms" + ", timeMin: " + timeMinMillis + "ms" - + ", thermalStatusLimit: " + thermalStatusLimit + ", allowInLowPowerMode: " + allowInLowPowerMode + ", minimumHdrPercentOfScreen: " + minimumHdrPercentOfScreen + "} "; diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index f1efec04fa69..78c5f0eee492 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -2828,6 +2828,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(" mDisplayId=" + mDisplayId); pw.println(" mLeadDisplayId=" + mLeadDisplayId); pw.println(" mLightSensor=" + mLightSensor); + pw.println(" mDisplayBrightnessFollowers=" + mDisplayBrightnessFollowers); pw.println(); pw.println("Display Power Controller Locked State:"); diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index 59e112ecbccb..a76f907a41f5 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -2222,6 +2222,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal pw.println(" mDisplayId=" + mDisplayId); pw.println(" mLeadDisplayId=" + mLeadDisplayId); pw.println(" mLightSensor=" + mLightSensor); + pw.println(" mDisplayBrightnessFollowers=" + mDisplayBrightnessFollowers); pw.println(); pw.println("Display Power Controller Locked State:"); diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java index ca208ac73290..11160a532609 100644 --- a/services/core/java/com/android/server/display/HighBrightnessModeController.java +++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java @@ -22,13 +22,8 @@ import android.hardware.display.BrightnessInfo; import android.net.Uri; import android.os.Handler; import android.os.IBinder; -import android.os.IThermalEventListener; -import android.os.IThermalService; import android.os.PowerManager; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; -import android.os.Temperature; import android.os.UserHandle; import android.provider.Settings; import android.util.MathUtils; @@ -75,7 +70,6 @@ class HighBrightnessModeController { private final Runnable mHbmChangeCallback; private final Runnable mRecalcRunnable; private final Clock mClock; - private final SkinThermalStatusObserver mSkinThermalStatusObserver; private final Context mContext; private final SettingsObserver mSettingsObserver; private final Injector mInjector; @@ -100,10 +94,8 @@ class HighBrightnessModeController { private int mHbmMode = BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; private boolean mIsHdrLayerPresent = false; - // mMaxDesiredHdrSdrRatio should only be applied when there is a valid backlight->nits mapping private float mMaxDesiredHdrSdrRatio = DEFAULT_MAX_DESIRED_HDR_SDR_RATIO; - private boolean mIsThermalStatusWithinLimit = true; private boolean mIsBlockedByLowPowerMode = false; private int mWidth; private int mHeight; @@ -138,7 +130,6 @@ class HighBrightnessModeController { mBrightnessMax = brightnessMax; mHbmChangeCallback = hbmChangeCallback; mHighBrightnessModeMetadata = hbmMetadata; - mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler); mSettingsObserver = new SettingsObserver(mHandler); mRecalcRunnable = this::recalculateTimeAllowance; mHdrListener = new HdrListener(); @@ -261,7 +252,6 @@ class HighBrightnessModeController { void stop() { registerHdrListener(null /*displayToken*/); - mSkinThermalStatusObserver.stopObserving(); mSettingsObserver.stopObserving(); } @@ -278,15 +268,10 @@ class HighBrightnessModeController { mDisplayStatsId = displayUniqueId.hashCode(); unregisterHdrListener(); - mSkinThermalStatusObserver.stopObserving(); mSettingsObserver.stopObserving(); if (deviceSupportsHbm()) { registerHdrListener(displayToken); recalculateTimeAllowance(); - if (mHbmData.thermalStatusLimit > PowerManager.THERMAL_STATUS_NONE) { - mIsThermalStatusWithinLimit = true; - mSkinThermalStatusObserver.startObserving(); - } if (!mHbmData.allowInLowPowerMode) { mIsBlockedByLowPowerMode = false; mSettingsObserver.startObserving(); @@ -327,7 +312,6 @@ class HighBrightnessModeController { pw.println(" mIsTimeAvailable= " + mIsTimeAvailable); pw.println(" mRunningStartTimeMillis=" + TimeUtils.formatUptime(mHighBrightnessModeMetadata.getRunningStartTimeMillis())); - pw.println(" mIsThermalStatusWithinLimit=" + mIsThermalStatusWithinLimit); pw.println(" mIsBlockedByLowPowerMode=" + mIsBlockedByLowPowerMode); pw.println(" width*height=" + mWidth + "*" + mHeight); pw.println(" mEvents="); @@ -344,8 +328,6 @@ class HighBrightnessModeController { } lastStartTime = dumpHbmEvent(pw, event); } - - mSkinThermalStatusObserver.dump(pw); } private long dumpHbmEvent(PrintWriter pw, HbmEvent event) { @@ -367,7 +349,7 @@ class HighBrightnessModeController { // See {@link #getHdrBrightnessValue}. return !mIsHdrLayerPresent && (mIsAutoBrightnessEnabled && mIsTimeAvailable && mIsInAllowedAmbientRange - && mIsThermalStatusWithinLimit && !mIsBlockedByLowPowerMode); + && !mIsBlockedByLowPowerMode); } private boolean deviceSupportsHbm() { @@ -469,7 +451,6 @@ class HighBrightnessModeController { + ", isAutoBrightnessEnabled: " + mIsAutoBrightnessEnabled + ", mIsTimeAvailable: " + mIsTimeAvailable + ", mIsInAllowedAmbientRange: " + mIsInAllowedAmbientRange - + ", mIsThermalStatusWithinLimit: " + mIsThermalStatusWithinLimit + ", mIsBlockedByLowPowerMode: " + mIsBlockedByLowPowerMode + ", mBrightness: " + mBrightness + ", mUnthrottledBrightness: " + mUnthrottledBrightness @@ -499,13 +480,12 @@ class HighBrightnessModeController { } private void updateHbmStats(int newMode) { - final float transitionPoint = mHbmData.transitionPoint; int state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF; if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR - && getHdrBrightnessValue() > transitionPoint) { + && getHdrBrightnessValue() > mHbmData.transitionPoint) { state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR; } else if (newMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT - && mBrightness > transitionPoint) { + && mBrightness > mHbmData.transitionPoint) { state = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT; } if (state == mHbmStatsState) { @@ -519,16 +499,6 @@ class HighBrightnessModeController { final boolean newHbmSv = (state == FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT); if (oldHbmSv && !newHbmSv) { - // HighBrightnessModeController (HBMC) currently supports throttling from two sources: - // 1. Internal, received from HBMC.SkinThermalStatusObserver.notifyThrottling() - // 2. External, received from HBMC.onBrightnessChanged() - // TODO(b/216373254): Deprecate internal throttling source - final boolean internalThermalThrottling = !mIsThermalStatusWithinLimit; - final boolean externalThermalThrottling = - mUnthrottledBrightness > transitionPoint && // We would've liked HBM brightness... - mBrightness <= transitionPoint && // ...but we got NBM, because of... - mThrottlingReason == BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL; // ...thermals. - // If more than one conditions are flipped and turn off HBM sunlight // visibility, only one condition will be reported to make it simple. if (!mIsAutoBrightnessEnabled && mIsAutoBrightnessOffByState) { @@ -541,7 +511,7 @@ class HighBrightnessModeController { reason = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LUX_DROP; } else if (!mIsTimeAvailable) { reason = FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_TIME_LIMIT; - } else if (internalThermalThrottling || externalThermalThrottling) { + } else if (isThermalThrottlingActive()) { reason = FrameworkStatsLog .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT; } else if (mIsHdrLayerPresent) { @@ -561,6 +531,14 @@ class HighBrightnessModeController { mHbmStatsState = state; } + @VisibleForTesting + boolean isThermalThrottlingActive() { + // We would've liked HBM, but we got NBM (normal brightness mode) because of thermals. + return mUnthrottledBrightness > mHbmData.transitionPoint + && mBrightness <= mHbmData.transitionPoint + && mThrottlingReason == BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL; + } + private String hbmStatsStateToString(int hbmStatsState) { switch (hbmStatsState) { case FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF: @@ -635,82 +613,6 @@ class HighBrightnessModeController { } } - private final class SkinThermalStatusObserver extends IThermalEventListener.Stub { - private final Injector mInjector; - private final Handler mHandler; - - private IThermalService mThermalService; - private boolean mStarted; - - SkinThermalStatusObserver(Injector injector, Handler handler) { - mInjector = injector; - mHandler = handler; - } - - @Override - public void notifyThrottling(Temperature temp) { - if (DEBUG) { - Slog.d(TAG, "New thermal throttling status " - + ", current thermal status = " + temp.getStatus() - + ", threshold = " + mHbmData.thermalStatusLimit); - } - mHandler.post(() -> { - mIsThermalStatusWithinLimit = temp.getStatus() <= mHbmData.thermalStatusLimit; - // This recalculates HbmMode and runs mHbmChangeCallback if the mode has changed - updateHbmMode(); - }); - } - - void startObserving() { - if (mStarted) { - if (DEBUG) { - Slog.d(TAG, "Thermal status observer already started"); - } - return; - } - mThermalService = mInjector.getThermalService(); - if (mThermalService == null) { - Slog.w(TAG, "Could not observe thermal status. Service not available"); - return; - } - try { - // We get a callback immediately upon registering so there's no need to query - // for the current value. - mThermalService.registerThermalEventListenerWithType(this, Temperature.TYPE_SKIN); - mStarted = true; - } catch (RemoteException e) { - Slog.e(TAG, "Failed to register thermal status listener", e); - } - } - - void stopObserving() { - mIsThermalStatusWithinLimit = true; - if (!mStarted) { - if (DEBUG) { - Slog.d(TAG, "Stop skipped because thermal status observer not started"); - } - return; - } - try { - mThermalService.unregisterThermalEventListener(this); - mStarted = false; - } catch (RemoteException e) { - Slog.e(TAG, "Failed to unregister thermal status listener", e); - } - mThermalService = null; - } - - void dump(PrintWriter writer) { - writer.println(" SkinThermalStatusObserver:"); - writer.println(" mStarted: " + mStarted); - if (mThermalService != null) { - writer.println(" ThermalService available"); - } else { - writer.println(" ThermalService not available"); - } - } - } - private final class SettingsObserver extends ContentObserver { private final Uri mLowPowerModeSetting = Settings.Global.getUriFor( Settings.Global.LOW_POWER_MODE); @@ -766,11 +668,6 @@ class HighBrightnessModeController { return SystemClock::uptimeMillis; } - public IThermalService getThermalService() { - return IThermalService.Stub.asInterface( - ServiceManager.getService(Context.THERMAL_SERVICE)); - } - public void reportHbmStateChange(int display, int state, int reason) { FrameworkStatsLog.write( FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED, display, state, reason); diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index dab00d8070d4..0b6d1c851dc4 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -181,6 +181,19 @@ final class LogicalDisplay { */ private String mThermalBrightnessThrottlingDataId; + /** + * Refresh rate range limitation based on the current device layout + */ + @Nullable + private SurfaceControl.RefreshRateRange mLayoutLimitedRefreshRate; + + /** + * RefreshRateRange limitation for @Temperature.ThrottlingStatus + */ + @NonNull + private SparseArray<SurfaceControl.RefreshRateRange> mThermalRefreshRateThrottling = + new SparseArray<>(); + public LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) { mDisplayId = displayId; mLayerStack = layerStack; @@ -339,24 +352,24 @@ final class LogicalDisplay { */ public void updateLayoutLimitedRefreshRateLocked( @Nullable SurfaceControl.RefreshRateRange layoutLimitedRefreshRate) { - if (!Objects.equals(layoutLimitedRefreshRate, mBaseDisplayInfo.layoutLimitedRefreshRate)) { - mBaseDisplayInfo.layoutLimitedRefreshRate = layoutLimitedRefreshRate; - mInfo.set(null); + if (!Objects.equals(layoutLimitedRefreshRate, mLayoutLimitedRefreshRate)) { + mLayoutLimitedRefreshRate = layoutLimitedRefreshRate; + mDirty = true; } } /** - * Updates refreshRateThermalThrottling + * Updates thermalRefreshRateThrottling * - * @param refreshRanges new refreshRateThermalThrottling ranges limited by layout or default + * @param refreshRanges new thermalRefreshRateThrottling ranges limited by layout or default */ public void updateThermalRefreshRateThrottling( @Nullable SparseArray<SurfaceControl.RefreshRateRange> refreshRanges) { if (refreshRanges == null) { refreshRanges = new SparseArray<>(); } - if (!mBaseDisplayInfo.refreshRateThermalThrottling.contentEquals(refreshRanges)) { - mBaseDisplayInfo.refreshRateThermalThrottling = refreshRanges; - mInfo.set(null); + if (!mThermalRefreshRateThrottling.contentEquals(refreshRanges)) { + mThermalRefreshRateThrottling = refreshRanges; + mDirty = true; } } @@ -499,6 +512,9 @@ final class LogicalDisplay { mBaseDisplayInfo.removeMode = Display.REMOVE_MODE_DESTROY_CONTENT; } + mBaseDisplayInfo.layoutLimitedRefreshRate = mLayoutLimitedRefreshRate; + mBaseDisplayInfo.thermalRefreshRateThrottling = mThermalRefreshRateThrottling; + mPrimaryDisplayDeviceInfo = deviceInfo; mInfo.set(null); mDirty = false; @@ -952,6 +968,8 @@ final class LogicalDisplay { pw.println("mDisplayGroupName=" + mDisplayGroupName); pw.println("mThermalBrightnessThrottlingDataId=" + mThermalBrightnessThrottlingDataId); pw.println("mLeadDisplayId=" + mLeadDisplayId); + pw.println("mLayoutLimitedRefreshRate=" + mLayoutLimitedRefreshRate); + pw.println("mThermalRefreshRateThrottling=" + mThermalRefreshRateThrottling); } @Override diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java index 01892945ed17..06b7698e9df2 100644 --- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -65,6 +65,7 @@ import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.display.BrightnessSynchronizer; +import com.android.internal.display.RefreshRateSettingsUtils; import com.android.internal.os.BackgroundThread; import com.android.server.LocalServices; import com.android.server.display.DisplayDeviceConfig; @@ -1171,7 +1172,7 @@ public class DisplayModeDirector { public static final int PRIORITY_HIGH_BRIGHTNESS_MODE = 2; // SETTING_MIN_RENDER_FRAME_RATE is used to propose a lower bound of the render frame rate. - // It votes [MIN_REFRESH_RATE, Float.POSITIVE_INFINITY] + // It votes [minRefreshRate, Float.POSITIVE_INFINITY] public static final int PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE = 3; // APP_REQUEST_RENDER_FRAME_RATE_RANGE is used to for internal apps to limit the render @@ -1376,10 +1377,10 @@ public class DisplayModeDirector { @VisibleForTesting final class SettingsObserver extends ContentObserver { - private final Uri mPeakRefreshRateSetting = - Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE); - private final Uri mMinRefreshRateSetting = - Settings.System.getUriFor(Settings.System.MIN_REFRESH_RATE); + private final Uri mSmoothDisplaySetting = + Settings.System.getUriFor(Settings.System.SMOOTH_DISPLAY); + private final Uri mForcePeakRefreshRateSetting = + Settings.System.getUriFor(Settings.System.FORCE_PEAK_REFRESH_RATE); private final Uri mLowPowerModeSetting = Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE); private final Uri mMatchContentFrameRateSetting = @@ -1415,9 +1416,8 @@ public class DisplayModeDirector { public void observe() { final ContentResolver cr = mContext.getContentResolver(); - mInjector.registerPeakRefreshRateObserver(cr, this); - cr.registerContentObserver(mMinRefreshRateSetting, false /*notifyDescendants*/, this, - UserHandle.USER_SYSTEM); + mInjector.registerSmoothDisplayObserver(cr, this); + mInjector.registerForcePeakRefreshRateObserver(cr, this); cr.registerContentObserver(mLowPowerModeSetting, false /*notifyDescendants*/, this, UserHandle.USER_SYSTEM); cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/, @@ -1459,8 +1459,8 @@ public class DisplayModeDirector { @Override public void onChange(boolean selfChange, Uri uri, int userId) { synchronized (mLock) { - if (mPeakRefreshRateSetting.equals(uri) - || mMinRefreshRateSetting.equals(uri)) { + if (mSmoothDisplaySetting.equals(uri) + || mForcePeakRefreshRateSetting.equals(uri)) { updateRefreshRateSettingLocked(); } else if (mLowPowerModeSetting.equals(uri)) { updateLowPowerModeSettingLocked(); @@ -1515,12 +1515,9 @@ public class DisplayModeDirector { } private void updateRefreshRateSettingLocked() { - final ContentResolver cr = mContext.getContentResolver(); - float minRefreshRate = Settings.System.getFloatForUser(cr, - Settings.System.MIN_REFRESH_RATE, 0f, cr.getUserId()); - float peakRefreshRate = Settings.System.getFloatForUser(cr, - Settings.System.PEAK_REFRESH_RATE, mDefaultPeakRefreshRate, cr.getUserId()); - updateRefreshRateSettingLocked(minRefreshRate, peakRefreshRate, mDefaultRefreshRate); + updateRefreshRateSettingLocked(RefreshRateSettingsUtils.getMinRefreshRate(mContext), + RefreshRateSettingsUtils.getPeakRefreshRate(mContext, mDefaultPeakRefreshRate), + mDefaultRefreshRate); } private void updateRefreshRateSettingLocked( @@ -1708,14 +1705,13 @@ public class DisplayModeDirector { } public void observe() { - DisplayManager dm = mContext.getSystemService(DisplayManager.class); - dm.registerDisplayListener(this, mHandler); + mInjector.registerDisplayListener(this, mHandler); // Populate existing displays SparseArray<Display.Mode[]> modes = new SparseArray<>(); SparseArray<Display.Mode> defaultModes = new SparseArray<>(); DisplayInfo info = new DisplayInfo(); - Display[] displays = dm.getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED); + Display[] displays = mInjector.getDisplays(); for (Display d : displays) { final int displayId = d.getDisplayId(); d.getDisplayInfo(info); @@ -1754,17 +1750,9 @@ public class DisplayModeDirector { updateLayoutLimitedFrameRate(displayId, displayInfo); } - @Nullable private DisplayInfo getDisplayInfo(int displayId) { - Display d = mContext.getSystemService(DisplayManager.class).getDisplay(displayId); - if (d == null) { - // We can occasionally get a display added or changed event for a display that was - // subsequently removed, which means this returns null. Check this case and bail - // out early; if it gets re-attached we'll eventually get another call back for it. - return null; - } DisplayInfo info = new DisplayInfo(); - d.getDisplayInfo(info); + mInjector.getDisplayInfo(displayId, info); return info; } @@ -2435,8 +2423,7 @@ public class DisplayModeDirector { } private void updateDefaultDisplayState() { - Display display = mContext.getSystemService(DisplayManager.class) - .getDisplay(Display.DEFAULT_DISPLAY); + Display display = mInjector.getDisplay(Display.DEFAULT_DISPLAY); if (display == null) { return; } @@ -2753,8 +2740,7 @@ public class DisplayModeDirector { sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this); synchronized (mSensorObserverLock) { - for (Display d : mDisplayManager.getDisplays( - DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)) { + for (Display d : mInjector.getDisplays()) { mDozeStateByDisplay.put(d.getDisplayId(), mInjector.isDozeState(d)); } } @@ -2765,8 +2751,7 @@ public class DisplayModeDirector { } private void recalculateVotesLocked() { - final Display[] displays = mDisplayManager.getDisplays( - DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED); + final Display[] displays = mInjector.getDisplays(); for (Display d : displays) { int displayId = d.getDisplayId(); Vote vote = null; @@ -2797,7 +2782,7 @@ public class DisplayModeDirector { @Override public void onDisplayAdded(int displayId) { - boolean isDozeState = mInjector.isDozeState(mDisplayManager.getDisplay(displayId)); + boolean isDozeState = mInjector.isDozeState(mInjector.getDisplay(displayId)); synchronized (mSensorObserverLock) { mDozeStateByDisplay.put(displayId, isDozeState); recalculateVotesLocked(); @@ -2809,7 +2794,7 @@ public class DisplayModeDirector { boolean wasDozeState = mDozeStateByDisplay.get(displayId); synchronized (mSensorObserverLock) { mDozeStateByDisplay.put(displayId, - mInjector.isDozeState(mDisplayManager.getDisplay(displayId))); + mInjector.isDozeState(mInjector.getDisplay(displayId))); if (wasDozeState != mDozeStateByDisplay.get(displayId)) { recalculateVotesLocked(); } @@ -3165,17 +3150,27 @@ public class DisplayModeDirector { } interface Injector { - Uri PEAK_REFRESH_RATE_URI = Settings.System.getUriFor(Settings.System.PEAK_REFRESH_RATE); + Uri SMOOTH_DISPLAY_URI = Settings.System.getUriFor(Settings.System.SMOOTH_DISPLAY); + Uri FORCE_PEAK_REFRESH_RATE_URI = + Settings.System.getUriFor(Settings.System.FORCE_PEAK_REFRESH_RATE); @NonNull DeviceConfigInterface getDeviceConfig(); - void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, + void registerSmoothDisplayObserver(@NonNull ContentResolver cr, @NonNull ContentObserver observer); + void registerForcePeakRefreshRateObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer); + + void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener, + Handler handler); + void registerDisplayListener(@NonNull DisplayManager.DisplayListener listener, Handler handler, long flags); + Display getDisplay(int displayId); + Display[] getDisplays(); boolean getDisplayInfo(int displayId, DisplayInfo displayInfo); @@ -3205,19 +3200,37 @@ public class DisplayModeDirector { } @Override - public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, + public void registerSmoothDisplayObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + cr.registerContentObserver(SMOOTH_DISPLAY_URI, false /*notifyDescendants*/, + observer, UserHandle.USER_SYSTEM); + } + + @Override + public void registerForcePeakRefreshRateObserver(@NonNull ContentResolver cr, @NonNull ContentObserver observer) { - cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/, + cr.registerContentObserver(FORCE_PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/, observer, UserHandle.USER_SYSTEM); } @Override public void registerDisplayListener(DisplayManager.DisplayListener listener, + Handler handler) { + getDisplayManager().registerDisplayListener(listener, handler); + } + + @Override + public void registerDisplayListener(DisplayManager.DisplayListener listener, Handler handler, long flags) { getDisplayManager().registerDisplayListener(listener, handler, flags); } @Override + public Display getDisplay(int displayId) { + return getDisplayManager().getDisplay(displayId); + } + + @Override public Display[] getDisplays() { return getDisplayManager().getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED); } @@ -3225,10 +3238,13 @@ public class DisplayModeDirector { @Override public boolean getDisplayInfo(int displayId, DisplayInfo displayInfo) { Display display = getDisplayManager().getDisplay(displayId); - if (display != null) { - return display.getDisplayInfo(displayInfo); + if (display == null) { + // We can occasionally get a display added or changed event for a display that was + // subsequently removed, which means this returns null. Check this case and bail + // out early; if it gets re-attached we'll eventually get another call back for it. + return false; } - return false; + return display.getDisplayInfo(displayInfo); } @Override diff --git a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java index c04735d8f7e2..8a3b329211cf 100644 --- a/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java +++ b/services/core/java/com/android/server/display/mode/SkinThermalStatusObserver.java @@ -138,7 +138,7 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme for (Display d : displays) { final int displayId = d.getDisplayId(); d.getDisplayInfo(info); - localMap.put(displayId, info.refreshRateThermalThrottling); + localMap.put(displayId, info.thermalRefreshRateThrottling); } synchronized (mThermalObserverLock) { for (int i = 0; i < size; i++) { @@ -154,7 +154,7 @@ final class SkinThermalStatusObserver extends IThermalEventListener.Stub impleme DisplayInfo displayInfo = new DisplayInfo(); mInjector.getDisplayInfo(displayId, displayInfo); SparseArray<SurfaceControl.RefreshRateRange> throttlingMap = - displayInfo.refreshRateThermalThrottling; + displayInfo.thermalRefreshRateThrottling; synchronized (mThermalObserverLock) { mThermalThrottlingByDisplay.put(displayId, throttlingMap); diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index f733199d9967..2460ce56f165 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -48,6 +48,7 @@ import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; import android.metrics.LogMaker; +import android.net.Uri; import android.os.Binder; import android.os.Build; import android.os.UserHandle; @@ -60,6 +61,7 @@ import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IntArray; +import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; @@ -387,7 +389,8 @@ public class PreferencesHelper implements RankingConfig { NotificationChannel channel = new NotificationChannel( id, channelName, channelImportance); if (forRestore) { - channel.populateFromXmlForRestore(parser, mContext); + final boolean pkgInstalled = r.uid != UNKNOWN_UID; + channel.populateFromXmlForRestore(parser, pkgInstalled, mContext); } else { channel.populateFromXml(parser); } @@ -2412,6 +2415,21 @@ public class PreferencesHelper implements RankingConfig { mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, changeUserId)); synchronized (mPackagePreferences) { mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r); + + // Try to restore any unrestored sound resources + for (NotificationChannel channel : r.channels.values()) { + if (!channel.isSoundRestored()) { + Uri uri = channel.getSound(); + Uri restoredUri = channel.restoreSoundUri(mContext, uri, true); + if (Settings.System.DEFAULT_NOTIFICATION_URI.equals( + restoredUri)) { + Log.w(TAG, + "Could not restore sound: " + uri + " for channel: " + + channel); + } + channel.setSound(restoredUri, channel.getAudioAttributes()); + } + } } if (r.migrateToPm) { try { diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java index 064be7c5ddc7..39cd88810961 100644 --- a/services/core/java/com/android/server/pm/DexOptHelper.java +++ b/services/core/java/com/android/server/pm/DexOptHelper.java @@ -745,6 +745,9 @@ public final class DexOptHelper { applyPackageFilter(snapshot, remainingPredicate, result, remainingPkgSettings, sortTemp, packageManagerService); + // Make sure the system server isn't in the result, because it can never be dexopted here. + result.removeIf(pkgSetting -> PLATFORM_PACKAGE_NAME.equals(pkgSetting.getPackageName())); + if (debug) { Log.i(TAG, "Packages to be dexopted: " + packagesToString(result)); Log.i(TAG, "Packages skipped from dexopt: " + packagesToString(remainingPkgSettings)); diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index eea6720d6cc4..ef7d41309e85 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -24,6 +24,7 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; import static com.android.server.LocalManagerRegistry.ManagerNotFoundException; +import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME; import android.accounts.IAccountManager; import android.annotation.NonNull; @@ -1954,6 +1955,8 @@ class PackageManagerShellCommand extends ShellCommand { List<String> packageNames = null; if (allPackages) { packageNames = mInterface.getAllPackages(); + // Compiling the system server is only supported from odrefresh, so skip it. + packageNames.removeIf(packageName -> PLATFORM_PACKAGE_NAME.equals(packageName)); } else { String packageName = getNextArg(); if (packageName == null) { diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 85c601fe0a5c..dbd9e4b8ea68 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -107,6 +107,12 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE; private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION; + private static final int MAX_RAPID_ACTIVITY_LAUNCH_COUNT = 500; + private static final long RAPID_ACTIVITY_LAUNCH_MS = 300; + private static final long RESET_RAPID_ACTIVITY_LAUNCH_MS = 5 * RAPID_ACTIVITY_LAUNCH_MS; + + private int mRapidActivityLaunchCount; + // all about the first app in the process final ApplicationInfo mInfo; final String mName; @@ -538,7 +544,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return mLastActivityLaunchTime > 0; } - void setLastActivityLaunchTime(long launchTime) { + void setLastActivityLaunchTime(ActivityRecord r) { + long launchTime = r.lastLaunchTime; if (launchTime <= mLastActivityLaunchTime) { if (launchTime < mLastActivityLaunchTime) { Slog.w(TAG, @@ -547,9 +554,29 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } return; } + updateRapidActivityLaunch(r, launchTime, mLastActivityLaunchTime); mLastActivityLaunchTime = launchTime; } + void updateRapidActivityLaunch(ActivityRecord r, long launchTime, long lastLaunchTime) { + if (mInstrumenting || mDebugging || lastLaunchTime <= 0) { + return; + } + + final long diff = lastLaunchTime - launchTime; + if (diff < RAPID_ACTIVITY_LAUNCH_MS) { + mRapidActivityLaunchCount++; + } else if (diff >= RESET_RAPID_ACTIVITY_LAUNCH_MS) { + mRapidActivityLaunchCount = 0; + } + + if (mRapidActivityLaunchCount > MAX_RAPID_ACTIVITY_LAUNCH_COUNT) { + Slog.w(TAG, "Killing " + mPid + " because of rapid activity launch"); + r.getRootTask().moveTaskToBack(r.getTask()); + mAtm.mH.post(() -> mAtm.mAmInternal.killProcess(mName, mUid, "rapidActivityLaunch")); + } + } + void setLastActivityFinishTimeIfNeeded(long finishTime) { if (finishTime <= mLastActivityFinishTime || !hasActivityInVisibleTask()) { return; @@ -696,7 +723,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio void addActivityIfNeeded(ActivityRecord r) { // even if we already track this activity, note down that it has been launched - setLastActivityLaunchTime(r.lastLaunchTime); + setLastActivityLaunchTime(r); if (mActivities.contains(r)) { return; } diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index 981844cf9338..f96ca582c28f 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -153,12 +153,6 @@ <xs:annotation name="nullable"/> <xs:annotation name="final"/> </xs:element> - <!-- The highest (most severe) thermal status at which high-brightness-mode is allowed - to operate. --> - <xs:element name="thermalStatusLimit" type="thermalStatus" minOccurs="0" maxOccurs="1"> - <xs:annotation name="nonnull"/> - <xs:annotation name="final"/> - </xs:element> <xs:element name="allowInLowPowerMode" type="xs:boolean" minOccurs="0" maxOccurs="1"> <xs:annotation name="nonnull"/> <xs:annotation name="final"/> diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index 8cb483770c85..ad6434e0c545 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -156,7 +156,6 @@ package com.android.server.display.config { method @NonNull public final java.math.BigDecimal getMinimumLux_all(); method @Nullable public final com.android.server.display.config.RefreshRateRange getRefreshRate_all(); method @Nullable public final com.android.server.display.config.SdrHdrRatioMap getSdrHdrRatioMap_all(); - method @NonNull public final com.android.server.display.config.ThermalStatus getThermalStatusLimit_all(); method public com.android.server.display.config.HbmTiming getTiming_all(); method @NonNull public final java.math.BigDecimal getTransitionPoint_all(); method public final void setAllowInLowPowerMode_all(@NonNull boolean); @@ -165,7 +164,6 @@ package com.android.server.display.config { method public final void setMinimumLux_all(@NonNull java.math.BigDecimal); method public final void setRefreshRate_all(@Nullable com.android.server.display.config.RefreshRateRange); method public final void setSdrHdrRatioMap_all(@Nullable com.android.server.display.config.SdrHdrRatioMap); - method public final void setThermalStatusLimit_all(@NonNull com.android.server.display.config.ThermalStatus); method public void setTiming_all(com.android.server.display.config.HbmTiming); method public final void setTransitionPoint_all(@NonNull java.math.BigDecimal); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 4d739d2c3685..f6bc93ab2491 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -5865,8 +5865,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // would allow bypassing of the maximum time to lock. mInjector.settingsGlobalPutInt(Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0); } - getPowerManagerInternal().setMaximumScreenOffTimeoutFromDeviceAdmin( - UserHandle.USER_SYSTEM, timeMs); + getPowerManagerInternal().setMaximumScreenOffTimeoutFromDeviceAdmin(parentId, timeMs); }); } @@ -9199,9 +9198,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_CAMERA, caller.getPackageName(), getProfileParentUserIfRequested(userId, parent)); - - setBackwardCompatibleUserRestriction( - caller, enforcingAdmin, UserManager.DISALLOW_CAMERA, disabled, parent); + try { + setBackwardCompatibleUserRestriction( + caller, enforcingAdmin, UserManager.DISALLOW_CAMERA, disabled, parent); + } catch (IllegalStateException e) { + throw new IllegalStateException( + "Please use addUserRestriction or addUserRestrictionGlobally using the key" + + " UserManager.DISALLOW_CAMERA to disable the camera locally or" + + " globally, respectively"); + } } else { Objects.requireNonNull(who, "ComponentName is null"); if (parent) { @@ -15465,11 +15470,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int userId = caller.getUserId(); synchronized (getLockObject()) { - Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId), - "Admin " + who - + " is neither the device owner or affiliated user's profile owner."); - if (isManagedProfile(userId)) { - throw new SecurityException("Managed profile cannot disable status bar"); + if (!isPermissionCheckFlagEnabled()) { + Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId), + "Admin " + who + " is neither the device owner or affiliated " + + "user's profile owner."); + if (isManagedProfile(userId)) { + throw new SecurityException("Managed profile cannot disable status bar"); + } } checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_STATUS_BAR_DISABLED); @@ -15522,16 +15529,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @Override public boolean isStatusBarDisabled(String callerPackage) { final CallerIdentity caller = getCallerIdentity(callerPackage); - Preconditions.checkCallAuthorization( - isProfileOwner(caller) || isDefaultDeviceOwner(caller)); + if (isPermissionCheckFlagEnabled()) { + enforceCanQuery( + MANAGE_DEVICE_POLICY_STATUS_BAR, caller.getPackageName(), caller.getUserId()); + } else { + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); + } int userId = caller.getUserId(); synchronized (getLockObject()) { - Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId), - "Admin " + callerPackage - + " is neither the device owner or affiliated user's profile owner."); - if (isManagedProfile(userId)) { - throw new SecurityException("Managed profile cannot disable status bar"); + if (!isPermissionCheckFlagEnabled()) { + Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId), + "Admin " + callerPackage + + " is neither the device owner or affiliated user's profile owner."); + if (isManagedProfile(userId)) { + throw new SecurityException("Managed profile cannot disable status bar"); + } } DevicePolicyData policy = getUserData(userId); return policy.mStatusBarDisabled; @@ -22753,6 +22767,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_AUTOFILL, MANAGE_DEVICE_POLICY_BLUETOOTH, MANAGE_DEVICE_POLICY_CALLS, + MANAGE_DEVICE_POLICY_CAMERA, MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES, MANAGE_DEVICE_POLICY_DISPLAY, MANAGE_DEVICE_POLICY_FACTORY_RESET, @@ -22788,7 +22803,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_ACROSS_USERS, MANAGE_DEVICE_POLICY_AIRPLANE_MODE, MANAGE_DEVICE_POLICY_APPS_CONTROL, - MANAGE_DEVICE_POLICY_CAMERA, MANAGE_DEVICE_POLICY_CERTIFICATES, MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE, MANAGE_DEVICE_POLICY_DEFAULT_SMS, @@ -22816,7 +22830,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final List<String> ADDITIONAL_PROFILE_OWNER_ON_USER_0_PERMISSIONS = List.of( MANAGE_DEVICE_POLICY_AIRPLANE_MODE, - MANAGE_DEVICE_POLICY_CAMERA, MANAGE_DEVICE_POLICY_DISPLAY, MANAGE_DEVICE_POLICY_FUN, MANAGE_DEVICE_POLICY_LOCK_TASK, @@ -22827,7 +22840,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { MANAGE_DEVICE_POLICY_PROFILE_INTERACTION, MANAGE_DEVICE_POLICY_SAFE_BOOT, MANAGE_DEVICE_POLICY_SMS, - MANAGE_DEVICE_POLICY_STATUS_BAR, MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS, MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER, MANAGE_DEVICE_POLICY_USERS, @@ -22848,7 +22860,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * All the additional permissions granted to a Profile Owner on an affiliated user. */ private static final List<String> ADDITIONAL_AFFILIATED_PROFILE_OWNER_ON_USER_PERMISSIONS = - List.of(); + List.of( + MANAGE_DEVICE_POLICY_STATUS_BAR + ); /** * Combination of {@link PROFILE_OWNER_PERMISSIONS} and diff --git a/services/tests/mockingservicestests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java b/services/tests/mockingservicestests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java new file mode 100644 index 000000000000..17fba9f43707 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/display/RefreshRateSettingsUtilsTest.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import static com.android.internal.display.RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +import android.hardware.display.DisplayManager; +import android.provider.Settings; +import android.testing.TestableContext; +import android.view.Display; + +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.display.RefreshRateSettingsUtils; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class RefreshRateSettingsUtilsTest { + + @Rule + public final TestableContext mContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getContext()); + + @Mock + private DisplayManager mDisplayManagerMock; + @Mock + private Display mDisplayMock; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext.addMockSystemService(DisplayManager.class, mDisplayManagerMock); + + Display.Mode[] modes = new Display.Mode[] { + new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600, + /* refreshRate= */ 60), + new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600, + /* refreshRate= */ 120), + new Display.Mode(/* modeId= */ 0, /* width= */ 800, /* height= */ 600, + /* refreshRate= */ 90) + }; + + when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock); + when(mDisplayMock.getSupportedModes()).thenReturn(modes); + } + + @Test + public void testFindHighestRefreshRateForDefaultDisplay() { + when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(null); + assertEquals(DEFAULT_REFRESH_RATE, + RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext), + /* delta= */ 0); + + when(mDisplayManagerMock.getDisplay(Display.DEFAULT_DISPLAY)).thenReturn(mDisplayMock); + assertEquals(120, + RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext), + /* delta= */ 0); + } + + @Test + public void testGetMinRefreshRate() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.FORCE_PEAK_REFRESH_RATE, -1); + assertEquals(0, RefreshRateSettingsUtils.getMinRefreshRate(mContext), /* delta= */ 0); + + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.FORCE_PEAK_REFRESH_RATE, 0); + assertEquals(0, RefreshRateSettingsUtils.getMinRefreshRate(mContext), /* delta= */ 0); + + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.FORCE_PEAK_REFRESH_RATE, 1); + assertEquals(120, RefreshRateSettingsUtils.getMinRefreshRate(mContext), /* delta= */ 0); + } + + @Test + public void testGetPeakRefreshRate() { + float defaultPeakRefreshRate = 100; + + Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, -1); + assertEquals(defaultPeakRefreshRate, + RefreshRateSettingsUtils.getPeakRefreshRate(mContext, defaultPeakRefreshRate), + /* delta= */ 0); + + Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, 0); + assertEquals(DEFAULT_REFRESH_RATE, + RefreshRateSettingsUtils.getPeakRefreshRate(mContext, defaultPeakRefreshRate), + /* delta= */ 0); + + Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, 1); + assertEquals(120, + RefreshRateSettingsUtils.getPeakRefreshRate(mContext, defaultPeakRefreshRate), + /* delta= */ 0); + } +} diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 16aadacb5af6..d85db64b74cd 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -3267,31 +3267,31 @@ public class DevicePolicyManagerTest extends DpmTestBase { reset(getServices().settings); dpm.setMaximumTimeToLock(admin1, 0); - verifyScreenTimeoutCall(null, UserHandle.USER_SYSTEM); + verifyScreenTimeoutCall(null, CALLER_USER_HANDLE); verifyStayOnWhilePluggedCleared(false); reset(getServices().powerManagerInternal); reset(getServices().settings); dpm.setMaximumTimeToLock(admin1, 1); - verifyScreenTimeoutCall(1L, UserHandle.USER_SYSTEM); + verifyScreenTimeoutCall(1L, CALLER_USER_HANDLE); verifyStayOnWhilePluggedCleared(true); reset(getServices().powerManagerInternal); reset(getServices().settings); dpm.setMaximumTimeToLock(admin2, 10); - verifyScreenTimeoutCall(null, UserHandle.USER_SYSTEM); + verifyScreenTimeoutCall(null, CALLER_USER_HANDLE); verifyStayOnWhilePluggedCleared(false); reset(getServices().powerManagerInternal); reset(getServices().settings); dpm.setMaximumTimeToLock(admin1, 5); - verifyScreenTimeoutCall(5L, UserHandle.USER_SYSTEM); + verifyScreenTimeoutCall(5L, CALLER_USER_HANDLE); verifyStayOnWhilePluggedCleared(true); reset(getServices().powerManagerInternal); reset(getServices().settings); dpm.setMaximumTimeToLock(admin2, 4); - verifyScreenTimeoutCall(4L, UserHandle.USER_SYSTEM); + verifyScreenTimeoutCall(4L, CALLER_USER_HANDLE); verifyStayOnWhilePluggedCleared(true); reset(getServices().powerManagerInternal); reset(getServices().settings); @@ -3301,20 +3301,20 @@ public class DevicePolicyManagerTest extends DpmTestBase { reset(getServices().settings); dpm.setMaximumTimeToLock(admin2, Long.MAX_VALUE); - verifyScreenTimeoutCall(Long.MAX_VALUE, UserHandle.USER_SYSTEM); + verifyScreenTimeoutCall(Long.MAX_VALUE, CALLER_USER_HANDLE); verifyStayOnWhilePluggedCleared(true); reset(getServices().powerManagerInternal); reset(getServices().settings); dpm.setMaximumTimeToLock(admin2, 10); - verifyScreenTimeoutCall(10L, UserHandle.USER_SYSTEM); + verifyScreenTimeoutCall(10L, CALLER_USER_HANDLE); verifyStayOnWhilePluggedCleared(true); reset(getServices().powerManagerInternal); reset(getServices().settings); // There's no restriction; should be set to MAX. dpm.setMaximumTimeToLock(admin2, 0); - verifyScreenTimeoutCall(Long.MAX_VALUE, UserHandle.USER_SYSTEM); + verifyScreenTimeoutCall(Long.MAX_VALUE, CALLER_USER_HANDLE); verifyStayOnWhilePluggedCleared(false); } diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java index 3b10db4dceb8..e2a66f03f5ca 100644 --- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java @@ -16,8 +16,6 @@ package com.android.server.display; -import static android.hardware.display.BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE; -import static android.hardware.display.BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL; import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR; import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF; import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT; @@ -29,6 +27,8 @@ import static com.android.server.display.DisplayDeviceConfig.HDR_PERCENT_OF_SCRE import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.anyFloat; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.eq; @@ -39,14 +39,10 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.ContextWrapper; +import android.hardware.display.BrightnessInfo; import android.os.Binder; import android.os.Handler; -import android.os.IThermalEventListener; -import android.os.IThermalService; import android.os.Message; -import android.os.PowerManager; -import android.os.Temperature; -import android.os.Temperature.ThrottlingStatus; import android.os.test.TestLooper; import android.test.mock.MockContentResolver; import android.util.MathUtils; @@ -66,8 +62,6 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -80,7 +74,6 @@ public class HighBrightnessModeControllerTest { private static final long TIME_WINDOW_MILLIS = 55 * 1000; private static final long TIME_ALLOWED_IN_WINDOW_MILLIS = 12 * 1000; private static final long TIME_MINIMUM_AVAILABLE_TO_ENABLE_MILLIS = 5 * 1000; - private static final int THERMAL_STATUS_LIMIT = PowerManager.THERMAL_STATUS_SEVERE; private static final boolean ALLOW_IN_LOW_POWER_MODE = false; private static final float DEFAULT_MIN = 0.01f; @@ -102,17 +95,13 @@ public class HighBrightnessModeControllerTest { @Rule public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); - @Mock IThermalService mThermalServiceMock; @Mock Injector mInjectorMock; @Mock HighBrightnessModeController.HdrBrightnessDeviceConfig mHdrBrightnessDeviceConfigMock; - @Captor ArgumentCaptor<IThermalEventListener> mThermalEventListenerCaptor; - private static final HighBrightnessModeData DEFAULT_HBM_DATA = new HighBrightnessModeData(MINIMUM_LUX, TRANSITION_POINT, TIME_WINDOW_MILLIS, TIME_ALLOWED_IN_WINDOW_MILLIS, TIME_MINIMUM_AVAILABLE_TO_ENABLE_MILLIS, - THERMAL_STATUS_LIMIT, ALLOW_IN_LOW_POWER_MODE, - HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT); + ALLOW_IN_LOW_POWER_MODE, HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT); @Before public void setUp() { @@ -125,8 +114,6 @@ public class HighBrightnessModeControllerTest { mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext())); final MockContentResolver resolver = mSettingsProviderRule.mockContentResolver(mContextSpy); when(mContextSpy.getContentResolver()).thenReturn(resolver); - - when(mInjectorMock.getThermalService()).thenReturn(mThermalServiceMock); } ///////////////// @@ -321,34 +308,14 @@ public class HighBrightnessModeControllerTest { } @Test - public void testNoHbmInHighThermalState() throws Exception { + public void testHbmIsNotTurnedOffInHighThermalState() throws Exception { final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock()); - verify(mThermalServiceMock).registerThermalEventListenerWithType( - mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN)); - final IThermalEventListener listener = mThermalEventListenerCaptor.getValue(); - - // Set the thermal status too high. - listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL)); - - // Try to go into HBM mode but fail - hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); - hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); - advanceTime(10); + // Disabled thermal throttling + hbmc.onBrightnessChanged(/*brightness=*/ 1f, /*unthrottledBrightness*/ 1f, + BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE); - assertEquals(HIGH_BRIGHTNESS_MODE_OFF, hbmc.getHighBrightnessMode()); - } - - @Test - public void testHbmTurnsOffInHighThermalState() throws Exception { - final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock()); - - verify(mThermalServiceMock).registerThermalEventListenerWithType( - mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN)); - final IThermalEventListener listener = mThermalEventListenerCaptor.getValue(); - - // Set the thermal status tolerable - listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_LIGHT)); + assertFalse(hbmc.isThermalThrottlingActive()); // Try to go into HBM mode hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); @@ -357,15 +324,19 @@ public class HighBrightnessModeControllerTest { assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode()); - // Set the thermal status too high and verify we're off. - listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL)); + // Enable thermal throttling + hbmc.onBrightnessChanged(/*brightness=*/ TRANSITION_POINT - 0.01f, + /*unthrottledBrightness*/ 1f, BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL); advanceTime(10); - assertEquals(HIGH_BRIGHTNESS_MODE_OFF, hbmc.getHighBrightnessMode()); + assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode()); + assertTrue(hbmc.isThermalThrottlingActive()); - // Set the thermal status low again and verify we're back on. - listener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_SEVERE)); + // Disabled thermal throttling + hbmc.onBrightnessChanged(/*brightness=*/ 1f, /*unthrottledBrightness*/ 1f, + BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE); advanceTime(1); assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode()); + assertFalse(hbmc.isThermalThrottlingActive()); } @Test @@ -578,33 +549,6 @@ public class HighBrightnessModeControllerTest { anyInt()); } - // Test reporting of thermal throttling when triggered by HighBrightnessModeController's - // internal thermal throttling. - @Test - public void testHbmStats_InternalThermalOff() throws Exception { - final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock()); - final int displayStatsId = mDisplayUniqueId.hashCode(); - - verify(mThermalServiceMock).registerThermalEventListenerWithType( - mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN)); - final IThermalEventListener thermListener = mThermalEventListenerCaptor.getValue(); - - hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); - hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); - hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f); - advanceTime(1); - verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), - eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT), - eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN)); - - thermListener.notifyThrottling(getSkinTemp(Temperature.THROTTLING_CRITICAL)); - advanceTime(10); - assertEquals(HIGH_BRIGHTNESS_MODE_OFF, hbmc.getHighBrightnessMode()); - verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), - eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF), - eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT)); - } - // Test reporting of thermal throttling when triggered externally through // HighBrightnessModeController.onBrightnessChanged() @Test @@ -617,14 +561,16 @@ public class HighBrightnessModeControllerTest { hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); // Brightness is unthrottled, HBM brightness granted - hbmc.onBrightnessChanged(hbmBrightness, hbmBrightness, BRIGHTNESS_MAX_REASON_NONE); + hbmc.onBrightnessChanged(hbmBrightness, hbmBrightness, + BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE); advanceTime(1); verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT), eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN)); // Brightness is thermally throttled, HBM brightness denied (NBM brightness granted) - hbmc.onBrightnessChanged(nbmBrightness, hbmBrightness, BRIGHTNESS_MAX_REASON_THERMAL); + hbmc.onBrightnessChanged(nbmBrightness, hbmBrightness, + BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL); advanceTime(1); // We expect HBM mode to remain set to sunlight, indicating that HBMC *allows* this mode. // However, we expect the HBM state reported by HBMC to be off, since external thermal @@ -784,11 +730,7 @@ public class HighBrightnessModeControllerTest { mTestLooper.dispatchAll(); } - private Temperature getSkinTemp(@ThrottlingStatus int status) { - return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status); - } - private void hbmcOnBrightnessChanged(HighBrightnessModeController hbmc, float brightness) { - hbmc.onBrightnessChanged(brightness, brightness, BRIGHTNESS_MAX_REASON_NONE); + hbmc.onBrightnessChanged(brightness, brightness, BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE); } } diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java index ff89be75c5d4..5ea30298890f 100644 --- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java +++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java @@ -17,6 +17,8 @@ package com.android.server.display; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.any; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; @@ -26,6 +28,7 @@ import static org.mockito.Mockito.when; import android.app.PropertyInvalidatedCache; import android.graphics.Point; +import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; import android.view.Surface; @@ -47,6 +50,7 @@ public class LogicalDisplayTest { private static final int LAYER_STACK = 0; private static final int DISPLAY_WIDTH = 100; private static final int DISPLAY_HEIGHT = 200; + private static final int MODE_ID = 1; private LogicalDisplay mLogicalDisplay; private DisplayDevice mDisplayDevice; @@ -65,6 +69,9 @@ public class LogicalDisplayTest { mDisplayDeviceInfo.height = DISPLAY_HEIGHT; mDisplayDeviceInfo.flags = DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL; + mDisplayDeviceInfo.modeId = MODE_ID; + mDisplayDeviceInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID, + DISPLAY_WIDTH, DISPLAY_HEIGHT, /* refreshRate= */ 60)}; when(mDisplayDevice.getDisplayDeviceInfoLocked()).thenReturn(mDisplayDeviceInfo); // Disable binder caches in this process. @@ -168,14 +175,34 @@ public class LogicalDisplayTest { } @Test - public void testLayoutLimitedRefreshRateNotClearedAfterUpdate() { - SurfaceControl.RefreshRateRange refreshRateRange = new SurfaceControl.RefreshRateRange(1, - 2); - mLogicalDisplay.updateLayoutLimitedRefreshRateLocked(refreshRateRange); - mLogicalDisplay.updateDisplayGroupIdLocked(1); + public void testUpdateLayoutLimitedRefreshRate() { + SurfaceControl.RefreshRateRange layoutLimitedRefreshRate = + new SurfaceControl.RefreshRateRange(0, 120); + DisplayInfo info1 = mLogicalDisplay.getDisplayInfoLocked(); + mLogicalDisplay.updateLayoutLimitedRefreshRateLocked(layoutLimitedRefreshRate); + DisplayInfo info2 = mLogicalDisplay.getDisplayInfoLocked(); + // Display info should only be updated when updateLocked is called + assertEquals(info2, info1); - DisplayInfo result = mLogicalDisplay.getDisplayInfoLocked(); + mLogicalDisplay.updateLocked(mDeviceRepo); + DisplayInfo info3 = mLogicalDisplay.getDisplayInfoLocked(); + assertNotEquals(info3, info2); + assertEquals(layoutLimitedRefreshRate, info3.layoutLimitedRefreshRate); + } - assertEquals(refreshRateRange, result.layoutLimitedRefreshRate); + @Test + public void testUpdateRefreshRateThermalThrottling() { + SparseArray<SurfaceControl.RefreshRateRange> refreshRanges = new SparseArray<>(); + refreshRanges.put(0, new SurfaceControl.RefreshRateRange(0, 120)); + DisplayInfo info1 = mLogicalDisplay.getDisplayInfoLocked(); + mLogicalDisplay.updateThermalRefreshRateThrottling(refreshRanges); + DisplayInfo info2 = mLogicalDisplay.getDisplayInfoLocked(); + // Display info should only be updated when updateLocked is called + assertEquals(info2, info1); + + mLogicalDisplay.updateLocked(mDeviceRepo); + DisplayInfo info3 = mLogicalDisplay.getDisplayInfoLocked(); + assertNotEquals(info3, info2); + assertTrue(refreshRanges.contentEquals(info3.thermalRefreshRateThrottling)); } } diff --git a/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java index 6907145542cc..4cfcee5005b4 100644 --- a/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java @@ -84,6 +84,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.internal.display.BrightnessSynchronizer; +import com.android.internal.display.RefreshRateSettingsUtils; import com.android.internal.os.BackgroundThread; import com.android.internal.util.Preconditions; import com.android.internal.util.test.FakeSettingsProvider; @@ -126,7 +127,8 @@ public class DisplayModeDirectorTest { private static final String TAG = "DisplayModeDirectorTest"; private static final boolean DEBUG = false; private static final float FLOAT_TOLERANCE = 0.01f; - private static final int DISPLAY_ID = 0; + private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY; + private static final int MODE_ID = 1; private static final float TRANSITION_POINT = 0.763f; private static final float HBM_TRANSITION_POINT_INVALID = Float.POSITIVE_INFINITY; @@ -158,6 +160,9 @@ public class DisplayModeDirectorTest { LocalServices.addService(SensorManagerInternal.class, mSensorManagerInternalMock); LocalServices.removeServiceForTest(DisplayManagerInternal.class); LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock); + + clearSmoothDisplaySetting(); + clearForcePeakRefreshRateSetting(); } private DisplayModeDirector createDirectorFromRefreshRateArray( @@ -919,7 +924,6 @@ public class DisplayModeDirectorTest { public void testLockFpsForLowZone() throws Exception { DisplayModeDirector director = createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); - setPeakRefreshRate(90); director.getSettingsObserver().setDefaultRefreshRate(90); director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); @@ -927,6 +931,7 @@ public class DisplayModeDirectorTest { config.setRefreshRateInLowZone(90); config.setLowDisplayBrightnessThresholds(new int[] { 10 }); config.setLowAmbientBrightnessThresholds(new int[] { 20 }); + config.setDefaultPeakRefreshRate(90); Sensor lightSensor = createLightSensor(); SensorManager sensorManager = createMockSensorManager(lightSensor); @@ -977,7 +982,6 @@ public class DisplayModeDirectorTest { public void testLockFpsForHighZone() throws Exception { DisplayModeDirector director = createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); - setPeakRefreshRate(90 /*fps*/); director.getSettingsObserver().setDefaultRefreshRate(90); director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); @@ -985,6 +989,7 @@ public class DisplayModeDirectorTest { config.setRefreshRateInHighZone(60); config.setHighDisplayBrightnessThresholds(new int[] { 255 }); config.setHighAmbientBrightnessThresholds(new int[] { 8000 }); + config.setDefaultPeakRefreshRate(90); Sensor lightSensor = createLightSensor(); SensorManager sensorManager = createMockSensorManager(lightSensor); @@ -1032,16 +1037,123 @@ public class DisplayModeDirectorTest { } @Test + public void testSmoothDisplay() { + float defaultRefreshRate = 60; + int defaultPeakRefreshRate = 100; + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + director.getSettingsObserver().setDefaultRefreshRate(defaultRefreshRate); + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); + + final FakeDeviceConfig config = mInjector.getDeviceConfig(); + config.setDefaultPeakRefreshRate(defaultPeakRefreshRate); + + Sensor lightSensor = createLightSensor(); + SensorManager sensorManager = createMockSensorManager(lightSensor); + director.start(sensorManager); + + // Default value of the setting + + Vote vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ + defaultPeakRefreshRate); + vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ + Float.POSITIVE_INFINITY); + vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ + defaultRefreshRate); + + setSmoothDisplayEnabled(false); + + vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ + RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE); + vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ + Float.POSITIVE_INFINITY); + vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ + defaultRefreshRate); + + setSmoothDisplayEnabled(true); + + vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ + RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext)); + vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ + Float.POSITIVE_INFINITY); + vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ + defaultRefreshRate); + } + + @Test + public void testForcePeakRefreshRate() { + float defaultRefreshRate = 60; + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + director.getSettingsObserver().setDefaultRefreshRate(defaultRefreshRate); + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); + + Sensor lightSensor = createLightSensor(); + SensorManager sensorManager = createMockSensorManager(lightSensor); + director.start(sensorManager); + + setForcePeakRefreshRateEnabled(false); + setSmoothDisplayEnabled(false); + + Vote vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ + RefreshRateSettingsUtils.DEFAULT_REFRESH_RATE); + vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, + /* frameRateHigh= */ Float.POSITIVE_INFINITY); + vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ + defaultRefreshRate); + + setForcePeakRefreshRateEnabled(true); + + vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ + RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext)); + vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ + RefreshRateSettingsUtils.findHighestRefreshRateForDefaultDisplay(mContext), + /* frameRateHigh= */ Float.POSITIVE_INFINITY); + vote = director.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_DEFAULT_RENDER_FRAME_RATE); + assertVoteForRenderFrameRateRange(vote, /* frameRateLow= */ 0, /* frameRateHigh= */ + defaultRefreshRate); + } + + @Test public void testSensorRegistration() { // First, configure brightness zones or DMD won't register for sensor data. final FakeDeviceConfig config = mInjector.getDeviceConfig(); config.setRefreshRateInHighZone(60); config.setHighDisplayBrightnessThresholds(new int[] { 255 }); config.setHighAmbientBrightnessThresholds(new int[] { 8000 }); + config.setDefaultPeakRefreshRate(90); DisplayModeDirector director = createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); - setPeakRefreshRate(90 /*fps*/); director.getSettingsObserver().setDefaultRefreshRate(90); director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); @@ -2417,10 +2529,10 @@ public class DisplayModeDirectorTest { config.setRefreshRateInHighZone(60); config.setHighDisplayBrightnessThresholds(new int[] { 255 }); config.setHighAmbientBrightnessThresholds(new int[] { 8000 }); + config.setDefaultPeakRefreshRate(90); DisplayModeDirector director = createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); - setPeakRefreshRate(90 /*fps*/); director.getSettingsObserver().setDefaultRefreshRate(90); director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); @@ -2533,6 +2645,33 @@ public class DisplayModeDirectorTest { assertNull(vote); } + @Test + public void testUpdateLayoutLimitedRefreshRate() { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0); + director.start(createMockSensorManager()); + + ArgumentCaptor<DisplayListener> displayListenerCaptor = + ArgumentCaptor.forClass(DisplayListener.class); + verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(), + any(Handler.class)); + DisplayListener displayListener = displayListenerCaptor.getValue(); + + float refreshRate = 60; + mInjector.mDisplayInfo.layoutLimitedRefreshRate = + new RefreshRateRange(refreshRate, refreshRate); + displayListener.onDisplayChanged(DISPLAY_ID); + + Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE); + assertVoteForPhysicalRefreshRate(vote, /* refreshRate= */ refreshRate); + + mInjector.mDisplayInfo.layoutLimitedRefreshRate = null; + displayListener.onDisplayChanged(DISPLAY_ID); + + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_LAYOUT_LIMITED_FRAME_RATE); + assertNull(vote); + } + private Temperature getSkinTemp(@Temperature.ThrottlingStatus int status) { return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status); } @@ -2671,10 +2810,30 @@ public class DisplayModeDirectorTest { listener.onDisplayChanged(DISPLAY_ID); } - private void setPeakRefreshRate(float fps) { - Settings.System.putFloat(mContext.getContentResolver(), Settings.System.PEAK_REFRESH_RATE, - fps); - mInjector.notifyPeakRefreshRateChanged(); + private void setSmoothDisplayEnabled(boolean enabled) { + Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, + enabled ? 1 : 0); + mInjector.notifySmoothDisplaySettingChanged(); + waitForIdleSync(); + } + + private void clearSmoothDisplaySetting() { + Settings.System.putInt(mContext.getContentResolver(), Settings.System.SMOOTH_DISPLAY, -1); + mInjector.notifySmoothDisplaySettingChanged(); + waitForIdleSync(); + } + + private void setForcePeakRefreshRateEnabled(boolean enabled) { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.FORCE_PEAK_REFRESH_RATE, enabled ? 1 : 0); + mInjector.notifyForcePeakRefreshRateSettingChanged(); + waitForIdleSync(); + } + + private void clearForcePeakRefreshRateSetting() { + Settings.System.putInt(mContext.getContentResolver(), + Settings.System.FORCE_PEAK_REFRESH_RATE, -1); + mInjector.notifySmoothDisplaySettingChanged(); waitForIdleSync(); } @@ -2719,11 +2878,19 @@ public class DisplayModeDirectorTest { public static class FakesInjector implements DisplayModeDirector.Injector { private final FakeDeviceConfig mDeviceConfig; + private final DisplayInfo mDisplayInfo; + private final Display mDisplay; private ContentObserver mBrightnessObserver; - private ContentObserver mPeakRefreshRateObserver; + private ContentObserver mSmoothDisplaySettingObserver; + private ContentObserver mForcePeakRefreshRateSettingObserver; FakesInjector() { mDeviceConfig = new FakeDeviceConfig(); + mDisplayInfo = new DisplayInfo(); + mDisplayInfo.defaultModeId = MODE_ID; + mDisplayInfo.supportedModes = new Display.Mode[] {new Display.Mode(MODE_ID, + 800, 600, /* refreshRate= */ 60)}; + mDisplay = createDisplay(DISPLAY_ID); } @NonNull @@ -2732,22 +2899,37 @@ public class DisplayModeDirectorTest { } @Override - public void registerPeakRefreshRateObserver(@NonNull ContentResolver cr, + public void registerSmoothDisplayObserver(@NonNull ContentResolver cr, + @NonNull ContentObserver observer) { + mSmoothDisplaySettingObserver = observer; + } + + @Override + public void registerForcePeakRefreshRateObserver(@NonNull ContentResolver cr, @NonNull ContentObserver observer) { - mPeakRefreshRateObserver = observer; + mForcePeakRefreshRateSettingObserver = observer; } @Override + public void registerDisplayListener(DisplayListener listener, Handler handler) {} + + @Override public void registerDisplayListener(DisplayListener listener, Handler handler, long flag) {} @Override + public Display getDisplay(int displayId) { + return mDisplay; + } + + @Override public Display[] getDisplays() { - return new Display[] { createDisplay(DISPLAY_ID) }; + return new Display[] { mDisplay }; } @Override public boolean getDisplayInfo(int displayId, DisplayInfo displayInfo) { - return false; + displayInfo.copyFrom(mDisplayInfo); + return true; } @Override @@ -2771,14 +2953,21 @@ public class DisplayModeDirectorTest { } protected Display createDisplay(int id) { - return new Display(DisplayManagerGlobal.getInstance(), id, new DisplayInfo(), + return new Display(DisplayManagerGlobal.getInstance(), id, mDisplayInfo, ApplicationProvider.getApplicationContext().getResources()); } - void notifyPeakRefreshRateChanged() { - if (mPeakRefreshRateObserver != null) { - mPeakRefreshRateObserver.dispatchChange(false /*selfChange*/, - PEAK_REFRESH_RATE_URI); + void notifySmoothDisplaySettingChanged() { + if (mSmoothDisplaySettingObserver != null) { + mSmoothDisplaySettingObserver.dispatchChange(false /*selfChange*/, + SMOOTH_DISPLAY_URI); + } + } + + void notifyForcePeakRefreshRateSettingChanged() { + if (mForcePeakRefreshRateSettingObserver != null) { + mForcePeakRefreshRateSettingObserver.dispatchChange(false /*selfChange*/, + FORCE_PEAK_REFRESH_RATE_URI); } } } diff --git a/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java b/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java index fd1889ca0982..13540d60b930 100644 --- a/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java +++ b/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java @@ -253,7 +253,7 @@ public class SkinThermalStatusObserverTest { public boolean getDisplayInfo(int displayId, DisplayInfo displayInfo) { SparseArray<SurfaceControl.RefreshRateRange> config = mOverriddenConfig.get(displayId); if (config != null) { - displayInfo.refreshRateThermalThrottling = config; + displayInfo.thermalRefreshRateThrottling = config; return true; } return false; 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 94f52bbc6021..5dbc6ab4a6d0 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -40,7 +40,6 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MAX; import static android.app.NotificationManager.IMPORTANCE_NONE; -import static android.app.NotificationManager.IMPORTANCE_UNSPECIFIED; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS; import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; @@ -5312,7 +5311,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { parser.setInput(new BufferedInputStream( new ByteArrayInputStream(baos.toByteArray())), null); NotificationChannel restored = new NotificationChannel("a", "ab", IMPORTANCE_DEFAULT); - restored.populateFromXmlForRestore(parser, getContext()); + restored.populateFromXmlForRestore(parser, true, getContext()); assertNull(restored.getSound()); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index f6d10b9f371c..c78b03e4b5cb 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -140,6 +140,7 @@ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; @@ -170,6 +171,12 @@ public class PreferencesHelperTest extends UiServiceTestCase { Uri.parse("content://" + TEST_AUTHORITY + "/internal/audio/media/10?title=Test&canonical=1"); + private static final Uri ANDROID_RES_SOUND_URI = + Uri.parse("android.resource://" + TEST_AUTHORITY + "/raw/test"); + + private static final Uri FILE_SOUND_URI = + Uri.parse("file://" + TEST_AUTHORITY + "/product/media/test.ogg"); + @Mock PermissionHelper mPermissionHelper; @Mock RankingHandler mHandler; @Mock PackageManager mPm; @@ -1338,6 +1345,57 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound()); } + /** + * Test sound Uri restore retry behavior when channel is restored before package + * and then package is installed. + */ + @Test + public void testRestoreXml_withNonExistentCanonicalizedSoundUriAndMissingPackage() + throws Exception { + // canonicalization returns CANONICAL_SOUND_URI for getSoundForBackup (backup part) + doReturn(CANONICAL_SOUND_URI) + .when(mTestIContentProvider).canonicalize(any(), eq(SOUND_URI)); + + NotificationChannel channel = + new NotificationChannel("id", "name", IMPORTANCE_LOW); + channel.setSound(SOUND_URI, mAudioAttributes); + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, + USER_SYSTEM, channel.getId()); + + // canonicalization / uncanonicalization returns null for the restore part + doReturn(null) + .when(mTestIContentProvider).canonicalize(any(), eq(CANONICAL_SOUND_URI)); + doReturn(null) + .when(mTestIContentProvider).uncanonicalize(any(), any()); + + // simulate package not installed + when(mPm.getPackageUidAsUser(PKG_N_MR1, USER_SYSTEM)).thenReturn(UNKNOWN_UID); + when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenThrow( + new PackageManager.NameNotFoundException()); + + loadStreamXml(baos, true, USER_SYSTEM); + + // 1st restore pass fails + NotificationChannel actualChannel = mHelper.getNotificationChannel( + PKG_N_MR1, UNKNOWN_UID, channel.getId(), false); + // sound is CANONICAL_SOUND_URI, unchanged from backup + assertEquals(CANONICAL_SOUND_URI, actualChannel.getSound()); + // sound is flagged as not restored + assertFalse(actualChannel.isSoundRestored()); + + // package is "installed" + when(mPm.getPackageUidAsUser(PKG_N_MR1, USER_SYSTEM)).thenReturn(UID_N_MR1); + + // Trigger 2nd restore pass + mHelper.onPackagesChanged(false, USER_SYSTEM, new String[]{PKG_N_MR1}, + new int[]{UID_N_MR1}); + + // sound is flagged as restored and set to default URI + assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound()); + assertTrue(actualChannel.isSoundRestored()); + } + /** * Although we don't make backups with uncanonicalized uris anymore, we used to, so we have to @@ -1363,7 +1421,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { backupWithUncanonicalizedSoundUri.getBytes(), true, USER_SYSTEM); NotificationChannel actualChannel = mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, id, false); + assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, actualChannel.getSound()); + assertTrue(actualChannel.isSoundRestored()); } @Test @@ -1389,6 +1449,73 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test + public void testBackupRestoreXml_withAndroidResourceSoundUri() throws Exception { + // Mock ContentResolver.getResourceId: + // throw exception on restore 1st pass => simulate app not installed yet + // then return a valid resource on package update => sim. app installed + ContentResolver contentResolver = mock(ContentResolver.class); + when(mContext.getContentResolver()).thenReturn(contentResolver); + ContentResolver.OpenResourceIdResult resId = mock( + ContentResolver.OpenResourceIdResult.class); + when(contentResolver.getResourceId(ANDROID_RES_SOUND_URI)).thenReturn(resId).thenThrow( + new FileNotFoundException("")).thenReturn(resId); + + mHelper = new PreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper, + mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory, false); + + NotificationChannel channel = + new NotificationChannel("id", "name", IMPORTANCE_LOW); + channel.setSound(ANDROID_RES_SOUND_URI, mAudioAttributes); + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, + USER_SYSTEM, channel.getId()); + + // simulate package not installed + when(mPm.getPackageUidAsUser(PKG_N_MR1, USER_SYSTEM)).thenReturn(UNKNOWN_UID); + when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenThrow( + new PackageManager.NameNotFoundException()); + + loadStreamXml(baos, true, USER_SYSTEM); + + NotificationChannel actualChannel = mHelper.getNotificationChannel( + PKG_N_MR1, UNKNOWN_UID, channel.getId(), false); + // sound is ANDROID_RES_SOUND_URI, unchanged from backup + assertEquals(ANDROID_RES_SOUND_URI, actualChannel.getSound()); + // sound is flagged as not restored + assertFalse(actualChannel.isSoundRestored()); + + // package is "installed" + when(mPm.getPackageUidAsUser(PKG_N_MR1, USER_SYSTEM)).thenReturn(UID_N_MR1); + + // Trigger 2nd restore pass + mHelper.onPackagesChanged(false, USER_SYSTEM, new String[]{PKG_N_MR1}, + new int[]{UID_N_MR1}); + + // sound is flagged as restored + assertEquals(ANDROID_RES_SOUND_URI, actualChannel.getSound()); + assertTrue(actualChannel.isSoundRestored()); + } + + @Test + public void testBackupRestoreXml_withFileResourceSoundUri() throws Exception { + NotificationChannel channel = + new NotificationChannel("id", "name", IMPORTANCE_LOW); + channel.setSound(FILE_SOUND_URI, mAudioAttributes); + mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false); + ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, + USER_SYSTEM, channel.getId()); + + loadStreamXml(baos, true, USER_SYSTEM); + + NotificationChannel actualChannel = mHelper.getNotificationChannel( + PKG_N_MR1, UID_N_MR1, channel.getId(), false); + // sound is FILE_SOUND_URI, unchanged from backup + assertEquals(FILE_SOUND_URI, actualChannel.getSound()); + // sound is flagged as restored + assertTrue(actualChannel.isSoundRestored()); + } + + @Test public void testChannelXml_backup() throws Exception { NotificationChannelGroup ncg = new NotificationChannelGroup("1", "bye"); NotificationChannelGroup ncg2 = new NotificationChannelGroup("2", "hello"); |