• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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