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.POSTFIX_CONFIGURATION; 21 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; 22 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; 23 import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; 24 25 import android.app.ActivityManager; 26 import android.app.AppGlobals; 27 import android.app.compat.CompatChanges; 28 import android.compat.annotation.ChangeId; 29 import android.compat.annotation.Disabled; 30 import android.compat.annotation.EnabledSince; 31 import android.compat.annotation.Overridable; 32 import android.content.pm.ApplicationInfo; 33 import android.content.pm.IPackageManager; 34 import android.content.res.CompatibilityInfo; 35 import android.content.res.Configuration; 36 import android.os.Build; 37 import android.os.Handler; 38 import android.os.Looper; 39 import android.os.Message; 40 import android.os.RemoteException; 41 import android.os.UserHandle; 42 import android.util.AtomicFile; 43 import android.util.DisplayMetrics; 44 import android.util.Slog; 45 import android.util.SparseArray; 46 import android.util.TypedXmlPullParser; 47 import android.util.TypedXmlSerializer; 48 import android.util.Xml; 49 50 import com.android.internal.protolog.common.ProtoLog; 51 52 import org.xmlpull.v1.XmlPullParser; 53 import org.xmlpull.v1.XmlPullParserException; 54 55 import java.io.File; 56 import java.io.FileInputStream; 57 import java.io.FileOutputStream; 58 import java.util.HashMap; 59 import java.util.Iterator; 60 import java.util.Map; 61 62 public final class CompatModePackages { 63 private static final String TAG = TAG_WITH_CLASS_NAME ? "CompatModePackages" : TAG_ATM; 64 private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION; 65 66 private final ActivityTaskManagerService mService; 67 private final AtomicFile mFile; 68 69 // Compatibility state: no longer ask user to select the mode. 70 private static final int COMPAT_FLAG_DONT_ASK = 1<<0; 71 // Compatibility state: compatibility mode is enabled. 72 private static final int COMPAT_FLAG_ENABLED = 1<<1; 73 74 /** 75 * CompatModePackages#DOWNSCALED is the gatekeeper of all per-app buffer downscaling 76 * changes. Disabling this change will prevent the following scaling factors from working: 77 * CompatModePackages#DOWNSCALE_90 78 * CompatModePackages#DOWNSCALE_85 79 * CompatModePackages#DOWNSCALE_80 80 * CompatModePackages#DOWNSCALE_75 81 * CompatModePackages#DOWNSCALE_70 82 * CompatModePackages#DOWNSCALE_65 83 * CompatModePackages#DOWNSCALE_60 84 * CompatModePackages#DOWNSCALE_55 85 * CompatModePackages#DOWNSCALE_50 86 * CompatModePackages#DOWNSCALE_45 87 * CompatModePackages#DOWNSCALE_40 88 * CompatModePackages#DOWNSCALE_35 89 * CompatModePackages#DOWNSCALE_30 90 * 91 * If CompatModePackages#DOWNSCALED is enabled for an app package, then the app will be forcibly 92 * resized to the highest enabled scaling factor e.g. 80% if both 80% and 70% were enabled. 93 */ 94 @ChangeId 95 @Disabled 96 @Overridable 97 public static final long DOWNSCALED = 168419799L; 98 99 /** 100 * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id 101 * CompatModePackages#DOWNSCALE_90 for a package will force the app to assume it's 102 * running on a display with 90% the vertical and horizontal resolution of the real display. 103 */ 104 @ChangeId 105 @Disabled 106 @Overridable 107 public static final long DOWNSCALE_90 = 182811243L; 108 109 /** 110 * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id 111 * CompatModePackages#DOWNSCALE_85 for a package will force the app to assume it's 112 * running on a display with 85% the vertical and horizontal resolution of the real display. 113 */ 114 @ChangeId 115 @Disabled 116 @Overridable 117 public static final long DOWNSCALE_85 = 189969734L; 118 119 /** 120 * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id 121 * CompatModePackages#DOWNSCALE_80 for a package will force the app to assume it's 122 * running on a display with 80% the vertical and horizontal resolution of the real display. 123 */ 124 @ChangeId 125 @Disabled 126 @Overridable 127 public static final long DOWNSCALE_80 = 176926753L; 128 129 /** 130 * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id 131 * CompatModePackages#DOWNSCALE_75 for a package will force the app to assume it's 132 * running on a display with 75% the vertical and horizontal resolution of the real display. 133 */ 134 @ChangeId 135 @Disabled 136 @Overridable 137 public static final long DOWNSCALE_75 = 189969779L; 138 139 /** 140 * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id 141 * CompatModePackages#DOWNSCALE_70 for a package will force the app to assume it's 142 * running on a display with 70% the vertical and horizontal resolution of the real display. 143 */ 144 @ChangeId 145 @Disabled 146 @Overridable 147 public static final long DOWNSCALE_70 = 176926829L; 148 149 /** 150 * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id 151 * CompatModePackages#DOWNSCALE_65 for a package will force the app to assume it's 152 * running on a display with 65% the vertical and horizontal resolution of the real display. 153 */ 154 @ChangeId 155 @Disabled 156 @Overridable 157 public static final long DOWNSCALE_65 = 189969744L; 158 159 /** 160 * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id 161 * CompatModePackages#DOWNSCALE_60 for a package will force the app to assume it's 162 * running on a display with 60% the vertical and horizontal resolution of the real display. 163 */ 164 @ChangeId 165 @Disabled 166 @Overridable 167 public static final long DOWNSCALE_60 = 176926771L; 168 169 /** 170 * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id 171 * CompatModePackages#DOWNSCALE_55 for a package will force the app to assume it's 172 * running on a display with 55% the vertical and horizontal resolution of the real display. 173 */ 174 @ChangeId 175 @Disabled 176 @Overridable 177 public static final long DOWNSCALE_55 = 189970036L; 178 179 /** 180 * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id 181 * CompatModePackages#DOWNSCALE_50 for a package will force the app to assume it's 182 * running on a display with 50% vertical and horizontal resolution of the real display. 183 */ 184 @ChangeId 185 @Disabled 186 @Overridable 187 public static final long DOWNSCALE_50 = 176926741L; 188 189 /** 190 * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id 191 * CompatModePackages#DOWNSCALE_45 for a package will force the app to assume it's 192 * running on a display with 45% the vertical and horizontal resolution of the real display. 193 */ 194 @ChangeId 195 @Disabled 196 @Overridable 197 public static final long DOWNSCALE_45 = 189969782L; 198 199 /** 200 * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id 201 * CompatModePackages#DOWNSCALE_40 for a package will force the app to assume it's 202 * running on a display with 40% the vertical and horizontal resolution of the real display. 203 */ 204 @ChangeId 205 @Disabled 206 @Overridable 207 public static final long DOWNSCALE_40 = 189970038L; 208 209 /** 210 * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id 211 * CompatModePackages#DOWNSCALE_35 for a package will force the app to assume it's 212 * running on a display with 35% the vertical and horizontal resolution of the real display. 213 */ 214 @ChangeId 215 @Disabled 216 @Overridable 217 public static final long DOWNSCALE_35 = 189969749L; 218 219 /** 220 * With CompatModePackages#DOWNSCALED enabled, subsequently enabling change-id 221 * CompatModePackages#DOWNSCALE_30 for a package will force the app to assume it's 222 * running on a display with 30% the vertical and horizontal resolution of the real display. 223 */ 224 @ChangeId 225 @Disabled 226 @Overridable 227 public static final long DOWNSCALE_30 = 189970040L; 228 229 /** 230 * On Android TV applications that target pre-S are not expecting to receive a Window larger 231 * than 1080p, so if needed we are downscaling their Windows to 1080p. 232 * However, applications that target S and greater release version are expected to be able to 233 * handle any Window size, so we should not downscale their Windows. 234 */ 235 @ChangeId 236 @Overridable 237 @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) 238 private static final long DO_NOT_DOWNSCALE_TO_1080P_ON_TV = 157629738L; // This is a Bug ID. 239 240 private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>(); 241 242 private static final int MSG_WRITE = 300; 243 244 private final CompatHandler mHandler; 245 246 private final class CompatHandler extends Handler { CompatHandler(Looper looper)247 public CompatHandler(Looper looper) { 248 super(looper, null, true); 249 } 250 251 @Override handleMessage(Message msg)252 public void handleMessage(Message msg) { 253 switch (msg.what) { 254 case MSG_WRITE: 255 saveCompatModes(); 256 break; 257 } 258 } 259 } 260 CompatModePackages(ActivityTaskManagerService service, File systemDir, Handler handler)261 public CompatModePackages(ActivityTaskManagerService service, File systemDir, Handler handler) { 262 mService = service; 263 mFile = new AtomicFile(new File(systemDir, "packages-compat.xml"), "compat-mode"); 264 mHandler = new CompatHandler(handler.getLooper()); 265 266 FileInputStream fis = null; 267 try { 268 fis = mFile.openRead(); 269 TypedXmlPullParser parser = Xml.resolvePullParser(fis); 270 int eventType = parser.getEventType(); 271 while (eventType != XmlPullParser.START_TAG && 272 eventType != XmlPullParser.END_DOCUMENT) { 273 eventType = parser.next(); 274 } 275 if (eventType == XmlPullParser.END_DOCUMENT) { 276 return; 277 } 278 279 String tagName = parser.getName(); 280 if ("compat-packages".equals(tagName)) { 281 eventType = parser.next(); 282 do { 283 if (eventType == XmlPullParser.START_TAG) { 284 tagName = parser.getName(); 285 if (parser.getDepth() == 2) { 286 if ("pkg".equals(tagName)) { 287 String pkg = parser.getAttributeValue(null, "name"); 288 if (pkg != null) { 289 int modeInt = parser.getAttributeInt(null, "mode", 0); 290 mPackages.put(pkg, modeInt); 291 } 292 } 293 } 294 } 295 eventType = parser.next(); 296 } while (eventType != XmlPullParser.END_DOCUMENT); 297 } 298 } catch (XmlPullParserException e) { 299 Slog.w(TAG, "Error reading compat-packages", e); 300 } catch (java.io.IOException e) { 301 if (fis != null) Slog.w(TAG, "Error reading compat-packages", e); 302 } finally { 303 if (fis != null) { 304 try { 305 fis.close(); 306 } catch (java.io.IOException e1) { 307 } 308 } 309 } 310 } 311 getPackages()312 public HashMap<String, Integer> getPackages() { 313 return mPackages; 314 } 315 getPackageFlags(String packageName)316 private int getPackageFlags(String packageName) { 317 Integer flags = mPackages.get(packageName); 318 return flags != null ? flags : 0; 319 } 320 handlePackageDataClearedLocked(String packageName)321 public void handlePackageDataClearedLocked(String packageName) { 322 // User has explicitly asked to clear all associated data. 323 removePackage(packageName); 324 } 325 handlePackageUninstalledLocked(String packageName)326 public void handlePackageUninstalledLocked(String packageName) { 327 // Clear settings when app is uninstalled since this is an explicit 328 // signal from the user to remove the app and all associated data. 329 removePackage(packageName); 330 } 331 removePackage(String packageName)332 private void removePackage(String packageName) { 333 if (mPackages.containsKey(packageName)) { 334 mPackages.remove(packageName); 335 scheduleWrite(); 336 } 337 } 338 handlePackageAddedLocked(String packageName, boolean updated)339 public void handlePackageAddedLocked(String packageName, boolean updated) { 340 ApplicationInfo ai = null; 341 try { 342 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0); 343 } catch (RemoteException e) { 344 } 345 if (ai == null) { 346 return; 347 } 348 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai); 349 final boolean mayCompat = !ci.alwaysSupportsScreen() 350 && !ci.neverSupportsScreen(); 351 352 if (updated) { 353 // Update -- if the app no longer can run in compat mode, clear 354 // any current settings for it. 355 if (!mayCompat && mPackages.containsKey(packageName)) { 356 mPackages.remove(packageName); 357 scheduleWrite(); 358 } 359 } 360 } 361 scheduleWrite()362 private void scheduleWrite() { 363 mHandler.removeMessages(MSG_WRITE); 364 Message msg = mHandler.obtainMessage(MSG_WRITE); 365 mHandler.sendMessageDelayed(msg, 10000); 366 } 367 compatibilityInfoForPackageLocked(ApplicationInfo ai)368 public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) { 369 final boolean forceCompat = getPackageCompatModeEnabledLocked(ai); 370 final float compatScale = getCompatScale(ai.packageName, ai.uid); 371 final Configuration config = mService.getGlobalConfiguration(); 372 return new CompatibilityInfo(ai, config.screenLayout, config.smallestScreenWidthDp, 373 forceCompat, compatScale); 374 } 375 getCompatScale(String packageName, int uid)376 float getCompatScale(String packageName, int uid) { 377 final UserHandle userHandle = UserHandle.getUserHandleForUid(uid); 378 if (CompatChanges.isChangeEnabled(DOWNSCALED, packageName, userHandle)) { 379 if (CompatChanges.isChangeEnabled(DOWNSCALE_90, packageName, userHandle)) { 380 return 1f / 0.9f; 381 } 382 if (CompatChanges.isChangeEnabled(DOWNSCALE_85, packageName, userHandle)) { 383 return 1f / 0.85f; 384 } 385 if (CompatChanges.isChangeEnabled(DOWNSCALE_80, packageName, userHandle)) { 386 return 1f / 0.8f; 387 } 388 if (CompatChanges.isChangeEnabled(DOWNSCALE_75, packageName, userHandle)) { 389 return 1f / 0.75f; 390 } 391 if (CompatChanges.isChangeEnabled(DOWNSCALE_70, packageName, userHandle)) { 392 return 1f / 0.7f; 393 } 394 if (CompatChanges.isChangeEnabled(DOWNSCALE_65, packageName, userHandle)) { 395 return 1f / 0.65f; 396 } 397 if (CompatChanges.isChangeEnabled(DOWNSCALE_60, packageName, userHandle)) { 398 return 1f / 0.6f; 399 } 400 if (CompatChanges.isChangeEnabled(DOWNSCALE_55, packageName, userHandle)) { 401 return 1f / 0.55f; 402 } 403 if (CompatChanges.isChangeEnabled(DOWNSCALE_50, packageName, userHandle)) { 404 return 1f / 0.5f; 405 } 406 if (CompatChanges.isChangeEnabled(DOWNSCALE_45, packageName, userHandle)) { 407 return 1f / 0.45f; 408 } 409 if (CompatChanges.isChangeEnabled(DOWNSCALE_40, packageName, userHandle)) { 410 return 1f / 0.4f; 411 } 412 if (CompatChanges.isChangeEnabled(DOWNSCALE_35, packageName, userHandle)) { 413 return 1f / 0.35f; 414 } 415 if (CompatChanges.isChangeEnabled(DOWNSCALE_30, packageName, userHandle)) { 416 return 1f / 0.3f; 417 } 418 } 419 420 if (mService.mHasLeanbackFeature) { 421 final Configuration config = mService.getGlobalConfiguration(); 422 final float density = config.densityDpi / (float) DisplayMetrics.DENSITY_DEFAULT; 423 final int smallestScreenWidthPx = (int) (config.smallestScreenWidthDp * density + .5f); 424 if (smallestScreenWidthPx > 1080 && !CompatChanges.isChangeEnabled( 425 DO_NOT_DOWNSCALE_TO_1080P_ON_TV, packageName, userHandle)) { 426 return smallestScreenWidthPx / 1080f; 427 } 428 } 429 430 return 1f; 431 } 432 computeCompatModeLocked(ApplicationInfo ai)433 public int computeCompatModeLocked(ApplicationInfo ai) { 434 final CompatibilityInfo info = compatibilityInfoForPackageLocked(ai); 435 if (info.alwaysSupportsScreen()) { 436 return ActivityManager.COMPAT_MODE_NEVER; 437 } 438 if (info.neverSupportsScreen()) { 439 return ActivityManager.COMPAT_MODE_ALWAYS; 440 } 441 return getPackageCompatModeEnabledLocked(ai) ? ActivityManager.COMPAT_MODE_ENABLED 442 : ActivityManager.COMPAT_MODE_DISABLED; 443 } 444 getPackageAskCompatModeLocked(String packageName)445 public boolean getPackageAskCompatModeLocked(String packageName) { 446 return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0; 447 } 448 setPackageAskCompatModeLocked(String packageName, boolean ask)449 public void setPackageAskCompatModeLocked(String packageName, boolean ask) { 450 setPackageFlagLocked(packageName, COMPAT_FLAG_DONT_ASK, ask); 451 } 452 getPackageCompatModeEnabledLocked(ApplicationInfo ai)453 private boolean getPackageCompatModeEnabledLocked(ApplicationInfo ai) { 454 return (getPackageFlags(ai.packageName) & COMPAT_FLAG_ENABLED) != 0; 455 } 456 setPackageFlagLocked(String packageName, int flag, boolean set)457 private void setPackageFlagLocked(String packageName, int flag, boolean set) { 458 final int curFlags = getPackageFlags(packageName); 459 final int newFlags = set ? (curFlags & ~flag) : (curFlags | flag); 460 if (curFlags != newFlags) { 461 if (newFlags != 0) { 462 mPackages.put(packageName, newFlags); 463 } else { 464 mPackages.remove(packageName); 465 } 466 scheduleWrite(); 467 } 468 } 469 getPackageScreenCompatModeLocked(String packageName)470 public int getPackageScreenCompatModeLocked(String packageName) { 471 ApplicationInfo ai = null; 472 try { 473 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0); 474 } catch (RemoteException e) { 475 } 476 if (ai == null) { 477 return ActivityManager.COMPAT_MODE_UNKNOWN; 478 } 479 return computeCompatModeLocked(ai); 480 } 481 setPackageScreenCompatModeLocked(String packageName, int mode)482 public void setPackageScreenCompatModeLocked(String packageName, int mode) { 483 ApplicationInfo ai = null; 484 try { 485 ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0); 486 } catch (RemoteException e) { 487 } 488 if (ai == null) { 489 Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName); 490 return; 491 } 492 setPackageScreenCompatModeLocked(ai, mode); 493 } 494 setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode)495 void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) { 496 final String packageName = ai.packageName; 497 498 int curFlags = getPackageFlags(packageName); 499 500 boolean enable; 501 switch (mode) { 502 case ActivityManager.COMPAT_MODE_DISABLED: 503 enable = false; 504 break; 505 case ActivityManager.COMPAT_MODE_ENABLED: 506 enable = true; 507 break; 508 case ActivityManager.COMPAT_MODE_TOGGLE: 509 enable = (curFlags&COMPAT_FLAG_ENABLED) == 0; 510 break; 511 default: 512 Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring"); 513 return; 514 } 515 516 int newFlags = curFlags; 517 if (enable) { 518 newFlags |= COMPAT_FLAG_ENABLED; 519 } else { 520 newFlags &= ~COMPAT_FLAG_ENABLED; 521 } 522 523 CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai); 524 if (ci.alwaysSupportsScreen()) { 525 Slog.w(TAG, "Ignoring compat mode change of " + packageName 526 + "; compatibility never needed"); 527 newFlags = 0; 528 } 529 if (ci.neverSupportsScreen()) { 530 Slog.w(TAG, "Ignoring compat mode change of " + packageName 531 + "; compatibility always needed"); 532 newFlags = 0; 533 } 534 535 if (newFlags != curFlags) { 536 if (newFlags != 0) { 537 mPackages.put(packageName, newFlags); 538 } else { 539 mPackages.remove(packageName); 540 } 541 542 // Need to get compatibility info in new state. 543 ci = compatibilityInfoForPackageLocked(ai); 544 545 scheduleWrite(); 546 547 final Task rootTask = mService.getTopDisplayFocusedRootTask(); 548 ActivityRecord starting = rootTask.restartPackage(packageName); 549 550 // Tell all processes that loaded this package about the change. 551 SparseArray<WindowProcessController> pidMap = mService.mProcessMap.getPidMap(); 552 for (int i = pidMap.size() - 1; i >= 0; i--) { 553 final WindowProcessController app = pidMap.valueAt(i); 554 if (!app.mPkgList.contains(packageName)) { 555 continue; 556 } 557 try { 558 if (app.hasThread()) { 559 ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s " 560 + "new compat %s", app.mName, ci); 561 app.getThread().updatePackageCompatibilityInfo(packageName, ci); 562 } 563 } catch (Exception e) { 564 } 565 } 566 567 if (starting != null) { 568 starting.ensureActivityConfiguration(0 /* globalChanges */, 569 false /* preserveWindow */); 570 // And we need to make sure at this point that all other activities 571 // are made visible with the correct configuration. 572 rootTask.ensureActivitiesVisible(starting, 0, !PRESERVE_WINDOWS); 573 } 574 } 575 } 576 saveCompatModes()577 private void saveCompatModes() { 578 HashMap<String, Integer> pkgs; 579 synchronized (mService.mGlobalLock) { 580 pkgs = new HashMap<>(mPackages); 581 } 582 583 FileOutputStream fos = null; 584 585 try { 586 fos = mFile.startWrite(); 587 TypedXmlSerializer out = Xml.resolveSerializer(fos); 588 out.startDocument(null, true); 589 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 590 out.startTag(null, "compat-packages"); 591 592 final IPackageManager pm = AppGlobals.getPackageManager(); 593 final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator(); 594 while (it.hasNext()) { 595 Map.Entry<String, Integer> entry = it.next(); 596 String pkg = entry.getKey(); 597 int mode = entry.getValue(); 598 if (mode == 0) { 599 continue; 600 } 601 ApplicationInfo ai = null; 602 try { 603 ai = pm.getApplicationInfo(pkg, 0, 0); 604 } catch (RemoteException e) { 605 } 606 if (ai == null) { 607 continue; 608 } 609 final CompatibilityInfo info = compatibilityInfoForPackageLocked(ai); 610 if (info.alwaysSupportsScreen()) { 611 continue; 612 } 613 if (info.neverSupportsScreen()) { 614 continue; 615 } 616 out.startTag(null, "pkg"); 617 out.attribute(null, "name", pkg); 618 out.attributeInt(null, "mode", mode); 619 out.endTag(null, "pkg"); 620 } 621 622 out.endTag(null, "compat-packages"); 623 out.endDocument(); 624 625 mFile.finishWrite(fos); 626 } catch (java.io.IOException e1) { 627 Slog.w(TAG, "Error writing compat packages", e1); 628 if (fos != null) { 629 mFile.failWrite(fos); 630 } 631 } 632 } 633 } 634