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.Iterator; 836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackbornimport java.util.Map; 90f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 100f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport org.xmlpull.v1.XmlPullParser; 110f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport org.xmlpull.v1.XmlPullParserException; 120f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport org.xmlpull.v1.XmlSerializer; 130f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 140f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport com.android.internal.util.FastXmlSerializer; 150f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 160f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.app.ActivityManager; 170f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.app.AppGlobals; 188ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackbornimport android.content.pm.ActivityInfo; 190f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.content.pm.ApplicationInfo; 200f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.content.pm.IPackageManager; 210f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.content.res.CompatibilityInfo; 220f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.os.Handler; 230f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.os.Message; 240f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.os.RemoteException; 2539606a007a5b1309dd000234f2b8cf156c49fd0fDianne Hackbornimport android.util.AtomicFile; 260f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.util.Slog; 270f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornimport android.util.Xml; 280f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 290f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackbornpublic class CompatModePackages { 300f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn private final String TAG = ActivityManagerService.TAG; 310f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn private final boolean DEBUG_CONFIGURATION = ActivityManagerService.DEBUG_CONFIGURATION; 320f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 330f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn private final ActivityManagerService mService; 340f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn private final AtomicFile mFile; 350f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 3636cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn // Compatibility state: no longer ask user to select the mode. 3736cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn public static final int COMPAT_FLAG_DONT_ASK = 1<<0; 3836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn // Compatibility state: compatibility mode is enabled. 3936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn public static final int COMPAT_FLAG_ENABLED = 1<<1; 4036cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 4136cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn private final HashMap<String, Integer> mPackages = new HashMap<String, Integer>(); 420f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 4340c8db5a28e9abae2033facce1354e3677911fccDianne Hackborn private static final int MSG_WRITE = ActivityManagerService.FIRST_COMPAT_MODE_MSG; 440f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 450f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn private final Handler mHandler = new Handler() { 460f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn @Override public void handleMessage(Message msg) { 470f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn switch (msg.what) { 480f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn case MSG_WRITE: 490f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn saveCompatModes(); 500f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn break; 510f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn default: 520f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn super.handleMessage(msg); 530f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn break; 540f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 550f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 560f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn }; 570f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 580f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn public CompatModePackages(ActivityManagerService service, File systemDir) { 590f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mService = service; 600f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mFile = new AtomicFile(new File(systemDir, "packages-compat.xml")); 610f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 620f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn FileInputStream fis = null; 630f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn try { 640f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn fis = mFile.openRead(); 650f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn XmlPullParser parser = Xml.newPullParser(); 660f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn parser.setInput(fis, null); 670f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn int eventType = parser.getEventType(); 680f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn while (eventType != XmlPullParser.START_TAG) { 690f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn eventType = parser.next(); 700f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 710f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn String tagName = parser.getName(); 720f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if ("compat-packages".equals(tagName)) { 730f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn eventType = parser.next(); 740f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn do { 750f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (eventType == XmlPullParser.START_TAG) { 760f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn tagName = parser.getName(); 770f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (parser.getDepth() == 2) { 780f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if ("pkg".equals(tagName)) { 790f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn String pkg = parser.getAttributeValue(null, "name"); 800f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (pkg != null) { 8136cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn String mode = parser.getAttributeValue(null, "mode"); 8236cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn int modeInt = 0; 8336cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn if (mode != null) { 8436cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn try { 8536cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn modeInt = Integer.parseInt(mode); 8636cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } catch (NumberFormatException e) { 8736cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 8836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 8936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn mPackages.put(pkg, modeInt); 900f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 910f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 920f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 930f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 940f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn eventType = parser.next(); 950f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } while (eventType != XmlPullParser.END_DOCUMENT); 960f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 970f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } catch (XmlPullParserException e) { 980f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn Slog.w(TAG, "Error reading compat-packages", e); 990f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } catch (java.io.IOException e) { 1000f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (fis != null) Slog.w(TAG, "Error reading compat-packages", e); 1010f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } finally { 1020f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (fis != null) { 1030f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn try { 1040f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn fis.close(); 1050f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } catch (java.io.IOException e1) { 1060f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1070f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1080f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1090f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1100f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 11136cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn public HashMap<String, Integer> getPackages() { 1120f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return mPackages; 1130f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1140f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 11536cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn private int getPackageFlags(String packageName) { 11636cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn Integer flags = mPackages.get(packageName); 11736cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn return flags != null ? flags : 0; 11836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 11936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 1208ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn public void handlePackageAddedLocked(String packageName, boolean updated) { 1218ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn ApplicationInfo ai = null; 1228ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn try { 123483f3b06ea84440a082e21b68ec2c2e54046f5a6Amith Yamasani ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0); 1248ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } catch (RemoteException e) { 1258ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } 1268ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn if (ai == null) { 1278ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn return; 1288ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } 1298ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai); 1308ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn final boolean mayCompat = !ci.alwaysSupportsScreen() 1318ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn && !ci.neverSupportsScreen(); 1328ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn 133a9551706a279a798c91d8ef593b2acb16a30c682Dianne Hackborn if (updated) { 1348ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn // Update -- if the app no longer can run in compat mode, clear 1358ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn // any current settings for it. 1368ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn if (!mayCompat && mPackages.containsKey(packageName)) { 1378ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn mPackages.remove(packageName); 1388ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn mHandler.removeMessages(MSG_WRITE); 1398ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn Message msg = mHandler.obtainMessage(MSG_WRITE); 1408ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn mHandler.sendMessageDelayed(msg, 10000); 1418ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } 1428ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } 1438ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } 1448ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn 1450f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn public CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) { 1462f0b17573d4324832f7a20402a3d2b5920bc4866Dianne Hackborn CompatibilityInfo ci = new CompatibilityInfo(ai, mService.mConfiguration.screenLayout, 147df6e980e3f63eb0f6f9eb437fa925d5009cd9c44Dianne Hackborn mService.mConfiguration.smallestScreenWidthDp, 14836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0); 1492f0b17573d4324832f7a20402a3d2b5920bc4866Dianne Hackborn //Slog.i(TAG, "*********** COMPAT FOR PKG " + ai.packageName + ": " + ci); 1502f0b17573d4324832f7a20402a3d2b5920bc4866Dianne Hackborn return ci; 1510f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1520f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 15336cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn public int computeCompatModeLocked(ApplicationInfo ai) { 15436cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn boolean enabled = (getPackageFlags(ai.packageName)&COMPAT_FLAG_ENABLED) != 0; 1550f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn CompatibilityInfo info = new CompatibilityInfo(ai, 156df6e980e3f63eb0f6f9eb437fa925d5009cd9c44Dianne Hackborn mService.mConfiguration.screenLayout, 157df6e980e3f63eb0f6f9eb437fa925d5009cd9c44Dianne Hackborn mService.mConfiguration.smallestScreenWidthDp, enabled); 1580f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (info.alwaysSupportsScreen()) { 1590f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return ActivityManager.COMPAT_MODE_NEVER; 1600f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1610f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (info.neverSupportsScreen()) { 1620f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return ActivityManager.COMPAT_MODE_ALWAYS; 1630f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1640f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return enabled ? ActivityManager.COMPAT_MODE_ENABLED 1650f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn : ActivityManager.COMPAT_MODE_DISABLED; 1660f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 1670f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 16836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn public boolean getFrontActivityAskCompatModeLocked() { 16936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null); 17036cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn if (r == null) { 17136cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn return false; 17236cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 17336cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn return getPackageAskCompatModeLocked(r.packageName); 17436cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 17536cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 17636cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn public boolean getPackageAskCompatModeLocked(String packageName) { 17736cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn return (getPackageFlags(packageName)&COMPAT_FLAG_DONT_ASK) == 0; 17836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 17936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 18036cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn public void setFrontActivityAskCompatModeLocked(boolean ask) { 18136cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null); 18236cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn if (r != null) { 18336cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn setPackageAskCompatModeLocked(r.packageName, ask); 18436cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 18536cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 18636cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 18736cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn public void setPackageAskCompatModeLocked(String packageName, boolean ask) { 18836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn int curFlags = getPackageFlags(packageName); 18936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn int newFlags = ask ? (curFlags&~COMPAT_FLAG_DONT_ASK) : (curFlags|COMPAT_FLAG_DONT_ASK); 19036cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn if (curFlags != newFlags) { 19136cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn if (newFlags != 0) { 19236cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn mPackages.put(packageName, newFlags); 19336cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } else { 19436cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn mPackages.remove(packageName); 19536cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 19636cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn mHandler.removeMessages(MSG_WRITE); 19736cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn Message msg = mHandler.obtainMessage(MSG_WRITE); 19836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn mHandler.sendMessageDelayed(msg, 10000); 19936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 20036cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 20136cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 2020f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn public int getFrontActivityScreenCompatModeLocked() { 2030f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null); 2040f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (r == null) { 2050f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return ActivityManager.COMPAT_MODE_UNKNOWN; 2060f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2070f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return computeCompatModeLocked(r.info.applicationInfo); 2080f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2090f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 2100f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn public void setFrontActivityScreenCompatModeLocked(int mode) { 2110f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn ActivityRecord r = mService.mMainStack.topRunningActivityLocked(null); 2120f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (r == null) { 2130f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn Slog.w(TAG, "setFrontActivityScreenCompatMode failed: no top activity"); 2140f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return; 2150f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2160f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn setPackageScreenCompatModeLocked(r.info.applicationInfo, mode); 2170f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2180f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 2190f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn public int getPackageScreenCompatModeLocked(String packageName) { 2200f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn ApplicationInfo ai = null; 2210f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn try { 222483f3b06ea84440a082e21b68ec2c2e54046f5a6Amith Yamasani ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0); 2230f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } catch (RemoteException e) { 2240f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2250f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (ai == null) { 2260f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return ActivityManager.COMPAT_MODE_UNKNOWN; 2270f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2280f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return computeCompatModeLocked(ai); 2290f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2300f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 2310f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn public void setPackageScreenCompatModeLocked(String packageName, int mode) { 2320f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn ApplicationInfo ai = null; 2330f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn try { 234483f3b06ea84440a082e21b68ec2c2e54046f5a6Amith Yamasani ai = AppGlobals.getPackageManager().getApplicationInfo(packageName, 0, 0); 2350f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } catch (RemoteException e) { 2360f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2370f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (ai == null) { 2380f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName); 2390f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return; 2400f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2410f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn setPackageScreenCompatModeLocked(ai, mode); 2420f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2430f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 2440f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn private void setPackageScreenCompatModeLocked(ApplicationInfo ai, int mode) { 2450f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn final String packageName = ai.packageName; 2460f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 24736cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn int curFlags = getPackageFlags(packageName); 24836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 2490f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn boolean enable; 2500f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn switch (mode) { 2510f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn case ActivityManager.COMPAT_MODE_DISABLED: 2520f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn enable = false; 2530f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn break; 2540f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn case ActivityManager.COMPAT_MODE_ENABLED: 2550f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn enable = true; 2560f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn break; 2570f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn case ActivityManager.COMPAT_MODE_TOGGLE: 25836cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn enable = (curFlags&COMPAT_FLAG_ENABLED) == 0; 2590f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn break; 2600f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn default: 2610f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn Slog.w(TAG, "Unknown screen compat mode req #" + mode + "; ignoring"); 2620f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn return; 2630f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 26436cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 26536cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn int newFlags = curFlags; 2660f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (enable) { 26736cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn newFlags |= COMPAT_FLAG_ENABLED; 2680f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } else { 26936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn newFlags &= ~COMPAT_FLAG_ENABLED; 27036cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 27136cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn 2728ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai); 2738ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn if (ci.alwaysSupportsScreen()) { 2748ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn Slog.w(TAG, "Ignoring compat mode change of " + packageName 2758ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn + "; compatibility never needed"); 2768ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn newFlags = 0; 2778ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } 2788ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn if (ci.neverSupportsScreen()) { 2798ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn Slog.w(TAG, "Ignoring compat mode change of " + packageName 2808ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn + "; compatibility always needed"); 2818ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn newFlags = 0; 2828ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn } 2838ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn 28436cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn if (newFlags != curFlags) { 28536cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn if (newFlags != 0) { 28636cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn mPackages.put(packageName, newFlags); 28736cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } else { 2880f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mPackages.remove(packageName); 2890f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 2908ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn 2918ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn // Need to get compatibility info in new state. 2928ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn ci = compatibilityInfoForPackageLocked(ai); 2930f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 2940f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mHandler.removeMessages(MSG_WRITE); 2950f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn Message msg = mHandler.obtainMessage(MSG_WRITE); 2960f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mHandler.sendMessageDelayed(msg, 10000); 2970f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 2982ad920759b1981eaf526fd37a314fbc5a3ed90aeCraig Mautner ActivityRecord starting = mService.mMainStack.topRunningActivityLocked(null); 2992ad920759b1981eaf526fd37a314fbc5a3ed90aeCraig Mautner 3002ad920759b1981eaf526fd37a314fbc5a3ed90aeCraig Mautner // All activities that came from the package must be 3012ad920759b1981eaf526fd37a314fbc5a3ed90aeCraig Mautner // restarted as if there was a config change. 3022ad920759b1981eaf526fd37a314fbc5a3ed90aeCraig Mautner for (int i=mService.mMainStack.mHistory.size()-1; i>=0; i--) { 3032ad920759b1981eaf526fd37a314fbc5a3ed90aeCraig Mautner ActivityRecord a = (ActivityRecord)mService.mMainStack.mHistory.get(i); 3042ad920759b1981eaf526fd37a314fbc5a3ed90aeCraig Mautner if (a.info.packageName.equals(packageName)) { 3052ad920759b1981eaf526fd37a314fbc5a3ed90aeCraig Mautner a.forceNewConfig = true; 3062ad920759b1981eaf526fd37a314fbc5a3ed90aeCraig Mautner if (starting != null && a == starting && a.visible) { 3072ad920759b1981eaf526fd37a314fbc5a3ed90aeCraig Mautner a.startFreezingScreenLocked(starting.app, 3082ad920759b1981eaf526fd37a314fbc5a3ed90aeCraig Mautner ActivityInfo.CONFIG_SCREEN_LAYOUT); 3092ad920759b1981eaf526fd37a314fbc5a3ed90aeCraig Mautner } 3102ad920759b1981eaf526fd37a314fbc5a3ed90aeCraig Mautner } 3112ad920759b1981eaf526fd37a314fbc5a3ed90aeCraig Mautner } 3128ea5e1d79eb1f05ee7628b0d45ea8fc8eea5330dDianne Hackborn 3130f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn // Tell all processes that loaded this package about the change. 3140f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn for (int i=mService.mLruProcesses.size()-1; i>=0; i--) { 3150f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn ProcessRecord app = mService.mLruProcesses.get(i); 3160f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (!app.pkgList.contains(packageName)) { 3170f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn continue; 3180f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3190f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn try { 3200f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (app.thread != null) { 3210f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc " 3220f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn + app.processName + " new compat " + ci); 3230f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn app.thread.updatePackageCompatibilityInfo(packageName, ci); 3240f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3250f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } catch (Exception e) { 3260f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3270f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3280f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 3290f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (starting != null) { 3300f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mService.mMainStack.ensureActivityConfigurationLocked(starting, 0); 3310f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn // And we need to make sure at this point that all other activities 3320f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn // are made visible with the correct configuration. 3330f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mService.mMainStack.ensureActivitiesVisibleLocked(starting, 0); 3340f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3350f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3360f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3370f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 3380f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn void saveCompatModes() { 33936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn HashMap<String, Integer> pkgs; 3400f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn synchronized (mService) { 34136cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn pkgs = new HashMap<String, Integer>(mPackages); 3420f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3430f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 3440f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn FileOutputStream fos = null; 3450f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 3460f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn try { 3470f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn fos = mFile.startWrite(); 3480f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn XmlSerializer out = new FastXmlSerializer(); 3490f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.setOutput(fos, "utf-8"); 3500f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.startDocument(null, true); 3510f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 3520f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.startTag(null, "compat-packages"); 3530f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 3540f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn final IPackageManager pm = AppGlobals.getPackageManager(); 3550f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn final int screenLayout = mService.mConfiguration.screenLayout; 356df6e980e3f63eb0f6f9eb437fa925d5009cd9c44Dianne Hackborn final int smallestScreenWidthDp = mService.mConfiguration.smallestScreenWidthDp; 35736cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn final Iterator<Map.Entry<String, Integer>> it = pkgs.entrySet().iterator(); 3580f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn while (it.hasNext()) { 35936cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn Map.Entry<String, Integer> entry = it.next(); 36036cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn String pkg = entry.getKey(); 36136cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn int mode = entry.getValue(); 36236cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn if (mode == 0) { 36336cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn continue; 36436cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn } 3650f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn ApplicationInfo ai = null; 3660f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn try { 367483f3b06ea84440a082e21b68ec2c2e54046f5a6Amith Yamasani ai = pm.getApplicationInfo(pkg, 0, 0); 3680f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } catch (RemoteException e) { 3690f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3700f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (ai == null) { 3710f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn continue; 3720f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 373df6e980e3f63eb0f6f9eb437fa925d5009cd9c44Dianne Hackborn CompatibilityInfo info = new CompatibilityInfo(ai, screenLayout, 374df6e980e3f63eb0f6f9eb437fa925d5009cd9c44Dianne Hackborn smallestScreenWidthDp, false); 3750f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (info.alwaysSupportsScreen()) { 3760f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn continue; 3770f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3780f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (info.neverSupportsScreen()) { 3790f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn continue; 3800f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3810f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.startTag(null, "pkg"); 3820f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.attribute(null, "name", pkg); 38336cd41f8efa6f6a683d3353d309ff548295af9e9Dianne Hackborn out.attribute(null, "mode", Integer.toString(mode)); 3840f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.endTag(null, "pkg"); 3850f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3860f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 3870f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.endTag(null, "compat-packages"); 3880f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn out.endDocument(); 3890f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn 3900f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mFile.finishWrite(fos); 3910f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } catch (java.io.IOException e1) { 3920f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn Slog.w(TAG, "Error writing compat packages", e1); 3930f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn if (fos != null) { 3940f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn mFile.failWrite(fos); 3950f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3960f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3970f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn } 3980f1de9adde0b52d2a385a76232bd7ac30c3eeea2Dianne Hackborn} 399