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