10f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornpackage com.android.server.am; 20f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 30f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport java.io.File; 40f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport java.io.FileInputStream; 50f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport java.io.FileOutputStream; 636cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackbornimport java.util.HashMap; 70f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport java.util.HashSet; 80f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport java.util.Iterator; 936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackbornimport java.util.Map; 100f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 110f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport org.xmlpull.v1.XmlPullParser; 120f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport org.xmlpull.v1.XmlPullParserException; 130f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport org.xmlpull.v1.XmlSerializer; 140f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 150f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport com.android.internal.os.AtomicFile; 160f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport com.android.internal.util.FastXmlSerializer; 170f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 180f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.app.ActivityManager; 190f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.app.AppGlobals; 208ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackbornimport android.content.pm.ActivityInfo; 210f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.content.pm.ApplicationInfo; 220f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.content.pm.IPackageManager; 230f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.content.res.CompatibilityInfo; 240f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.os.Handler; 250f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.os.Message; 260f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.os.RemoteException; 270f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.util.Slog; 280f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.util.Xml; 290f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 300f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornpublic class CompatModePackages { 310f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn private final String TAG = ActivityManagerService.TAG; 320f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn private final boolean DEBUG_CONFIGURATION = ActivityManagerService.DEBUG_CONFIGURATION; 330f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 340f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn private final ActivityManagerService mService; 350f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn private final AtomicFile mFile; 360f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 3736cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn // Compatibility state: no longer ask user to select the mode. 3836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn public static final int COMPAT_FLAG_DONT_ASK = 1<<0; 3936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn // Compatibility state: compatibility mode is enabled. 4036cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn public static final int COMPAT_FLAG_ENABLED = 1<<1; 4136cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 4236cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>(); 430f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 440f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn private static final int MSG_WRITE = 1; 450f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 460f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn private final Handler mHandler = new Handler() { 470f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn @Override public void handleMessage(Message msg) { 480f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn switch (msg.what) { 490f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn case MSG_WRITE: 500f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn saveCompatModes(); 510f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn break; 520f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn default: 530f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn super.handleMessage(msg); 540f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn break; 550f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 560f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 570f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn }; 580f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 590f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn public CompatModePackages(ActivityManagerService service, File systemDir) { 600f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mService = service; 610f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mFile = new AtomicFile(new File(systemDir, "packages-compat.xml")); 620f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 630f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn FileInputStream fis = null; 640f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn try { 650f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn fis = mFile.openRead(); 660f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn XmlPullParser parser = Xml.newPullParser(); 670f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn parser.setInput(fis, null); 680f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn int eventType = parser.getEventType(); 690f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn while (eventType != XmlPullParser.START_TAG) { 700f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn eventType = parser.next(); 710f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 720f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn String tagName = parser.getName(); 730f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if ("compat-packages".equals(tagName)) { 740f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn eventType = parser.next(); 750f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn do { 760f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (eventType == XmlPullParser.START_TAG) { 770f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn tagName = parser.getName(); 780f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (parser.getDepth() == 2) { 790f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if ("pkg".equals(tagName)) { 800f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn String pkg = parser.getAttributeValue(null, "name"); 810f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (pkg != null) { 8236cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn String mode = parser.getAttributeValue(null, "mode"); 8336cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn int modeInt = 0; 8436cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn if (mode != null) { 8536cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn try { 8636cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn modeInt = Integer.parseInt(mode); 8736cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } catch (NumberFormatException e) { 8836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 8936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 9036cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn mPackages.put(pkg, modeInt); 910f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 920f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 930f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 940f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 950f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn eventType = parser.next(); 960f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } while (eventType != XmlPullParser.END_DOCUMENT); 970f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 980f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } catch (XmlPullParserException e) { 990f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn Slog.w(TAG, "Error reading compat-packages", e); 1000f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } catch (java.io.IOException e) { 1010f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (fis != null) Slog.w(TAG, "Error reading compat-packages", e); 1020f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } finally { 1030f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (fis != null) { 1040f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn try { 1050f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn fis.close(); 1060f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } catch (java.io.IOException e1) { 1070f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1080f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1090f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1100f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1110f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 11236cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn public HashMap<String, Integer> getPackages() { 1130f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return mPackages; 1140f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1150f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 11636cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn private int getPackageFlags(String packageName) { 11736cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn Integer flags = mPackages.get(packageName); 11836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn return flags != null ? flags : 0; 11936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 12036cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 1218ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn public void handlePackageAddedLocked(String packageName, boolean updated) { 1228ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn ApplicationInfo ai = null; 1238ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn try { 1248ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0); 1258ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } catch (RemoteException e) { 1268ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } 1278ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn if (ai == null) { 1288ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn return; 1298ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } 1308ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai); 1318ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn final boolean mayCompat = !ci.alwaysSupportsScreen() 1328ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn && !ci.neverSupportsScreen(); 1338ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn 134a9551706a279a798c91d8ef593b2acb16a30c682Dianne Hackborn if (updated) { 1358ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn // Update -- if the app no longer can run in compat mode, clear 1368ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn // any current settings for it. 1378ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn if (!mayCompat && mPackages.containsKey(packageName)) { 1388ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn mPackages.remove(packageName); 1398ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn mHandler.removeMessages(MSG_WRITE); 1408ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn Message msg = mHandler.obtainMessage(MSG_WRITE); 1418ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn mHandler.sendMessageDelayed(msg, 10000); 1428ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } 1438ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } 1448ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } 1458ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn 1460f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) { 1472f0b17573d4324832f7a20402a3d2b5920bc4866Dianne Hackborn CompatibilityInfo ci = new CompatibilityInfo(ai, mService.mConfiguration.screenLayout, 148df6e980e3f63eb0f6f9eb437fa925d5009cd9c44Dianne Hackborn mService.mConfiguration.smallestScreenWidthDp, 14936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0); 1502f0b17573d4324832f7a20402a3d2b5920bc4866Dianne Hackborn //Slog.i(TAG, "*********** COMPAT FOR PKG " + ai.packageName + ": " + ci); 1512f0b17573d4324832f7a20402a3d2b5920bc4866Dianne Hackborn return ci; 1520f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1530f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 15436cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn public int computeCompatModeLocked(ApplicationInfo ai) { 15536cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0; 1560f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn CompatibilityInfo info = new CompatibilityInfo(ai, 157df6e980e3f63eb0f6f9eb437fa925d5009cd9c44Dianne Hackborn mService.mConfiguration.screenLayout, 158df6e980e3f63eb0f6f9eb437fa925d5009cd9c44Dianne Hackborn mService.mConfiguration.smallestScreenWidthDp, enabled); 1590f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (info.alwaysSupportsScreen()) { 1600f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return ActivityManager.COMPAT_MODE_NEVER; 1610f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1620f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (info.neverSupportsScreen()) { 1630f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return ActivityManager.COMPAT_MODE_ALWAYS; 1640f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1650f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return enabled ? ActivityManager.COMPAT_MODE_ENABLED 1660f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn : ActivityManager.COMPAT_MODE_DISABLED; 1670f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1680f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 16936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn public boolean getFrontActivityAskCompatModeLocked() { 17036cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null); 17136cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn if (r == null) { 17236cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn return false; 17336cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 17436cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn return getPackageAskCompatModeLocked(r.packageName); 17536cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 17636cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 17736cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn public boolean getPackageAskCompatModeLocked(String packageName) { 17836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0; 17936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 18036cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 18136cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn public void setFrontActivityAskCompatModeLocked(boolean ask) { 18236cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null); 18336cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn if (r != null) { 18436cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn setPackageAskCompatModeLocked(r.packageName, ask); 18536cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 18636cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 18736cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 18836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn public void setPackageAskCompatModeLocked(String packageName, boolean ask) { 18936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn int curFlags = getPackageFlags(packageName); 19036cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn int newFlags = ask ? (curFlags&~COMPAT_FLAG_DONT_ASK) : (curFlags|COMPAT_FLAG_DONT_ASK); 19136cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn if (curFlags != newFlags) { 19236cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn if (newFlags != 0) { 19336cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn mPackages.put(packageName, newFlags); 19436cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } else { 19536cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn mPackages.remove(packageName); 19636cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 19736cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn mHandler.removeMessages(MSG_WRITE); 19836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn Message msg = mHandler.obtainMessage(MSG_WRITE); 19936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn mHandler.sendMessageDelayed(msg, 10000); 20036cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 20136cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 20236cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 2030f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn public int getFrontActivityScreenCompatModeLocked() { 2040f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null); 2050f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (r == null) { 2060f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return ActivityManager.COMPAT_MODE_UNKNOWN; 2070f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2080f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return computeCompatModeLocked(r.info.applicationInfo); 2090f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2100f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 2110f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn public void setFrontActivityScreenCompatModeLocked(int mode) { 2120f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null); 2130f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (r == null) { 2140f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity"); 2150f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return; 2160f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2170f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn setPackageScreenCompatModeLocked(r.info.applicationInfo, mode); 2180f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2190f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 2200f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn public int getPackageScreenCompatModeLocked(String packageName) { 2210f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn ApplicationInfo ai = null; 2220f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn try { 2230f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0); 2240f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } catch (RemoteException e) { 2250f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2260f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (ai == null) { 2270f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return ActivityManager.COMPAT_MODE_UNKNOWN; 2280f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2290f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return computeCompatModeLocked(ai); 2300f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2310f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 2320f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn public void setPackageScreenCompatModeLocked(String packageName, int mode) { 2330f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn ApplicationInfo ai = null; 2340f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn try { 2350f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0); 2360f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } catch (RemoteException e) { 2370f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2380f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (ai == null) { 2390f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName); 2400f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return; 2410f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2420f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn setPackageScreenCompatModeLocked(ai, mode); 2430f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2440f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 2450f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn private void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) { 2460f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn final String packageName = ai.packageName; 2470f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 24836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn int curFlags = getPackageFlags(packageName); 24936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 2500f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn boolean enable; 2510f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn switch (mode) { 2520f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn case ActivityManager.COMPAT_MODE_DISABLED: 2530f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn enable = false; 2540f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn break; 2550f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn case ActivityManager.COMPAT_MODE_ENABLED: 2560f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn enable = true; 2570f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn break; 2580f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn case ActivityManager.COMPAT_MODE_TOGGLE: 25936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn enable = (curFlags&COMPAT_FLAG_ENABLED) == 0; 2600f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn break; 2610f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn default: 2620f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring"); 2630f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return; 2640f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 26536cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 26636cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn int newFlags = curFlags; 2670f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (enable) { 26836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn newFlags |= COMPAT_FLAG_ENABLED; 2690f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } else { 27036cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn newFlags &= ~COMPAT_FLAG_ENABLED; 27136cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 27236cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 2738ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai); 2748ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn if (ci.alwaysSupportsScreen()) { 2758ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn Slog.w(TAG, "Ignoring compat mode change of " + packageName 2768ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn + "; compatibility never needed"); 2778ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn newFlags = 0; 2788ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } 2798ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn if (ci.neverSupportsScreen()) { 2808ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn Slog.w(TAG, "Ignoring compat mode change of " + packageName 2818ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn + "; compatibility always needed"); 2828ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn newFlags = 0; 2838ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } 2848ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn 28536cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn if (newFlags != curFlags) { 28636cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn if (newFlags != 0) { 28736cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn mPackages.put(packageName, newFlags); 28836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } else { 2890f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mPackages.remove(packageName); 2900f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2918ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn 2928ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn // Need to get compatibility info in new state. 2938ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn ci = compatibilityInfoForPackageLocked(ai); 2940f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 2950f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mHandler.removeMessages(MSG_WRITE); 2960f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn Message msg = mHandler.obtainMessage(MSG_WRITE); 2970f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mHandler.sendMessageDelayed(msg, 10000); 2980f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 2998ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn ActivityRecord starting = mService.mMainStack.topRunningActivityLocked(null); 3008ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn 3018ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn // All activities that came from the package must be 3028ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn // restarted as if there was a config change. 3038ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn for (int i=mService.mMainStack.mHistory.size()-1; i>=0; i--) { 3048ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn ActivityRecord a = (ActivityRecord)mService.mMainStack.mHistory.get(i); 3058ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn if (a.info.packageName.equals(packageName)) { 3068ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn a.forceNewConfig = true; 3078ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn if (starting != null && a == starting && a.visible) { 3088ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn a.startFreezingScreenLocked(starting.app, 3098ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn ActivityInfo.CONFIG_SCREEN_LAYOUT); 3108ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } 3118ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } 3128ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } 3138ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn 3140f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn // Tell all processes that loaded this package about the change. 3150f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn for (int i=mService.mLruProcesses.size()-1; i>=0; i--) { 3160f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn ProcessRecord app = mService.mLruProcesses.get(i); 3170f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (!app.pkgList.contains(packageName)) { 3180f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn continue; 3190f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3200f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn try { 3210f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (app.thread != null) { 3220f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc " 3230f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn + app.processName + " new compat " + ci); 3240f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn app.thread.updatePackageCompatibilityInfo(packageName, ci); 3250f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3260f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } catch (Exception e) { 3270f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3280f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3290f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 3300f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (starting != null) { 3310f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mService.mMainStack.ensureActivityConfigurationLocked(starting, 0); 3320f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn // And we need to make sure at this point that all other activities 3330f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn // are made visible with the correct configuration. 3340f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mService.mMainStack.ensureActivitiesVisibleLocked(starting, 0); 3350f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3360f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3370f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3380f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 3390f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn void saveCompatModes() { 34036cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn HashMap<String, Integer> pkgs; 3410f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn synchronized (mService) { 34236cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn pkgs = new HashMap<String, Integer>(mPackages); 3430f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3440f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 3450f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn FileOutputStream fos = null; 3460f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 3470f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn try { 3480f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn fos = mFile.startWrite(); 3490f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn XmlSerializer out = new FastXmlSerializer(); 3500f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.setOutput(fos, "utf-8"); 3510f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.startDocument(null, true); 3520f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 3530f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.startTag(null, "compat-packages"); 3540f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 3550f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn final IPackageManager pm = AppGlobals.getPackageManager(); 3560f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn final int screenLayout = mService.mConfiguration.screenLayout; 357df6e980e3f63eb0f6f9eb437fa925d5009cd9c44Dianne Hackborn final int smallestScreenWidthDp = mService.mConfiguration.smallestScreenWidthDp; 35836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator(); 3590f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn while (it.hasNext()) { 36036cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn Map.Entry<String, Integer> entry = it.next(); 36136cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn String pkg = entry.getKey(); 36236cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn int mode = entry.getValue(); 36336cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn if (mode == 0) { 36436cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn continue; 36536cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 3660f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn ApplicationInfo ai = null; 3670f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn try { 3680f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn ai = pm.getApplicationInfo(pkg, 0); 3690f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } catch (RemoteException e) { 3700f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3710f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (ai == null) { 3720f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn continue; 3730f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 374df6e980e3f63eb0f6f9eb437fa925d5009cd9c44Dianne Hackborn CompatibilityInfo info = new CompatibilityInfo(ai, screenLayout, 375df6e980e3f63eb0f6f9eb437fa925d5009cd9c44Dianne Hackborn smallestScreenWidthDp, false); 3760f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (info.alwaysSupportsScreen()) { 3770f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn continue; 3780f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3790f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (info.neverSupportsScreen()) { 3800f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn continue; 3810f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3820f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.startTag(null, "pkg"); 3830f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.attribute(null, "name", pkg); 38436cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn out.attribute(null, "mode", Integer.toString(mode)); 3850f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.endTag(null, "pkg"); 3860f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3870f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 3880f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.endTag(null, "compat-packages"); 3890f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.endDocument(); 3900f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 3910f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mFile.finishWrite(fos); 3920f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } catch (java.io.IOException e1) { 3930f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn Slog.w(TAG, "Error writing compat packages", e1); 3940f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (fos != null) { 3950f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mFile.failWrite(fos); 3960f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3970f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3980f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3990f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn} 400