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