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