1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; 20 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; 21 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; 22 import static com.android.server.wm.CompatScaleProvider.COMPAT_SCALE_MODE_SYSTEM_FIRST; 23 import static com.android.server.wm.CompatScaleProvider.COMPAT_SCALE_MODE_SYSTEM_LAST; 24 25 import android.annotation.NonNull; 26 import android.app.ActivityManager; 27 import android.app.AppGlobals; 28 import android.app.compat.CompatChanges; 29 import android.compat.annotation.ChangeId; 30 import android.compat.annotation.Disabled; 31 import android.compat.annotation.EnabledSince; 32 import android.compat.annotation.Overridable; 33 import android.content.pm.ApplicationInfo; 34 import android.content.pm.IPackageManager; 35 import android.content.res.CompatibilityInfo; 36 import android.content.res.CompatibilityInfo.CompatScale; 37 import android.content.res.Configuration; 38 import android.os.Build; 39 import android.os.Handler; 40 import android.os.Looper; 41 import android.os.Message; 42 import android.os.RemoteException; 43 import android.os.UserHandle; 44 import android.util.AtomicFile; 45 import android.util.DisplayMetrics; 46 import android.util.Slog; 47 import android.util.SparseArray; 48 import android.util.SparseBooleanArray; 49 import android.util.Xml; 50 51 import com.android.internal.protolog.common.ProtoLog; 52 import com.android.modules.utils.TypedXmlPullParser; 53 import com.android.modules.utils.TypedXmlSerializer; 54 55 import org.xmlpull.v1.XmlPullParser; 56 import org.xmlpull.v1.XmlPullParserException; 57 58 import java.io.File; 59 import java.io.FileInputStream; 60 import java.io.FileOutputStream; 61 import java.util.ArrayList; 62 import java.util.HashMap; 63 import java.util.Iterator; 64 import java.util.Map; 65 66 public final class CompatModePackages { 67 /** 68 * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> is the gatekeeper of all per-app buffer 69 * inverse downscale changes. Enabling this change will allow the following scaling factors: 70 * <a href="#DOWNSCALE_90">DOWNSCALE_90</a> 71 * <a href="#DOWNSCALE_85">DOWNSCALE_85</a> 72 * <a href="#DOWNSCALE_80">DOWNSCALE_80</a> 73 * <a href="#DOWNSCALE_75">DOWNSCALE_75</a> 74 * <a href="#DOWNSCALE_70">DOWNSCALE_70</a> 75 * <a href="#DOWNSCALE_65">DOWNSCALE_65</a> 76 * <a href="#DOWNSCALE_60">DOWNSCALE_60</a> 77 * <a href="#DOWNSCALE_55">DOWNSCALE_55</a> 78 * <a href="#DOWNSCALE_50">DOWNSCALE_50</a> 79 * <a href="#DOWNSCALE_45">DOWNSCALE_45</a> 80 * <a href="#DOWNSCALE_40">DOWNSCALE_40</a> 81 * <a href="#DOWNSCALE_35">DOWNSCALE_35</a> 82 * <a href="#DOWNSCALE_30">DOWNSCALE_30</a> 83 * 84 * If <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> is enabled for an app package, then 85 * the app will be forcibly resized to the lowest enabled scaling factor e.g. 1/0.8 if both 86 * 1/0.8 and 1/0.7 (* 100%) were enabled. 87 * 88 * When both <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> 89 * and <a href="#DOWNSCALED">DOWNSCALED</a> are enabled, then 90 * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> takes precedence. 91 */ 92 @ChangeId 93 @Disabled 94 @Overridable 95 public static final long DOWNSCALED_INVERSE = 273564678L; // This is a Bug ID. 96 97 /** 98 * <a href="#DOWNSCALED">DOWNSCALED</a> is the gatekeeper of all per-app buffer downscaling 99 * changes. Enabling this change will allow the following scaling factors: 100 * <a href="#DOWNSCALE_90">DOWNSCALE_90</a> 101 * <a href="#DOWNSCALE_85">DOWNSCALE_85</a> 102 * <a href="#DOWNSCALE_80">DOWNSCALE_80</a> 103 * <a href="#DOWNSCALE_75">DOWNSCALE_75</a> 104 * <a href="#DOWNSCALE_70">DOWNSCALE_70</a> 105 * <a href="#DOWNSCALE_65">DOWNSCALE_65</a> 106 * <a href="#DOWNSCALE_60">DOWNSCALE_60</a> 107 * <a href="#DOWNSCALE_55">DOWNSCALE_55</a> 108 * <a href="#DOWNSCALE_50">DOWNSCALE_50</a> 109 * <a href="#DOWNSCALE_45">DOWNSCALE_45</a> 110 * <a href="#DOWNSCALE_40">DOWNSCALE_40</a> 111 * <a href="#DOWNSCALE_35">DOWNSCALE_35</a> 112 * <a href="#DOWNSCALE_30">DOWNSCALE_30</a> 113 * 114 * If <a href="#DOWNSCALED">DOWNSCALED</a> is enabled for an app package, then the app will be 115 * forcibly resized to the highest enabled scaling factor e.g. 80% if both 80% and 70% were 116 * enabled. 117 * 118 * When both <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> 119 * and <a href="#DOWNSCALED">DOWNSCALED</a> are enabled, then 120 * <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> takes precedence. 121 */ 122 @ChangeId 123 @Disabled 124 @Overridable 125 public static final long DOWNSCALED = 168419799L; 126 127 /** 128 * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id 129 * <a href="#DOWNSCALE_90">DOWNSCALE_90</a> for a package will force the app to assume it's 130 * running on a display with 90% the vertical and horizontal resolution of the real display. 131 * 132 * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to 133 * assume it's running on a display with 111.11% the vertical and horizontal resolution of 134 * the real display 135 */ 136 @ChangeId 137 @Disabled 138 @Overridable 139 public static final long DOWNSCALE_90 = 182811243L; 140 141 /** 142 * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id 143 * <a href="#DOWNSCALE_85">DOWNSCALE_85</a> for a package will force the app to assume it's 144 * running on a display with 85% the vertical and horizontal resolution of the real display. 145 * 146 * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to 147 * assume it's running on a display with 117.65% the vertical and horizontal resolution of the 148 * real display 149 */ 150 @ChangeId 151 @Disabled 152 @Overridable 153 public static final long DOWNSCALE_85 = 189969734L; 154 155 /** 156 * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id 157 * <a href="#DOWNSCALE_80">DOWNSCALE_80</a> for a package will force the app to assume it's 158 * running on a display with 80% the vertical and horizontal resolution of the real display. 159 * 160 * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to 161 * assume it's running on a display with 125% the vertical and horizontal resolution of the real 162 * display 163 */ 164 @ChangeId 165 @Disabled 166 @Overridable 167 public static final long DOWNSCALE_80 = 176926753L; 168 169 /** 170 * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id 171 * <a href="#DOWNSCALE_75">DOWNSCALE_75</a> for a package will force the app to assume it's 172 * running on a display with 75% the vertical and horizontal resolution of the real display. 173 * 174 * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to 175 * assume it's running on a display with 133.33% the vertical and horizontal resolution of the 176 * real display 177 */ 178 @ChangeId 179 @Disabled 180 @Overridable 181 public static final long DOWNSCALE_75 = 189969779L; 182 183 /** 184 * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id 185 * <a href="#DOWNSCALE_70">DOWNSCALE_70</a> for a package will force the app to assume it's 186 * running on a display with 70% the vertical and horizontal resolution of the real display. 187 * 188 * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to 189 * assume it's running on a display with 142.86% the vertical and horizontal resolution of the 190 * real display 191 */ 192 @ChangeId 193 @Disabled 194 @Overridable 195 public static final long DOWNSCALE_70 = 176926829L; 196 197 /** 198 * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id 199 * <a href="#DOWNSCALE_65">DOWNSCALE_65</a> for a package will force the app to assume it's 200 * running on a display with 65% the vertical and horizontal resolution of the real display. 201 * 202 * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to 203 * assume it's running on a display with 153.85% the vertical and horizontal resolution of the 204 * real display 205 */ 206 @ChangeId 207 @Disabled 208 @Overridable 209 public static final long DOWNSCALE_65 = 189969744L; 210 211 /** 212 * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id 213 * <a href="#DOWNSCALE_60">DOWNSCALE_60</a> for a package will force the app to assume it's 214 * running on a display with 60% the vertical and horizontal resolution of the real display. 215 * 216 * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to 217 * assume it's running on a display with 166.67% the vertical and horizontal resolution of the 218 * real display 219 */ 220 @ChangeId 221 @Disabled 222 @Overridable 223 public static final long DOWNSCALE_60 = 176926771L; 224 225 /** 226 * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id 227 * <a href="#DOWNSCALE_55">DOWNSCALE_55</a> for a package will force the app to assume it's 228 * running on a display with 55% the vertical and horizontal resolution of the real display. 229 * 230 * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to 231 * assume it's running on a display with 181.82% the vertical and horizontal resolution of the 232 * real display 233 */ 234 @ChangeId 235 @Disabled 236 @Overridable 237 public static final long DOWNSCALE_55 = 189970036L; 238 239 /** 240 * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id 241 * <a href="#DOWNSCALE_50">DOWNSCALE_50</a> for a package will force the app to assume it's 242 * running on a display with 50% vertical and horizontal resolution of the real display. 243 * 244 * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to 245 * assume it's running on a display with 200% the vertical and horizontal resolution of the real 246 * display 247 */ 248 @ChangeId 249 @Disabled 250 @Overridable 251 public static final long DOWNSCALE_50 = 176926741L; 252 253 /** 254 * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id 255 * <a href="#DOWNSCALE_45">DOWNSCALE_45</a> for a package will force the app to assume it's 256 * running on a display with 45% the vertical and horizontal resolution of the real display. 257 * 258 * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to 259 * assume it's running on a display with 222.22% the vertical and horizontal resolution of the 260 * real display 261 */ 262 @ChangeId 263 @Disabled 264 @Overridable 265 public static final long DOWNSCALE_45 = 189969782L; 266 267 /** 268 * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id 269 * <a href="#DOWNSCALE_40">DOWNSCALE_40</a> for a package will force the app to assume it's 270 * running on a display with 40% the vertical and horizontal resolution of the real display. 271 * 272 * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to 273 * assume it's running on a display with 250% the vertical and horizontal resolution of the real 274 * display 275 */ 276 @ChangeId 277 @Disabled 278 @Overridable 279 public static final long DOWNSCALE_40 = 189970038L; 280 281 /** 282 * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id 283 * <a href="#DOWNSCALE_35">DOWNSCALE_35</a> for a package will force the app to assume it's 284 * running on a display with 35% the vertical and horizontal resolution of the real display. 285 * 286 * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to 287 * assume it's running on a display with 285.71% the vertical and horizontal resolution of the 288 * real display 289 */ 290 @ChangeId 291 @Disabled 292 @Overridable 293 public static final long DOWNSCALE_35 = 189969749L; 294 295 /** 296 * With <a href="#DOWNSCALED">DOWNSCALED</a> enabled, subsequently enabling change-id 297 * <a href="#DOWNSCALE_30">DOWNSCALE_30</a> for a package will force the app to assume it's 298 * running on a display with 30% the vertical and horizontal resolution of the real display. 299 * 300 * With <a href="#DOWNSCALED_INVERSE">DOWNSCALED_INVERSE</a> enabled will force the app to 301 * assume it's running on a display with 333.33% the vertical and horizontal resolution of the 302 * real display 303 */ 304 @ChangeId 305 @Disabled 306 @Overridable 307 public static final long DOWNSCALE_30 = 189970040L; 308 309 /** 310 * On Android TV applications that target pre-S are not expecting to receive a Window larger 311 * than 1080p, so if needed we are downscaling their Windows to 1080p. 312 * However, applications that target S and greater release version are expected to be able to 313 * handle any Window size, so we should not downscale their Windows. 314 */ 315 @ChangeId 316 @Overridable 317 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) 318 private static final long DO_NOT_DOWNSCALE_TO_1080P_ON_TV = 157629738L; // This is a Bug ID. 319 320 private static final int MSG_WRITE = 300; 321 322 private static final String TAG = TAG_WITH_CLASS_NAME ? "CompatModePackages" : TAG_ATM; 323 324 // Compatibility state: no longer ask user to select the mode. 325 private static final int COMPAT_FLAG_DONT_ASK = 1 << 0; 326 327 // Compatibility state: compatibility mode is enabled. 328 private static final int COMPAT_FLAG_ENABLED = 1 << 1; 329 330 private final class CompatHandler extends Handler { CompatHandler(Looper looper)331 public CompatHandler(Looper looper) { 332 super(looper, null, true); 333 } 334 335 @Override handleMessage(Message msg)336 public void handleMessage(Message msg) { 337 switch (msg.what) { 338 case MSG_WRITE: 339 saveCompatModes(); 340 break; 341 } 342 } 343 } 344 345 private final ActivityTaskManagerService mService; 346 private final AtomicFile mFile; 347 private final HashMap<String, Integer> mPackages = new HashMap<>(); 348 private final SparseBooleanArray mLegacyScreenCompatPackages = new SparseBooleanArray(); 349 private final CompatHandler mHandler; 350 351 private final SparseArray<CompatScaleProvider> mProviders = new SparseArray<>(); 352 CompatModePackages(ActivityTaskManagerService service, File systemDir, Handler handler)353 public CompatModePackages(ActivityTaskManagerService service, File systemDir, Handler handler) { 354 mService = service; 355 mFile = new AtomicFile(new File(systemDir, "packages-compat.xml"), "compat-mode"); 356 mHandler = new CompatHandler(handler.getLooper()); 357 358 FileInputStream fis = null; 359 try { 360 fis = mFile.openRead(); 361 TypedXmlPullParser parser = Xml.resolvePullParser(fis); 362 int eventType = parser.getEventType(); 363 while (eventType != XmlPullParser.START_TAG && 364 eventType != XmlPullParser.END_DOCUMENT) { 365 eventType = parser.next(); 366 } 367 if (eventType == XmlPullParser.END_DOCUMENT) { 368 return; 369 } 370 371 String tagName = parser.getName(); 372 if ("compat-packages".equals(tagName)) { 373 eventType = parser.next(); 374 do { 375 if (eventType == XmlPullParser.START_TAG) { 376 tagName = parser.getName(); 377 if (parser.getDepth() == 2) { 378 if ("pkg".equals(tagName)) { 379 String pkg = parser.getAttributeValue(null, "name"); 380 if (pkg != null) { 381 int modeInt = parser.getAttributeInt(null, "mode", 0); 382 mPackages.put(pkg, modeInt); 383 } 384 } 385 } 386 } 387 eventType = parser.next(); 388 } while (eventType != XmlPullParser.END_DOCUMENT); 389 } 390 } catch (XmlPullParserException e) { 391 Slog.w(TAG, "Error reading compat-packages", e); 392 } catch (java.io.IOException e) { 393 if (fis != null) Slog.w(TAG, "Error reading compat-packages", e); 394 } finally { 395 if (fis != null) { 396 try { 397 fis.close(); 398 } catch (java.io.IOException e1) { 399 } 400 } 401 } 402 } 403 getPackages()404 public HashMap<String, Integer> getPackages() { 405 return mPackages; 406 } 407 getPackageFlags(String packageName)408 private int getPackageFlags(String packageName) { 409 Integer flags = mPackages.get(packageName); 410 return flags != null ? flags : 0; 411 } 412 handlePackageDataClearedLocked(String packageName)413 public void handlePackageDataClearedLocked(String packageName) { 414 // User has explicitly asked to clear all associated data. 415 removePackage(packageName); 416 } 417 handlePackageUninstalledLocked(String packageName)418 public void handlePackageUninstalledLocked(String packageName) { 419 // Clear settings when app is uninstalled since this is an explicit 420 // signal from the user to remove the app and all associated data. 421 removePackage(packageName); 422 } 423 removePackage(String packageName)424 private void removePackage(String packageName) { 425 if (mPackages.containsKey(packageName)) { 426 mPackages.remove(packageName); 427 scheduleWrite(); 428 } 429 mLegacyScreenCompatPackages.delete(packageName.hashCode()); 430 } 431 handlePackageAddedLocked(String packageName, boolean updated)432 public void handlePackageAddedLocked(String packageName, boolean updated) { 433 ApplicationInfo ai = null; 434 try { 435 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0); 436 } catch (RemoteException e) { 437 } 438 if (ai == null) { 439 return; 440 } 441 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai); 442 final boolean mayCompat = !ci.alwaysSupportsScreen() 443 && !ci.neverSupportsScreen(); 444 445 if (updated) { 446 // Update -- if the app no longer can run in compat mode, clear 447 // any current settings for it. 448 if (!mayCompat && mPackages.containsKey(packageName)) { 449 mPackages.remove(packageName); 450 scheduleWrite(); 451 } 452 } 453 } 454 scheduleWrite()455 private void scheduleWrite() { 456 mHandler.removeMessages(MSG_WRITE); 457 Message msg = mHandler.obtainMessage(MSG_WRITE); 458 mHandler.sendMessageDelayed(msg, 10000); 459 } 460 461 /** 462 * Returns {@code true} if the windows belonging to the package should be scaled with 463 * {@link DisplayContent#mCompatibleScreenScale}. 464 */ useLegacyScreenCompatMode(String packageName)465 boolean useLegacyScreenCompatMode(String packageName) { 466 if (mLegacyScreenCompatPackages.size() == 0) { 467 return false; 468 } 469 return mLegacyScreenCompatPackages.get(packageName.hashCode()); 470 } 471 compatibilityInfoForPackageLocked(ApplicationInfo ai)472 public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) { 473 final boolean forceCompat = getPackageCompatModeEnabledLocked(ai); 474 final CompatScale compatScale = getCompatScaleFromProvider(ai.packageName, ai.uid); 475 final float appScale = compatScale != null 476 ? compatScale.mScaleFactor 477 : getCompatScale(ai.packageName, ai.uid, /* checkProvider= */ false); 478 final float densityScale = compatScale != null ? compatScale.mDensityScaleFactor : appScale; 479 final Configuration config = mService.getGlobalConfiguration(); 480 final CompatibilityInfo info = new CompatibilityInfo(ai, config.screenLayout, 481 config.smallestScreenWidthDp, forceCompat, appScale, densityScale); 482 // Ignore invalid info which may be a placeholder of isolated process. 483 if (ai.flags != 0 && ai.sourceDir != null) { 484 if (!info.supportsScreen() && !"android".equals(ai.packageName)) { 485 Slog.i(TAG, "Use legacy screen compat mode: " + ai.packageName); 486 mLegacyScreenCompatPackages.put(ai.packageName.hashCode(), true); 487 } else if (mLegacyScreenCompatPackages.size() > 0) { 488 mLegacyScreenCompatPackages.delete(ai.packageName.hashCode()); 489 } 490 } 491 return info; 492 } 493 getCompatScale(String packageName, int uid)494 float getCompatScale(String packageName, int uid) { 495 return getCompatScale(packageName, uid, /* checkProvider= */ true); 496 } 497 getCompatScaleFromProvider(String packageName, int uid)498 private CompatScale getCompatScaleFromProvider(String packageName, int uid) { 499 for (int i = 0; i < mProviders.size(); i++) { 500 final CompatScaleProvider provider = mProviders.valueAt(i); 501 final CompatScale compatScale = provider.getCompatScale(packageName, uid); 502 if (compatScale != null) { 503 return compatScale; 504 } 505 } 506 return null; 507 } 508 getCompatScale(String packageName, int uid, boolean checkProviders)509 private float getCompatScale(String packageName, int uid, boolean checkProviders) { 510 if (checkProviders) { 511 final CompatScale compatScale = getCompatScaleFromProvider(packageName, uid); 512 if (compatScale != null) { 513 return compatScale.mScaleFactor; 514 } 515 } 516 final UserHandle userHandle = UserHandle.getUserHandleForUid(uid); 517 518 final boolean isDownscaledEnabled = CompatChanges.isChangeEnabled( 519 DOWNSCALED, packageName, userHandle); 520 final boolean isDownscaledInverseEnabled = CompatChanges.isChangeEnabled( 521 DOWNSCALED_INVERSE, packageName, userHandle); 522 if (isDownscaledEnabled || isDownscaledInverseEnabled) { 523 final float scalingFactor = getScalingFactor(packageName, userHandle); 524 if (scalingFactor != 1f) { 525 // For Upscaling the returned factor must be scalingFactor 526 // For Downscaling the returned factor must be 1f / scalingFactor 527 return isDownscaledInverseEnabled ? scalingFactor : 1f / scalingFactor; 528 } 529 } 530 531 if (mService.mHasLeanbackFeature) { 532 final Configuration config = mService.getGlobalConfiguration(); 533 final float density = config.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT; 534 final int smallestScreenWidthPx = (int) (config.smallestScreenWidthDp * density + .5f); 535 if (smallestScreenWidthPx > 1080 && !CompatChanges.isChangeEnabled( 536 DO_NOT_DOWNSCALE_TO_1080P_ON_TV, packageName, userHandle)) { 537 return smallestScreenWidthPx / 1080f; 538 } 539 } 540 541 return 1f; 542 } 543 registerCompatScaleProvider(@ompatScaleProvider.CompatScaleModeOrderId int id, @NonNull CompatScaleProvider provider)544 void registerCompatScaleProvider(@CompatScaleProvider.CompatScaleModeOrderId int id, 545 @NonNull CompatScaleProvider provider) { 546 synchronized (mService.mGlobalLock) { 547 if (mProviders.contains(id)) { 548 throw new IllegalArgumentException("Duplicate id provided: " + id); 549 } 550 if (provider == null) { 551 throw new IllegalArgumentException("The passed CompatScaleProvider " 552 + "can not be null"); 553 } 554 if (!CompatScaleProvider.isValidOrderId(id)) { 555 throw new IllegalArgumentException( 556 "Provided id " + id + " is not in range of valid ids for system " 557 + "services [" + COMPAT_SCALE_MODE_SYSTEM_FIRST + "," 558 + COMPAT_SCALE_MODE_SYSTEM_LAST + "]"); 559 } 560 mProviders.put(id, provider); 561 } 562 } 563 unregisterCompatScaleProvider(@ompatScaleProvider.CompatScaleModeOrderId int id)564 void unregisterCompatScaleProvider(@CompatScaleProvider.CompatScaleModeOrderId int id) { 565 synchronized (mService.mGlobalLock) { 566 if (!mProviders.contains(id)) { 567 throw new IllegalArgumentException( 568 "CompatScaleProvider with id (" + id + ") is not registered"); 569 } 570 mProviders.remove(id); 571 } 572 } 573 getScalingFactor(String packageName, UserHandle userHandle)574 private static float getScalingFactor(String packageName, UserHandle userHandle) { 575 if (CompatChanges.isChangeEnabled(DOWNSCALE_90, packageName, userHandle)) { 576 return 0.9f; 577 } 578 if (CompatChanges.isChangeEnabled(DOWNSCALE_85, packageName, userHandle)) { 579 return 0.85f; 580 } 581 if (CompatChanges.isChangeEnabled(DOWNSCALE_80, packageName, userHandle)) { 582 return 0.8f; 583 } 584 if (CompatChanges.isChangeEnabled(DOWNSCALE_75, packageName, userHandle)) { 585 return 0.75f; 586 } 587 if (CompatChanges.isChangeEnabled(DOWNSCALE_70, packageName, userHandle)) { 588 return 0.7f; 589 } 590 if (CompatChanges.isChangeEnabled(DOWNSCALE_65, packageName, userHandle)) { 591 return 0.65f; 592 } 593 if (CompatChanges.isChangeEnabled(DOWNSCALE_60, packageName, userHandle)) { 594 return 0.6f; 595 } 596 if (CompatChanges.isChangeEnabled(DOWNSCALE_55, packageName, userHandle)) { 597 return 0.55f; 598 } 599 if (CompatChanges.isChangeEnabled(DOWNSCALE_50, packageName, userHandle)) { 600 return 0.5f; 601 } 602 if (CompatChanges.isChangeEnabled(DOWNSCALE_45, packageName, userHandle)) { 603 return 0.45f; 604 } 605 if (CompatChanges.isChangeEnabled(DOWNSCALE_40, packageName, userHandle)) { 606 return 0.4f; 607 } 608 if (CompatChanges.isChangeEnabled(DOWNSCALE_35, packageName, userHandle)) { 609 return 0.35f; 610 } 611 if (CompatChanges.isChangeEnabled(DOWNSCALE_30, packageName, userHandle)) { 612 return 0.3f; 613 } 614 return 1f; 615 } 616 computeCompatModeLocked(ApplicationInfo ai)617 public int computeCompatModeLocked(ApplicationInfo ai) { 618 final CompatibilityInfo info = compatibilityInfoForPackageLocked(ai); 619 if (info.alwaysSupportsScreen()) { 620 return ActivityManager.COMPAT_MODE_NEVER; 621 } 622 if (info.neverSupportsScreen()) { 623 return ActivityManager.COMPAT_MODE_ALWAYS; 624 } 625 return getPackageCompatModeEnabledLocked(ai) ? ActivityManager.COMPAT_MODE_ENABLED 626 : ActivityManager.COMPAT_MODE_DISABLED; 627 } 628 getPackageAskCompatModeLocked(String packageName)629 public boolean getPackageAskCompatModeLocked(String packageName) { 630 return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0; 631 } 632 setPackageAskCompatModeLocked(String packageName, boolean ask)633 public void setPackageAskCompatModeLocked(String packageName, boolean ask) { 634 setPackageFlagLocked(packageName, COMPAT_FLAG_DONT_ASK, ask); 635 } 636 getPackageCompatModeEnabledLocked(ApplicationInfo ai)637 private boolean getPackageCompatModeEnabledLocked(ApplicationInfo ai) { 638 return (getPackageFlags(ai.packageName) & COMPAT_FLAG_ENABLED) != 0; 639 } 640 setPackageFlagLocked(String packageName, int flag, boolean set)641 private void setPackageFlagLocked(String packageName, int flag, boolean set) { 642 final int curFlags = getPackageFlags(packageName); 643 final int newFlags = set ? (curFlags & ~flag) : (curFlags | flag); 644 if (curFlags != newFlags) { 645 if (newFlags != 0) { 646 mPackages.put(packageName, newFlags); 647 } else { 648 mPackages.remove(packageName); 649 } 650 scheduleWrite(); 651 } 652 } 653 getPackageScreenCompatModeLocked(String packageName)654 public int getPackageScreenCompatModeLocked(String packageName) { 655 ApplicationInfo ai = null; 656 try { 657 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0); 658 } catch (RemoteException e) { 659 } 660 if (ai == null) { 661 return ActivityManager.COMPAT_MODE_UNKNOWN; 662 } 663 return computeCompatModeLocked(ai); 664 } 665 setPackageScreenCompatModeLocked(String packageName, int mode)666 public void setPackageScreenCompatModeLocked(String packageName, int mode) { 667 ApplicationInfo ai = null; 668 try { 669 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0); 670 } catch (RemoteException e) { 671 } 672 if (ai == null) { 673 Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName); 674 return; 675 } 676 setPackageScreenCompatModeLocked(ai, mode); 677 } 678 setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode)679 void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) { 680 final String packageName = ai.packageName; 681 682 int curFlags = getPackageFlags(packageName); 683 684 boolean enable; 685 switch (mode) { 686 case ActivityManager.COMPAT_MODE_DISABLED: 687 enable = false; 688 break; 689 case ActivityManager.COMPAT_MODE_ENABLED: 690 enable = true; 691 break; 692 case ActivityManager.COMPAT_MODE_TOGGLE: 693 enable = (curFlags&COMPAT_FLAG_ENABLED) == 0; 694 break; 695 default: 696 Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring"); 697 return; 698 } 699 700 int newFlags = curFlags; 701 if (enable) { 702 newFlags |= COMPAT_FLAG_ENABLED; 703 } else { 704 newFlags &= ~COMPAT_FLAG_ENABLED; 705 } 706 707 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai); 708 if (ci.alwaysSupportsScreen()) { 709 Slog.w(TAG, "Ignoring compat mode change of " + packageName 710 + "; compatibility never needed"); 711 newFlags = 0; 712 } 713 if (ci.neverSupportsScreen()) { 714 Slog.w(TAG, "Ignoring compat mode change of " + packageName 715 + "; compatibility always needed"); 716 newFlags = 0; 717 } 718 719 if (newFlags != curFlags) { 720 if (newFlags != 0) { 721 mPackages.put(packageName, newFlags); 722 } else { 723 mPackages.remove(packageName); 724 } 725 726 // Need to get compatibility info in new state. 727 ci = compatibilityInfoForPackageLocked(ai); 728 729 scheduleWrite(); 730 731 final ArrayList<WindowProcessController> restartedApps = new ArrayList<>(); 732 mService.mRootWindowContainer.forAllWindows(w -> { 733 final ActivityRecord ar = w.mActivityRecord; 734 if (ar != null) { 735 if (ar.packageName.equals(packageName) && !restartedApps.contains(ar.app)) { 736 ar.restartProcessIfVisible(); 737 restartedApps.add(ar.app); 738 } 739 } else if (w.getProcess().mInfo.packageName.equals(packageName)) { 740 w.updateGlobalScale(); 741 } 742 }, true /* traverseTopToBottom */); 743 // Tell all processes that loaded this package about the change. 744 SparseArray<WindowProcessController> pidMap = mService.mProcessMap.getPidMap(); 745 for (int i = pidMap.size() - 1; i >= 0; i--) { 746 final WindowProcessController app = pidMap.valueAt(i); 747 if (!app.containsPackage(packageName) || restartedApps.contains(app)) { 748 continue; 749 } 750 try { 751 if (app.hasThread()) { 752 ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s " 753 + "new compat %s", app.mName, ci); 754 app.getThread().updatePackageCompatibilityInfo(packageName, ci); 755 } 756 } catch (Exception e) { 757 } 758 } 759 } 760 } 761 saveCompatModes()762 private void saveCompatModes() { 763 HashMap<String, Integer> pkgs; 764 synchronized (mService.mGlobalLock) { 765 pkgs = new HashMap<>(mPackages); 766 } 767 768 FileOutputStream fos = null; 769 770 try { 771 fos = mFile.startWrite(); 772 TypedXmlSerializer out = Xml.resolveSerializer(fos); 773 out.startDocument(null, true); 774 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 775 out.startTag(null, "compat-packages"); 776 777 final IPackageManager pm = AppGlobals.getPackageManager(); 778 final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator(); 779 while (it.hasNext()) { 780 Map.Entry<String, Integer> entry = it.next(); 781 String pkg = entry.getKey(); 782 int mode = entry.getValue(); 783 if (mode == 0) { 784 continue; 785 } 786 ApplicationInfo ai = null; 787 try { 788 ai = pm.getApplicationInfo(pkg, 0, 0); 789 } catch (RemoteException e) { 790 } 791 if (ai == null) { 792 continue; 793 } 794 final CompatibilityInfo info = compatibilityInfoForPackageLocked(ai); 795 if (info.alwaysSupportsScreen()) { 796 continue; 797 } 798 if (info.neverSupportsScreen()) { 799 continue; 800 } 801 out.startTag(null, "pkg"); 802 out.attribute(null, "name", pkg); 803 out.attributeInt(null, "mode", mode); 804 out.endTag(null, "pkg"); 805 } 806 807 out.endTag(null, "compat-packages"); 808 out.endDocument(); 809 810 mFile.finishWrite(fos); 811 } catch (java.io.IOException e1) { 812 Slog.w(TAG, "Error writing compat packages", e1); 813 if (fos != null) { 814 mFile.failWrite(fos); 815 } 816 } 817 } 818 } 819