| /* |
| * Copyright (C) 2014 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.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; |
| import android.app.compat.CompatChanges; |
| import android.compat.annotation.ChangeId; |
| import android.compat.annotation.Disabled; |
| import android.compat.annotation.EnabledSince; |
| 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; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.RemoteException; |
| import android.os.UserHandle; |
| import android.util.AtomicFile; |
| import android.util.DisplayMetrics; |
| import android.util.Slog; |
| import android.util.SparseArray; |
| import android.util.Xml; |
| |
| import com.android.internal.protolog.common.ProtoLog; |
| import com.android.modules.utils.TypedXmlPullParser; |
| import com.android.modules.utils.TypedXmlSerializer; |
| import com.android.server.LocalServices; |
| |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| public final class CompatModePackages { |
| /** |
| * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> is the gatekeeper of all per-app buffer |
| * inverse downscale changes. Enabling this change will allow the following scaling factors: |
| * <a href="#DOWNSCALE_90">DOWNSCALE_90</a> |
| * <a href="#DOWNSCALE_85">DOWNSCALE_85</a> |
| * <a href="#DOWNSCALE_80">DOWNSCALE_80</a> |
| * <a href="#DOWNSCALE_75">DOWNSCALE_75</a> |
| * <a href="#DOWNSCALE_70">DOWNSCALE_70</a> |
| * <a href="#DOWNSCALE_65">DOWNSCALE_65</a> |
| * <a href="#DOWNSCALE_60">DOWNSCALE_60</a> |
| * <a href="#DOWNSCALE_55">DOWNSCALE_55</a> |
| * <a href="#DOWNSCALE_50">DOWNSCALE_50</a> |
| * <a href="#DOWNSCALE_45">DOWNSCALE_45</a> |
| * <a href="#DOWNSCALE_40">DOWNSCALE_40</a> |
| * <a href="#DOWNSCALE_35">DOWNSCALE_35</a> |
| * <a href="#DOWNSCALE_30">DOWNSCALE_30</a> |
| * |
| * If <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> is enabled for an app package, then |
| * the app will be forcibly resized to the lowest enabled scaling factor e.g. 1/0.8 if both |
| * 1/0.8 and 1/0.7 (* 100%) were enabled. |
| * |
| * When both <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> |
| * and <a href="#DOWNSCALED">DOWNSCALED</a> are enabled, then |
| * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> takes precedence. |
| */ |
| @ChangeId |
| @Disabled |
| @Overridable |
| public static final long DOWNSCALED_INVERSE = 273564678L; // This is a Bug ID. |
| |
| /** |
| * <a href="#DOWNSCALED">DOWNSCALED</a> is the gatekeeper of all per-app buffer downscaling |
| * changes. Enabling this change will allow the following scaling factors: |
| * <a href="#DOWNSCALE_90">DOWNSCALE_90</a> |
| * <a href="#DOWNSCALE_85">DOWNSCALE_85</a> |
| * <a href="#DOWNSCALE_80">DOWNSCALE_80</a> |
| * <a href="#DOWNSCALE_75">DOWNSCALE_75</a> |
| * <a href="#DOWNSCALE_70">DOWNSCALE_70</a> |
| * <a href="#DOWNSCALE_65">DOWNSCALE_65</a> |
| * <a href="#DOWNSCALE_60">DOWNSCALE_60</a> |
| * <a href="#DOWNSCALE_55">DOWNSCALE_55</a> |
| * <a href="#DOWNSCALE_50">DOWNSCALE_50</a> |
| * <a href="#DOWNSCALE_45">DOWNSCALE_45</a> |
| * <a href="#DOWNSCALE_40">DOWNSCALE_40</a> |
| * <a href="#DOWNSCALE_35">DOWNSCALE_35</a> |
| * <a href="#DOWNSCALE_30">DOWNSCALE_30</a> |
| * |
| * If <a href="#DOWNSCALED">DOWNSCALED</a> is enabled for an app package, then the app will be |
| * forcibly resized to the highest enabled scaling factor e.g. 80% if both 80% and 70% were |
| * enabled. |
| * |
| * When both <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> |
| * and <a href="#DOWNSCALED">DOWNSCALED</a> are enabled, then |
| * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> takes precedence. |
| */ |
| @ChangeId |
| @Disabled |
| @Overridable |
| public static final long DOWNSCALED = 168419799L; |
| |
| /** |
| * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id |
| * <a href="#DOWNSCALE_90">DOWNSCALE_90</a> for a package will force the app to assume it's |
| * running on a display with 90% the vertical and horizontal resolution of the real display. |
| * |
| * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to |
| * assume it's running on a display with 111.11% the vertical and horizontal resolution of |
| * the real display |
| */ |
| @ChangeId |
| @Disabled |
| @Overridable |
| public static final long DOWNSCALE_90 = 182811243L; |
| |
| /** |
| * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id |
| * <a href="#DOWNSCALE_85">DOWNSCALE_85</a> for a package will force the app to assume it's |
| * running on a display with 85% the vertical and horizontal resolution of the real display. |
| * |
| * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to |
| * assume it's running on a display with 117.65% the vertical and horizontal resolution of the |
| * real display |
| */ |
| @ChangeId |
| @Disabled |
| @Overridable |
| public static final long DOWNSCALE_85 = 189969734L; |
| |
| /** |
| * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id |
| * <a href="#DOWNSCALE_80">DOWNSCALE_80</a> for a package will force the app to assume it's |
| * running on a display with 80% the vertical and horizontal resolution of the real display. |
| * |
| * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to |
| * assume it's running on a display with 125% the vertical and horizontal resolution of the real |
| * display |
| */ |
| @ChangeId |
| @Disabled |
| @Overridable |
| public static final long DOWNSCALE_80 = 176926753L; |
| |
| /** |
| * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id |
| * <a href="#DOWNSCALE_75">DOWNSCALE_75</a> for a package will force the app to assume it's |
| * running on a display with 75% the vertical and horizontal resolution of the real display. |
| * |
| * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to |
| * assume it's running on a display with 133.33% the vertical and horizontal resolution of the |
| * real display |
| */ |
| @ChangeId |
| @Disabled |
| @Overridable |
| public static final long DOWNSCALE_75 = 189969779L; |
| |
| /** |
| * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id |
| * <a href="#DOWNSCALE_70">DOWNSCALE_70</a> for a package will force the app to assume it's |
| * running on a display with 70% the vertical and horizontal resolution of the real display. |
| * |
| * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to |
| * assume it's running on a display with 142.86% the vertical and horizontal resolution of the |
| * real display |
| */ |
| @ChangeId |
| @Disabled |
| @Overridable |
| public static final long DOWNSCALE_70 = 176926829L; |
| |
| /** |
| * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id |
| * <a href="#DOWNSCALE_65">DOWNSCALE_65</a> for a package will force the app to assume it's |
| * running on a display with 65% the vertical and horizontal resolution of the real display. |
| * |
| * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to |
| * assume it's running on a display with 153.85% the vertical and horizontal resolution of the |
| * real display |
| */ |
| @ChangeId |
| @Disabled |
| @Overridable |
| public static final long DOWNSCALE_65 = 189969744L; |
| |
| /** |
| * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id |
| * <a href="#DOWNSCALE_60">DOWNSCALE_60</a> for a package will force the app to assume it's |
| * running on a display with 60% the vertical and horizontal resolution of the real display. |
| * |
| * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to |
| * assume it's running on a display with 166.67% the vertical and horizontal resolution of the |
| * real display |
| */ |
| @ChangeId |
| @Disabled |
| @Overridable |
| public static final long DOWNSCALE_60 = 176926771L; |
| |
| /** |
| * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id |
| * <a href="#DOWNSCALE_55">DOWNSCALE_55</a> for a package will force the app to assume it's |
| * running on a display with 55% the vertical and horizontal resolution of the real display. |
| * |
| * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to |
| * assume it's running on a display with 181.82% the vertical and horizontal resolution of the |
| * real display |
| */ |
| @ChangeId |
| @Disabled |
| @Overridable |
| public static final long DOWNSCALE_55 = 189970036L; |
| |
| /** |
| * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id |
| * <a href="#DOWNSCALE_50">DOWNSCALE_50</a> for a package will force the app to assume it's |
| * running on a display with 50% vertical and horizontal resolution of the real display. |
| * |
| * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to |
| * assume it's running on a display with 200% the vertical and horizontal resolution of the real |
| * display |
| */ |
| @ChangeId |
| @Disabled |
| @Overridable |
| public static final long DOWNSCALE_50 = 176926741L; |
| |
| /** |
| * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id |
| * <a href="#DOWNSCALE_45">DOWNSCALE_45</a> for a package will force the app to assume it's |
| * running on a display with 45% the vertical and horizontal resolution of the real display. |
| * |
| * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to |
| * assume it's running on a display with 222.22% the vertical and horizontal resolution of the |
| * real display |
| */ |
| @ChangeId |
| @Disabled |
| @Overridable |
| public static final long DOWNSCALE_45 = 189969782L; |
| |
| /** |
| * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id |
| * <a href="#DOWNSCALE_40">DOWNSCALE_40</a> for a package will force the app to assume it's |
| * running on a display with 40% the vertical and horizontal resolution of the real display. |
| * |
| * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to |
| * assume it's running on a display with 250% the vertical and horizontal resolution of the real |
| * display |
| */ |
| @ChangeId |
| @Disabled |
| @Overridable |
| public static final long DOWNSCALE_40 = 189970038L; |
| |
| /** |
| * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id |
| * <a href="#DOWNSCALE_35">DOWNSCALE_35</a> for a package will force the app to assume it's |
| * running on a display with 35% the vertical and horizontal resolution of the real display. |
| * |
| * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to |
| * assume it's running on a display with 285.71% the vertical and horizontal resolution of the |
| * real display |
| */ |
| @ChangeId |
| @Disabled |
| @Overridable |
| public static final long DOWNSCALE_35 = 189969749L; |
| |
| /** |
| * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id |
| * <a href="#DOWNSCALE_30">DOWNSCALE_30</a> for a package will force the app to assume it's |
| * running on a display with 30% the vertical and horizontal resolution of the real display. |
| * |
| * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to |
| * assume it's running on a display with 333.33% the vertical and horizontal resolution of the |
| * real display |
| */ |
| @ChangeId |
| @Disabled |
| @Overridable |
| public static final long DOWNSCALE_30 = 189970040L; |
| |
| /** |
| * On Android TV applications that target pre-S are not expecting to receive a Window larger |
| * than 1080p, so if needed we are downscaling their Windows to 1080p. |
| * However, applications that target S and greater release version are expected to be able to |
| * handle any Window size, so we should not downscale their Windows. |
| */ |
| @ChangeId |
| @Overridable |
| @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) |
| private static final long DO_NOT_DOWNSCALE_TO_1080P_ON_TV = 157629738L; // This is a Bug ID. |
| |
| private static final int MSG_WRITE = 300; |
| |
| private static final String TAG = TAG_WITH_CLASS_NAME ? "CompatModePackages" : TAG_ATM; |
| |
| // Compatibility state: no longer ask user to select the mode. |
| private static final int COMPAT_FLAG_DONT_ASK = 1 << 0; |
| |
| // Compatibility state: compatibility mode is enabled. |
| private static final int COMPAT_FLAG_ENABLED = 1 << 1; |
| |
| private final class CompatHandler extends Handler { |
| public CompatHandler(Looper looper) { |
| super(looper, null, true); |
| } |
| |
| @Override |
| public void handleMessage(Message msg) { |
| switch (msg.what) { |
| case MSG_WRITE: |
| saveCompatModes(); |
| break; |
| } |
| } |
| } |
| |
| private final ActivityTaskManagerService mService; |
| private GameManagerInternal mGameManager; |
| private final AtomicFile mFile; |
| 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"); |
| mHandler = new CompatHandler(handler.getLooper()); |
| |
| FileInputStream fis = null; |
| try { |
| fis = mFile.openRead(); |
| TypedXmlPullParser parser = Xml.resolvePullParser(fis); |
| int eventType = parser.getEventType(); |
| while (eventType != XmlPullParser.START_TAG && |
| eventType != XmlPullParser.END_DOCUMENT) { |
| eventType = parser.next(); |
| } |
| if (eventType == XmlPullParser.END_DOCUMENT) { |
| return; |
| } |
| |
| String tagName = parser.getName(); |
| if ("compat-packages".equals(tagName)) { |
| eventType = parser.next(); |
| do { |
| if (eventType == XmlPullParser.START_TAG) { |
| tagName = parser.getName(); |
| if (parser.getDepth() == 2) { |
| if ("pkg".equals(tagName)) { |
| String pkg = parser.getAttributeValue(null, "name"); |
| if (pkg != null) { |
| int modeInt = parser.getAttributeInt(null, "mode", 0); |
| mPackages.put(pkg, modeInt); |
| } |
| } |
| } |
| } |
| eventType = parser.next(); |
| } while (eventType != XmlPullParser.END_DOCUMENT); |
| } |
| } catch (XmlPullParserException e) { |
| Slog.w(TAG, "Error reading compat-packages", e); |
| } catch (java.io.IOException e) { |
| if (fis != null) Slog.w(TAG, "Error reading compat-packages", e); |
| } finally { |
| if (fis != null) { |
| try { |
| fis.close(); |
| } catch (java.io.IOException e1) { |
| } |
| } |
| } |
| } |
| |
| public HashMap<String, Integer> getPackages() { |
| return mPackages; |
| } |
| |
| private int getPackageFlags(String packageName) { |
| Integer flags = mPackages.get(packageName); |
| return flags != null ? flags : 0; |
| } |
| |
| public void handlePackageDataClearedLocked(String packageName) { |
| // User has explicitly asked to clear all associated data. |
| removePackage(packageName); |
| } |
| |
| public void handlePackageUninstalledLocked(String packageName) { |
| // Clear settings when app is uninstalled since this is an explicit |
| // signal from the user to remove the app and all associated data. |
| removePackage(packageName); |
| } |
| |
| private void removePackage(String packageName) { |
| if (mPackages.containsKey(packageName)) { |
| mPackages.remove(packageName); |
| scheduleWrite(); |
| } |
| } |
| |
| public void handlePackageAddedLocked(String packageName, boolean updated) { |
| ApplicationInfo ai = null; |
| try { |
| ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0); |
| } catch (RemoteException e) { |
| } |
| if (ai == null) { |
| return; |
| } |
| CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai); |
| final boolean mayCompat = !ci.alwaysSupportsScreen() |
| && !ci.neverSupportsScreen(); |
| |
| if (updated) { |
| // Update -- if the app no longer can run in compat mode, clear |
| // any current settings for it. |
| if (!mayCompat && mPackages.containsKey(packageName)) { |
| mPackages.remove(packageName); |
| scheduleWrite(); |
| } |
| } |
| } |
| |
| private void scheduleWrite() { |
| mHandler.removeMessages(MSG_WRITE); |
| Message msg = mHandler.obtainMessage(MSG_WRITE); |
| mHandler.sendMessageDelayed(msg, 10000); |
| } |
| |
| public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) { |
| final boolean forceCompat = getPackageCompatModeEnabledLocked(ai); |
| 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 : appScale; |
| final Configuration config = mService.getGlobalConfiguration(); |
| return new CompatibilityInfo(ai, config.screenLayout, config.smallestScreenWidthDp, |
| 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); |
| } |
| if (mGameManager != null) { |
| final int userId = userHandle.getIdentifier(); |
| final float scalingFactor = mGameManager.getResolutionScalingFactor(packageName, |
| userId); |
| if (scalingFactor > 0) { |
| return 1f / scalingFactor; |
| } |
| } |
| |
| final boolean isDownscaledEnabled = CompatChanges.isChangeEnabled( |
| DOWNSCALED, packageName, userHandle); |
| final boolean isDownscaledInverseEnabled = CompatChanges.isChangeEnabled( |
| DOWNSCALED_INVERSE, packageName, userHandle); |
| if (isDownscaledEnabled || isDownscaledInverseEnabled) { |
| final float scalingFactor = getScalingFactor(packageName, userHandle); |
| if (scalingFactor != 1f) { |
| // For Upscaling the returned factor must be scalingFactor |
| // For Downscaling the returned factor must be 1f / scalingFactor |
| return isDownscaledInverseEnabled ? scalingFactor : 1f / scalingFactor; |
| } |
| } |
| |
| if (mService.mHasLeanbackFeature) { |
| final Configuration config = mService.getGlobalConfiguration(); |
| final float density = config.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT; |
| final int smallestScreenWidthPx = (int) (config.smallestScreenWidthDp * density + .5f); |
| if (smallestScreenWidthPx > 1080 && !CompatChanges.isChangeEnabled( |
| DO_NOT_DOWNSCALE_TO_1080P_ON_TV, packageName, userHandle)) { |
| return smallestScreenWidthPx / 1080f; |
| } |
| } |
| |
| 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; |
| } |
| if (CompatChanges.isChangeEnabled(DOWNSCALE_85, packageName, userHandle)) { |
| return 0.85f; |
| } |
| if (CompatChanges.isChangeEnabled(DOWNSCALE_80, packageName, userHandle)) { |
| return 0.8f; |
| } |
| if (CompatChanges.isChangeEnabled(DOWNSCALE_75, packageName, userHandle)) { |
| return 0.75f; |
| } |
| if (CompatChanges.isChangeEnabled(DOWNSCALE_70, packageName, userHandle)) { |
| return 0.7f; |
| } |
| if (CompatChanges.isChangeEnabled(DOWNSCALE_65, packageName, userHandle)) { |
| return 0.65f; |
| } |
| if (CompatChanges.isChangeEnabled(DOWNSCALE_60, packageName, userHandle)) { |
| return 0.6f; |
| } |
| if (CompatChanges.isChangeEnabled(DOWNSCALE_55, packageName, userHandle)) { |
| return 0.55f; |
| } |
| if (CompatChanges.isChangeEnabled(DOWNSCALE_50, packageName, userHandle)) { |
| return 0.5f; |
| } |
| if (CompatChanges.isChangeEnabled(DOWNSCALE_45, packageName, userHandle)) { |
| return 0.45f; |
| } |
| if (CompatChanges.isChangeEnabled(DOWNSCALE_40, packageName, userHandle)) { |
| return 0.4f; |
| } |
| if (CompatChanges.isChangeEnabled(DOWNSCALE_35, packageName, userHandle)) { |
| return 0.35f; |
| } |
| if (CompatChanges.isChangeEnabled(DOWNSCALE_30, packageName, userHandle)) { |
| return 0.3f; |
| } |
| return 1f; |
| } |
| |
| public int computeCompatModeLocked(ApplicationInfo ai) { |
| final CompatibilityInfo info = compatibilityInfoForPackageLocked(ai); |
| if (info.alwaysSupportsScreen()) { |
| return ActivityManager.COMPAT_MODE_NEVER; |
| } |
| if (info.neverSupportsScreen()) { |
| return ActivityManager.COMPAT_MODE_ALWAYS; |
| } |
| return getPackageCompatModeEnabledLocked(ai) ? ActivityManager.COMPAT_MODE_ENABLED |
| : ActivityManager.COMPAT_MODE_DISABLED; |
| } |
| |
| public boolean getPackageAskCompatModeLocked(String packageName) { |
| return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0; |
| } |
| |
| public void setPackageAskCompatModeLocked(String packageName, boolean ask) { |
| setPackageFlagLocked(packageName, COMPAT_FLAG_DONT_ASK, ask); |
| } |
| |
| private boolean getPackageCompatModeEnabledLocked(ApplicationInfo ai) { |
| return (getPackageFlags(ai.packageName) & COMPAT_FLAG_ENABLED) != 0; |
| } |
| |
| private void setPackageFlagLocked(String packageName, int flag, boolean set) { |
| final int curFlags = getPackageFlags(packageName); |
| final int newFlags = set ? (curFlags & ~flag) : (curFlags | flag); |
| if (curFlags != newFlags) { |
| if (newFlags != 0) { |
| mPackages.put(packageName, newFlags); |
| } else { |
| mPackages.remove(packageName); |
| } |
| scheduleWrite(); |
| } |
| } |
| |
| public int getPackageScreenCompatModeLocked(String packageName) { |
| ApplicationInfo ai = null; |
| try { |
| ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0); |
| } catch (RemoteException e) { |
| } |
| if (ai == null) { |
| return ActivityManager.COMPAT_MODE_UNKNOWN; |
| } |
| return computeCompatModeLocked(ai); |
| } |
| |
| public void setPackageScreenCompatModeLocked(String packageName, int mode) { |
| ApplicationInfo ai = null; |
| try { |
| ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0); |
| } catch (RemoteException e) { |
| } |
| if (ai == null) { |
| Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName); |
| return; |
| } |
| setPackageScreenCompatModeLocked(ai, mode); |
| } |
| |
| void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) { |
| final String packageName = ai.packageName; |
| |
| int curFlags = getPackageFlags(packageName); |
| |
| boolean enable; |
| switch (mode) { |
| case ActivityManager.COMPAT_MODE_DISABLED: |
| enable = false; |
| break; |
| case ActivityManager.COMPAT_MODE_ENABLED: |
| enable = true; |
| break; |
| case ActivityManager.COMPAT_MODE_TOGGLE: |
| enable = (curFlags&COMPAT_FLAG_ENABLED) == 0; |
| break; |
| default: |
| Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring"); |
| return; |
| } |
| |
| int newFlags = curFlags; |
| if (enable) { |
| newFlags |= COMPAT_FLAG_ENABLED; |
| } else { |
| newFlags &= ~COMPAT_FLAG_ENABLED; |
| } |
| |
| CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai); |
| if (ci.alwaysSupportsScreen()) { |
| Slog.w(TAG, "Ignoring compat mode change of " + packageName |
| + "; compatibility never needed"); |
| newFlags = 0; |
| } |
| if (ci.neverSupportsScreen()) { |
| Slog.w(TAG, "Ignoring compat mode change of " + packageName |
| + "; compatibility always needed"); |
| newFlags = 0; |
| } |
| |
| if (newFlags != curFlags) { |
| if (newFlags != 0) { |
| mPackages.put(packageName, newFlags); |
| } else { |
| mPackages.remove(packageName); |
| } |
| |
| // Need to get compatibility info in new state. |
| ci = compatibilityInfoForPackageLocked(ai); |
| |
| scheduleWrite(); |
| |
| final Task rootTask = mService.getTopDisplayFocusedRootTask(); |
| ActivityRecord starting = rootTask.restartPackage(packageName); |
| |
| // Tell all processes that loaded this package about the change. |
| SparseArray<WindowProcessController> pidMap = mService.mProcessMap.getPidMap(); |
| for (int i = pidMap.size() - 1; i >= 0; i--) { |
| final WindowProcessController app = pidMap.valueAt(i); |
| if (!app.containsPackage(packageName)) { |
| continue; |
| } |
| try { |
| if (app.hasThread()) { |
| ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s " |
| + "new compat %s", app.mName, ci); |
| app.getThread().updatePackageCompatibilityInfo(packageName, ci); |
| } |
| } catch (Exception e) { |
| } |
| } |
| |
| if (starting != null) { |
| starting.ensureActivityConfiguration(0 /* globalChanges */, |
| false /* preserveWindow */); |
| // And we need to make sure at this point that all other activities |
| // are made visible with the correct configuration. |
| rootTask.ensureActivitiesVisible(starting, 0, !PRESERVE_WINDOWS); |
| } |
| } |
| } |
| |
| private void saveCompatModes() { |
| HashMap<String, Integer> pkgs; |
| synchronized (mService.mGlobalLock) { |
| pkgs = new HashMap<>(mPackages); |
| } |
| |
| FileOutputStream fos = null; |
| |
| try { |
| fos = mFile.startWrite(); |
| TypedXmlSerializer out = Xml.resolveSerializer(fos); |
| out.startDocument(null, true); |
| out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); |
| out.startTag(null, "compat-packages"); |
| |
| final IPackageManager pm = AppGlobals.getPackageManager(); |
| final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator(); |
| while (it.hasNext()) { |
| Map.Entry<String, Integer> entry = it.next(); |
| String pkg = entry.getKey(); |
| int mode = entry.getValue(); |
| if (mode == 0) { |
| continue; |
| } |
| ApplicationInfo ai = null; |
| try { |
| ai = pm.getApplicationInfo(pkg, 0, 0); |
| } catch (RemoteException e) { |
| } |
| if (ai == null) { |
| continue; |
| } |
| final CompatibilityInfo info = compatibilityInfoForPackageLocked(ai); |
| if (info.alwaysSupportsScreen()) { |
| continue; |
| } |
| if (info.neverSupportsScreen()) { |
| continue; |
| } |
| out.startTag(null, "pkg"); |
| out.attribute(null, "name", pkg); |
| out.attributeInt(null, "mode", mode); |
| out.endTag(null, "pkg"); |
| } |
| |
| out.endTag(null, "compat-packages"); |
| out.endDocument(); |
| |
| mFile.finishWrite(fos); |
| } catch (java.io.IOException e1) { |
| Slog.w(TAG, "Error writing compat packages", e1); |
| if (fos != null) { |
| mFile.failWrite(fos); |
| } |
| } |
| } |
| } |