diff options
| author | 2023-09-09 00:16:03 +0000 | |
|---|---|---|
| committer | 2023-09-09 00:16:03 +0000 | |
| commit | ad32bfe36acd013165e62d8fd4731c2dcf15da09 (patch) | |
| tree | a803c86dbc848d993261552adce4bcaf292c086d | |
| parent | 39b23df74af26d088e970d0112c058c6f4c7019b (diff) | |
| parent | f2bcb596c256ac9ea35cea205b3619defb73ffd0 (diff) | |
Merge "Add CompatScaleProvider interface" into main
6 files changed, 482 insertions, 35 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 39589fa93149..f28b4b4eb207 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -36,7 +36,6 @@ import static android.view.Display.INVALID_DISPLAY; import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded; import static android.window.ConfigurationHelper.isDifferentDisplay; import static android.window.ConfigurationHelper.shouldUpdateResources; - import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import static com.android.internal.os.SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL; @@ -1286,8 +1285,13 @@ public final class ActivityThread extends ClientTransactionHandler } private void updateCompatOverrideScale(CompatibilityInfo info) { - CompatibilityInfo.setOverrideInvertedScale( - info.hasOverrideScaling() ? info.applicationInvertedScale : 1f); + if (info.hasOverrideScaling()) { + CompatibilityInfo.setOverrideInvertedScale(info.applicationInvertedScale, + info.applicationDensityInvertedScale); + } else { + CompatibilityInfo.setOverrideInvertedScale(/* invertScale */ 1f, + /* densityInvertScale */1f); + } } public final void runIsolatedEntryPoint(String entryPoint, String[] entryPointArgs) { diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java index 08ba5b6c268c..f929c1f03c87 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -100,7 +100,7 @@ public class CompatibilityInfo implements Parcelable { * The effective screen density we have selected for this application. */ public final int applicationDensity; - + /** * Application's scale. */ @@ -112,9 +112,27 @@ public class CompatibilityInfo implements Parcelable { */ public final float applicationInvertedScale; + /** + * Application's density scale. + * + * <p>In most cases this is equal to {@link #applicationScale}, but in some cases e.g. + * Automotive the requirement is to just scale the density and keep the resolution the same. + * This is used for artificially making apps look zoomed in to compensate for the user distance + * from the screen. + */ + public final float applicationDensityScale; + + /** + * Application's density inverted scale. + */ + public final float applicationDensityInvertedScale; + /** The process level override inverted scale. See {@link #HAS_OVERRIDE_SCALING}. */ private static float sOverrideInvertedScale = 1f; + /** The process level override inverted density scale. See {@link #HAS_OVERRIDE_SCALING}. */ + private static float sOverrideDensityInvertScale = 1f; + @UnsupportedAppUsage @Deprecated public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw, @@ -123,17 +141,24 @@ public class CompatibilityInfo implements Parcelable { } public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw, - boolean forceCompat, float overrideScale) { + boolean forceCompat, float scaleFactor) { + this(appInfo, screenLayout, sw, forceCompat, scaleFactor, scaleFactor); + } + + public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, int sw, + boolean forceCompat, float scaleFactor, float densityScaleFactor) { int compatFlags = 0; if (appInfo.targetSdkVersion < VERSION_CODES.O) { compatFlags |= NEEDS_COMPAT_RES; } - if (overrideScale != 1.0f) { - applicationScale = overrideScale; - applicationInvertedScale = 1.0f / overrideScale; + if (scaleFactor != 1f || densityScaleFactor != 1f) { + applicationScale = scaleFactor; + applicationInvertedScale = 1f / scaleFactor; + applicationDensityScale = densityScaleFactor; + applicationDensityInvertedScale = 1f / densityScaleFactor; applicationDensity = (int) ((DisplayMetrics.DENSITY_DEVICE_STABLE - * applicationInvertedScale) + .5f); + * applicationDensityInvertedScale) + .5f); mCompatibilityFlags = NEVER_NEEDS_COMPAT | HAS_OVERRIDE_SCALING; // Override scale has the highest priority. So ignore other compatibility attributes. return; @@ -181,7 +206,8 @@ public class CompatibilityInfo implements Parcelable { applicationDensity = DisplayMetrics.DENSITY_DEVICE; applicationScale = 1.0f; applicationInvertedScale = 1.0f; - + applicationDensityScale = 1.0f; + applicationDensityInvertedScale = 1.0f; } else { /** * Has the application said that its UI is expandable? Based on the @@ -271,11 +297,16 @@ public class CompatibilityInfo implements Parcelable { applicationDensity = DisplayMetrics.DENSITY_DEVICE; applicationScale = 1.0f; applicationInvertedScale = 1.0f; + applicationDensityScale = 1.0f; + applicationDensityInvertedScale = 1.0f; } else { applicationDensity = DisplayMetrics.DENSITY_DEFAULT; applicationScale = DisplayMetrics.DENSITY_DEVICE / (float) DisplayMetrics.DENSITY_DEFAULT; applicationInvertedScale = 1.0f / applicationScale; + applicationDensityScale = DisplayMetrics.DENSITY_DEVICE + / (float) DisplayMetrics.DENSITY_DEFAULT; + applicationDensityInvertedScale = 1f / applicationDensityScale; compatFlags |= SCALING_REQUIRED; } } @@ -289,6 +320,8 @@ public class CompatibilityInfo implements Parcelable { applicationDensity = dens; applicationScale = scale; applicationInvertedScale = invertedScale; + applicationDensityScale = (float) DisplayMetrics.DENSITY_DEVICE_STABLE / dens; + applicationDensityInvertedScale = 1f / applicationDensityScale; } @UnsupportedAppUsage @@ -528,7 +561,8 @@ public class CompatibilityInfo implements Parcelable { /** Applies the compatibility adjustment to the display metrics. */ public void applyDisplayMetricsIfNeeded(DisplayMetrics inoutDm, boolean applyToSize) { if (hasOverrideScale()) { - scaleDisplayMetrics(sOverrideInvertedScale, inoutDm, applyToSize); + scaleDisplayMetrics(sOverrideInvertedScale, sOverrideDensityInvertScale, inoutDm, + applyToSize); return; } if (!equals(DEFAULT_COMPATIBILITY_INFO)) { @@ -548,15 +582,17 @@ public class CompatibilityInfo implements Parcelable { } if (isScalingRequired()) { - scaleDisplayMetrics(applicationInvertedScale, inoutDm, true /* applyToSize */); + scaleDisplayMetrics(applicationInvertedScale, applicationDensityInvertedScale, inoutDm, + true /* applyToSize */); } } /** Scales the density of the given display metrics. */ - private static void scaleDisplayMetrics(float invertedRatio, DisplayMetrics inoutDm, - boolean applyToSize) { - inoutDm.density = inoutDm.noncompatDensity * invertedRatio; - inoutDm.densityDpi = (int) ((inoutDm.noncompatDensityDpi * invertedRatio) + .5f); + private static void scaleDisplayMetrics(float invertScale, float densityInvertScale, + DisplayMetrics inoutDm, boolean applyToSize) { + inoutDm.density = inoutDm.noncompatDensity * densityInvertScale; + inoutDm.densityDpi = (int) ((inoutDm.noncompatDensityDpi + * densityInvertScale) + .5f); // Note: since this is changing the scaledDensity, you might think we also need to change // inoutDm.fontScaleConverter to accurately calculate non-linear font scaling. But we're not // going to do that, for a couple of reasons (see b/265695259 for details): @@ -570,12 +606,12 @@ public class CompatibilityInfo implements Parcelable { // b. Sometime later by WindowManager in onResume or other windowing events. In this case // the DisplayMetrics object is never used by the app/resources, so it's ok if // fontScaleConverter is null because it's not being used to scale fonts anyway. - inoutDm.scaledDensity = inoutDm.noncompatScaledDensity * invertedRatio; - inoutDm.xdpi = inoutDm.noncompatXdpi * invertedRatio; - inoutDm.ydpi = inoutDm.noncompatYdpi * invertedRatio; + inoutDm.scaledDensity = inoutDm.noncompatScaledDensity * densityInvertScale; + inoutDm.xdpi = inoutDm.noncompatXdpi * densityInvertScale; + inoutDm.ydpi = inoutDm.noncompatYdpi * densityInvertScale; if (applyToSize) { - inoutDm.widthPixels = (int) (inoutDm.widthPixels * invertedRatio + 0.5f); - inoutDm.heightPixels = (int) (inoutDm.heightPixels * invertedRatio + 0.5f); + inoutDm.widthPixels = (int) (inoutDm.widthPixels * invertScale + 0.5f); + inoutDm.heightPixels = (int) (inoutDm.heightPixels * invertScale + 0.5f); } } @@ -594,38 +630,55 @@ public class CompatibilityInfo implements Parcelable { } inoutConfig.densityDpi = displayDensity; if (isScalingRequired()) { - scaleConfiguration(applicationInvertedScale, inoutConfig); + scaleConfiguration(applicationInvertedScale, applicationDensityInvertedScale, + inoutConfig); } } /** Scales the density and bounds of the given configuration. */ - public static void scaleConfiguration(float invertedRatio, Configuration inoutConfig) { - inoutConfig.densityDpi = (int) ((inoutConfig.densityDpi * invertedRatio) + .5f); - inoutConfig.windowConfiguration.scale(invertedRatio); + public static void scaleConfiguration(float invertScale, Configuration inoutConfig) { + scaleConfiguration(invertScale, invertScale, inoutConfig); + } + + /** Scales the density and bounds of the given configuration. */ + public static void scaleConfiguration(float invertScale, float densityInvertScale, + Configuration inoutConfig) { + inoutConfig.densityDpi = (int) ((inoutConfig.densityDpi + * densityInvertScale) + .5f); + inoutConfig.windowConfiguration.scale(invertScale); } /** @see #sOverrideInvertedScale */ public static void applyOverrideScaleIfNeeded(Configuration config) { if (!hasOverrideScale()) return; - scaleConfiguration(sOverrideInvertedScale, config); + scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale, config); } /** @see #sOverrideInvertedScale */ public static void applyOverrideScaleIfNeeded(MergedConfiguration mergedConfig) { if (!hasOverrideScale()) return; - scaleConfiguration(sOverrideInvertedScale, mergedConfig.getGlobalConfiguration()); - scaleConfiguration(sOverrideInvertedScale, mergedConfig.getOverrideConfiguration()); - scaleConfiguration(sOverrideInvertedScale, mergedConfig.getMergedConfiguration()); + scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale, + mergedConfig.getGlobalConfiguration()); + scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale, + mergedConfig.getOverrideConfiguration()); + scaleConfiguration(sOverrideInvertedScale, sOverrideDensityInvertScale, + mergedConfig.getMergedConfiguration()); } /** Returns {@code true} if this process is in a environment with override scale. */ private static boolean hasOverrideScale() { - return sOverrideInvertedScale != 1f; + return sOverrideInvertedScale != 1f || sOverrideDensityInvertScale != 1f; } /** @see #sOverrideInvertedScale */ - public static void setOverrideInvertedScale(float invertedRatio) { - sOverrideInvertedScale = invertedRatio; + public static void setOverrideInvertedScale(float invertScale) { + setOverrideInvertedScale(invertScale, invertScale); + } + + /** @see #sOverrideInvertedScale */ + public static void setOverrideInvertedScale(float invertScale, float densityInvertScale) { + sOverrideInvertedScale = invertScale; + sOverrideDensityInvertScale = densityInvertScale; } /** @see #sOverrideInvertedScale */ @@ -633,6 +686,11 @@ public class CompatibilityInfo implements Parcelable { return sOverrideInvertedScale; } + /** @see #sOverrideDensityInvertScale */ + public static float getOverrideDensityInvertedScale() { + return sOverrideDensityInvertScale; + } + /** * Compute the frame Rect for applications runs under compatibility mode. * @@ -693,6 +751,8 @@ public class CompatibilityInfo implements Parcelable { if (applicationDensity != oc.applicationDensity) return false; if (applicationScale != oc.applicationScale) return false; if (applicationInvertedScale != oc.applicationInvertedScale) return false; + if (applicationDensityScale != oc.applicationDensityScale) return false; + if (applicationDensityInvertedScale != oc.applicationDensityInvertedScale) return false; return true; } catch (ClassCastException e) { return false; @@ -713,6 +773,8 @@ public class CompatibilityInfo implements Parcelable { if (hasOverrideScaling()) { sb.append(" overrideInvScale="); sb.append(applicationInvertedScale); + sb.append(" overrideDensityInvScale="); + sb.append(applicationDensityInvertedScale); } if (!supportsScreen()) { sb.append(" resizing"); @@ -734,6 +796,8 @@ public class CompatibilityInfo implements Parcelable { result = 31 * result + applicationDensity; result = 31 * result + Float.floatToIntBits(applicationScale); result = 31 * result + Float.floatToIntBits(applicationInvertedScale); + result = 31 * result + Float.floatToIntBits(applicationDensityScale); + result = 31 * result + Float.floatToIntBits(applicationDensityInvertedScale); return result; } @@ -748,6 +812,8 @@ public class CompatibilityInfo implements Parcelable { dest.writeInt(applicationDensity); dest.writeFloat(applicationScale); dest.writeFloat(applicationInvertedScale); + dest.writeFloat(applicationDensityScale); + dest.writeFloat(applicationDensityInvertedScale); } @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @@ -769,5 +835,61 @@ public class CompatibilityInfo implements Parcelable { applicationDensity = source.readInt(); applicationScale = source.readFloat(); applicationInvertedScale = source.readFloat(); + applicationDensityScale = source.readFloat(); + applicationDensityInvertedScale = source.readFloat(); + } + + /** + * A data class for holding scale factor for width, height, and density. + */ + public static final class CompatScale { + + public final float mScaleFactor; + public final float mDensityScaleFactor; + + public CompatScale(float scaleFactor) { + this(scaleFactor, scaleFactor); + } + + public CompatScale(float scaleFactor, float densityScaleFactor) { + mScaleFactor = scaleFactor; + mDensityScaleFactor = densityScaleFactor; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) { + return true; + } + if (!(o instanceof CompatScale)) { + return false; + } + try { + CompatScale oc = (CompatScale) o; + if (mScaleFactor != oc.mScaleFactor) return false; + if (mDensityScaleFactor != oc.mDensityScaleFactor) return false; + return true; + } catch (ClassCastException e) { + return false; + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(128); + sb.append("mScaleFactor= "); + sb.append(mScaleFactor); + sb.append(" mDensityScaleFactor= "); + sb.append(mDensityScaleFactor); + return sb.toString(); + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + Float.floatToIntBits(mScaleFactor); + result = 31 * result + Float.floatToIntBits(mDensityScaleFactor); + return result; + } } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 1ef552f670f4..4aea70c6dd64 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -68,7 +68,6 @@ import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_PIP; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT; - import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DREAM; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS; @@ -5631,6 +5630,15 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + void registerCompatScaleProvider(@CompatScaleProvider.CompatScaleModeOrderId int id, + @NonNull CompatScaleProvider provider) { + mCompatModePackages.registerCompatScaleProvider(id, provider); + } + + void unregisterCompatScaleProvider(@CompatScaleProvider.CompatScaleModeOrderId int id) { + mCompatModePackages.unregisterCompatScaleProvider(id); + } + /** * Returns {@code true} if the process represented by the pid passed as argument is * instrumented and the instrumentation source was granted with the permission also diff --git a/services/core/java/com/android/server/wm/CompatModePackages.java b/services/core/java/com/android/server/wm/CompatModePackages.java index c6978fd59134..e906b185bae7 100644 --- a/services/core/java/com/android/server/wm/CompatModePackages.java +++ b/services/core/java/com/android/server/wm/CompatModePackages.java @@ -20,7 +20,10 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; +import static com.android.server.wm.CompatScaleProvider.COMPAT_SCALE_MODE_SYSTEM_FIRST; +import static com.android.server.wm.CompatScaleProvider.COMPAT_SCALE_MODE_SYSTEM_LAST; +import android.annotation.NonNull; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.GameManagerInternal; @@ -32,6 +35,7 @@ import android.compat.annotation.Overridable; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.res.CompatibilityInfo; +import android.content.res.CompatibilityInfo.CompatScale; import android.content.res.Configuration; import android.os.Build; import android.os.Handler; @@ -332,6 +336,8 @@ public final class CompatModePackages { private final HashMap<String, Integer> mPackages = new HashMap<>(); private final CompatHandler mHandler; + private final SparseArray<CompatScaleProvider> mProviders = new SparseArray<>(); + public CompatModePackages(ActivityTaskManagerService service, File systemDir, Handler handler) { mService = service; mFile = new AtomicFile(new File(systemDir, "packages-compat.xml"), "compat-mode"); @@ -441,13 +447,38 @@ public final class CompatModePackages { public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) { final boolean forceCompat = getPackageCompatModeEnabledLocked(ai); - final float compatScale = getCompatScale(ai.packageName, ai.uid); + final CompatScale compatScale = getCompatScaleFromProvider(ai.packageName, ai.uid); + final float appScale = compatScale != null + ? compatScale.mScaleFactor + : getCompatScale(ai.packageName, ai.uid, /* checkProvider= */ false); + final float densityScale = compatScale != null ? compatScale.mDensityScaleFactor : 1f; final Configuration config = mService.getGlobalConfiguration(); return new CompatibilityInfo(ai, config.screenLayout, config.smallestScreenWidthDp, - forceCompat, compatScale); + forceCompat, appScale, densityScale); } float getCompatScale(String packageName, int uid) { + return getCompatScale(packageName, uid, /* checkProvider= */ true); + } + + private CompatScale getCompatScaleFromProvider(String packageName, int uid) { + for (int i = 0; i < mProviders.size(); i++) { + final CompatScaleProvider provider = mProviders.valueAt(i); + final CompatScale compatScale = provider.getCompatScale(packageName, uid); + if (compatScale != null) { + return compatScale; + } + } + return null; + } + + private float getCompatScale(String packageName, int uid, boolean checkProviders) { + if (checkProviders) { + final CompatScale compatScale = getCompatScaleFromProvider(packageName, uid); + if (compatScale != null) { + return compatScale.mScaleFactor; + } + } final UserHandle userHandle = UserHandle.getUserHandleForUid(uid); if (mGameManager == null) { mGameManager = LocalServices.getService(GameManagerInternal.class); @@ -487,6 +518,36 @@ public final class CompatModePackages { return 1f; } + void registerCompatScaleProvider(@CompatScaleProvider.CompatScaleModeOrderId int id, + @NonNull CompatScaleProvider provider) { + synchronized (mService.mGlobalLock) { + if (mProviders.contains(id)) { + throw new IllegalArgumentException("Duplicate id provided: " + id); + } + if (provider == null) { + throw new IllegalArgumentException("The passed CompatScaleProvider " + + "can not be null"); + } + if (!CompatScaleProvider.isValidOrderId(id)) { + throw new IllegalArgumentException( + "Provided id " + id + " is not in range of valid ids for system " + + "services [" + COMPAT_SCALE_MODE_SYSTEM_FIRST + "," + + COMPAT_SCALE_MODE_SYSTEM_LAST + "]"); + } + mProviders.put(id, provider); + } + } + + void unregisterCompatScaleProvider(@CompatScaleProvider.CompatScaleModeOrderId int id) { + synchronized (mService.mGlobalLock) { + if (!mProviders.contains(id)) { + throw new IllegalArgumentException( + "CompatScaleProvider with id (" + id + ") is not registered"); + } + mProviders.remove(id); + } + } + private static float getScalingFactor(String packageName, UserHandle userHandle) { if (CompatChanges.isChangeEnabled(DOWNSCALE_90, packageName, userHandle)) { return 0.9f; diff --git a/services/core/java/com/android/server/wm/CompatScaleProvider.java b/services/core/java/com/android/server/wm/CompatScaleProvider.java new file mode 100644 index 000000000000..5474ecec381a --- /dev/null +++ b/services/core/java/com/android/server/wm/CompatScaleProvider.java @@ -0,0 +1,86 @@ +/* + * 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.wm; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.res.CompatibilityInfo.CompatScale; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * An interface for services that need to provide compatibility scale different than + * the default android compatibility. + */ +public interface CompatScaleProvider { + + /** + * The unique id of each provider registered by a system service which determines the order + * it will execute in. + */ + @IntDef(prefix = { "COMPAT_SCALE_MODE_" }, value = { + // Order Ids for system services + COMPAT_SCALE_MODE_SYSTEM_FIRST, + COMPAT_SCALE_MODE_GAME, + COMPAT_SCALE_MODE_PRODUCT, + COMPAT_SCALE_MODE_SYSTEM_LAST, // Update this when adding new ids + }) + @Retention(RetentionPolicy.SOURCE) + @interface CompatScaleModeOrderId {} + + /** + * The first id, used by the framework to determine the valid range of ids. + * @hide + */ + int COMPAT_SCALE_MODE_SYSTEM_FIRST = 0; + + /** + * TODO(b/295207384) + * The identifier for {@link android.app.GameManagerInternal} provider + * @hide + */ + int COMPAT_SCALE_MODE_GAME = 1; + + /** + * The identifier for a provider which is specific to the type of android product like + * Automotive, Wear, TV etc. + * @hide + */ + int COMPAT_SCALE_MODE_PRODUCT = 2; + + /** + * The final id, used by the framework to determine the valid range of ids. Update this when + * adding new ids. + * @hide + */ + int COMPAT_SCALE_MODE_SYSTEM_LAST = COMPAT_SCALE_MODE_PRODUCT; + + /** + * Returns {@code true} if the id is in the range of valid system services + * @hide + */ + static boolean isValidOrderId(int id) { + return (id >= COMPAT_SCALE_MODE_SYSTEM_FIRST && id <= COMPAT_SCALE_MODE_SYSTEM_LAST); + } + + /** + * @return an instance of {@link CompatScale} to apply for the given package + */ + @Nullable + CompatScale getCompatScale(@NonNull String packageName, int uid); +} diff --git a/services/tests/wmtests/src/com/android/server/wm/CompatScaleProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/CompatScaleProviderTest.java new file mode 100644 index 000000000000..96e3cb1030ce --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/CompatScaleProviderTest.java @@ -0,0 +1,166 @@ +/* + * 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.wm; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; + +/** + * Tests for the {@link CompatScaleProvider} interface. + * See {@link CompatModePackages} class for implementation. + * + * Build/Install/Run: + * atest WmTests:CompatScaleProviderTest + */ +@SmallTest +@Presubmit +public class CompatScaleProviderTest extends SystemServiceTestsBase { + private static final String TEST_PACKAGE = "compat.mode.packages"; + static final int TEST_USER_ID = 1; + + private ActivityTaskManagerService mAtm; + + /** + * setup method before every test. + */ + @Before + public void setUp() { + mAtm = mSystemServicesTestRule.getActivityTaskManagerService(); + } + + /** + * Registering a {@link CompatScaleProvider} with an invalid id should throw an exception. + */ + @Test + public void registerCompatScaleProviderWithInvalidId() { + CompatScaleProvider compatScaleProvider = mock(CompatScaleProvider.class); + assertThrows( + IllegalArgumentException.class, + () -> mAtm.registerCompatScaleProvider(-1, compatScaleProvider) + ); + } + + /** + * Registering a {@code null} {@link CompatScaleProvider} should throw an exception. + */ + @Test + public void registerCompatScaleProviderFailIfCallbackIsNull() { + assertThrows( + IllegalArgumentException.class, + () -> mAtm.registerCompatScaleProvider( + CompatScaleProvider.COMPAT_SCALE_MODE_PRODUCT, null) + ); + } + + /** + * Registering a {@link CompatScaleProvider} with a already registered id should throw an + * exception. + */ + @Test + public void registerCompatScaleProviderFailIfIdIsAlreadyRegistered() { + CompatScaleProvider compatScaleProvider = mock(CompatScaleProvider.class); + mAtm.registerCompatScaleProvider(CompatScaleProvider.COMPAT_SCALE_MODE_PRODUCT, + compatScaleProvider); + assertThrows( + IllegalArgumentException.class, + () -> mAtm.registerCompatScaleProvider( + CompatScaleProvider.COMPAT_SCALE_MODE_PRODUCT, compatScaleProvider) + ); + mAtm.unregisterCompatScaleProvider(CompatScaleProvider.COMPAT_SCALE_MODE_PRODUCT); + } + + /** + * Successfully registering a {@link CompatScaleProvider} with should result in callbacks + * getting called. + */ + @Test + public void registerCompatScaleProviderSuccessfully() { + CompatScaleProvider compatScaleProvider = mock(CompatScaleProvider.class); + mAtm.registerCompatScaleProvider(CompatScaleProvider.COMPAT_SCALE_MODE_PRODUCT, + compatScaleProvider); + mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID); + verify(compatScaleProvider, times(1)).getCompatScale(TEST_PACKAGE, TEST_USER_ID); + mAtm.unregisterCompatScaleProvider(CompatScaleProvider.COMPAT_SCALE_MODE_PRODUCT); + } + + /** + * Unregistering a {@link CompatScaleProvider} with a unregistered id should throw an exception. + */ + @Test + public void unregisterCompatScaleProviderFailIfIdNotRegistered() { + assertThrows( + IllegalArgumentException.class, + () -> mAtm.unregisterCompatScaleProvider( + CompatScaleProvider.COMPAT_SCALE_MODE_PRODUCT) + ); + } + + /** + * Unregistering a {@link CompatScaleProvider} with an invalid id should throw an exception. + */ + @Test + public void unregisterCompatScaleProviderFailIfIdNotInRange() { + assertThrows( + IllegalArgumentException.class, + () -> mAtm.unregisterCompatScaleProvider(-1) + ); + } + + /** + * Successfully unregistering a {@link CompatScaleProvider} should stop the callbacks from + * getting called. + */ + @Test + public void unregisterCompatScaleProviderSuccessfully() { + CompatScaleProvider compatScaleProvider = mock(CompatScaleProvider.class); + mAtm.registerCompatScaleProvider(CompatScaleProvider.COMPAT_SCALE_MODE_PRODUCT, + compatScaleProvider); + mAtm.unregisterCompatScaleProvider(CompatScaleProvider.COMPAT_SCALE_MODE_PRODUCT); + mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID); + verify(compatScaleProvider, never()).getCompatScale(TEST_PACKAGE, TEST_USER_ID); + } + + /** + * Order of calling {@link CompatScaleProvider} is same as the id that was used for + * registering it. + */ + @Test + public void registerCompatScaleProviderRespectsOrderId() { + CompatScaleProvider gameModeCompatScaleProvider = mock(CompatScaleProvider.class); + CompatScaleProvider productCompatScaleProvider = mock(CompatScaleProvider.class); + mAtm.registerCompatScaleProvider(CompatScaleProvider.COMPAT_SCALE_MODE_GAME, + gameModeCompatScaleProvider); + mAtm.registerCompatScaleProvider(CompatScaleProvider.COMPAT_SCALE_MODE_PRODUCT, + productCompatScaleProvider); + mAtm.mCompatModePackages.getCompatScale(TEST_PACKAGE, TEST_USER_ID); + InOrder inOrder = inOrder(gameModeCompatScaleProvider, productCompatScaleProvider); + inOrder.verify(gameModeCompatScaleProvider).getCompatScale(TEST_PACKAGE, TEST_USER_ID); + inOrder.verify(productCompatScaleProvider).getCompatScale(TEST_PACKAGE, TEST_USER_ID); + } +} |