Better compat mode part one: start scaling windows.

First step of improving app screen size compatibility mode.  When
running in compat mode, an application's windows are scaled up on
the screen rather than being small with 1:1 pixels.

Currently we scale the application to fill the entire screen, so
don't use an even pixel scaling.  Though this may have some
negative impact on the appearance (it looks okay to me), it has a
big benefit of allowing us to now treat these apps as normal
full-screens apps and do the normal transition animations as you
move in and out and around in them.

This introduces fun stuff in the input system to take care of
modifying pointer coordinates to account for the app window
surface scaling.  The input dispatcher is told about the scale
that is being applied to each window and, when there is one,
adjusts pointer events appropriately as they are being sent
to the transport.

Also modified is CompatibilityInfo, which has been greatly
simplified to not be so insane and incomprehendible.  It is
now simple -- when constructed it determines if the given app
is compatible with the current screen size and density, and
that is that.

There are new APIs on ActivityManagerService to put applications
that we would traditionally consider compatible with larger screens
in compatibility mode.  This is the start of a facility to have
a UI affordance for a user to switch apps in and out of
compatibility.

To test switching of modes, there is a new variation of the "am"
command to do this: am screen-compat [on|off] [package]

This mode switching has the fundamentals of restarting activities
when it is changed, though the state still needs to be persisted
and the overall mode switch cleaned up.

For the few small apps I have tested, things mostly seem to be
working well.  I know of one problem with the text selection
handles being drawn at the wrong position because at some point
the window offset is being scaled incorrectly.  There are
probably other similar issues around the interaction between
two windows because the different window coordinate spaces are
done in a hacky way instead of being formally integrated into
the window manager layout process.

Change-Id: Ie038e3746b448135117bd860859d74e360938557
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 6426635..11e9975 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1398,6 +1398,16 @@
             return true;
         }
 
+        case SET_PACKAGE_SCREEN_COMPAT_MODE_TRANSACTION:
+        {
+            data.enforceInterface(IActivityManager.descriptor);
+            String pkg = data.readString();
+            boolean enabled = data.readInt() != 0;
+            setPackageScreenCompatMode(pkg, enabled);
+            reply.writeNoException();
+            return true;
+        }
+
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -3142,5 +3152,18 @@
         return result;
     }
 
+    public void setPackageScreenCompatMode(String packageName, boolean compatEnabled)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(packageName);
+        data.writeInt(compatEnabled ? 1 : 0);
+        mRemote.transact(SET_PACKAGE_SCREEN_COMPAT_MODE_TRANSACTION, data, reply, 0);
+        reply.readException();
+        reply.recycle();
+        data.recycle();
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index bd83762..67e8839 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -202,7 +202,7 @@
 
     Bundle mCoreSettings = null;
 
-    private static final class ActivityClientRecord {
+    static final class ActivityClientRecord {
         IBinder token;
         int ident;
         Intent intent;
@@ -220,6 +220,7 @@
         ActivityClientRecord nextIdle;
 
         ActivityInfo activityInfo;
+        CompatibilityInfo compatInfo;
         LoadedApk packageInfo;
 
         List<ResultInfo> pendingResults;
@@ -260,7 +261,7 @@
         }
     }
 
-    private final class ProviderClientRecord implements IBinder.DeathRecipient {
+    final class ProviderClientRecord implements IBinder.DeathRecipient {
         final String mName;
         final IContentProvider mProvider;
         final ContentProvider mLocalProvider;
@@ -277,7 +278,7 @@
         }
     }
 
-    private static final class NewIntentData {
+    static final class NewIntentData {
         List<Intent> intents;
         IBinder token;
         public String toString() {
@@ -285,7 +286,7 @@
         }
     }
 
-    private static final class ReceiverData extends BroadcastReceiver.PendingResult {
+    static final class ReceiverData extends BroadcastReceiver.PendingResult {
         public ReceiverData(Intent intent, int resultCode, String resultData, Bundle resultExtras,
                 boolean ordered, boolean sticky, IBinder token) {
             super(resultCode, resultData, resultExtras, TYPE_COMPONENT, ordered, sticky, token);
@@ -294,6 +295,7 @@
 
         Intent intent;
         ActivityInfo info;
+        CompatibilityInfo compatInfo;
         public String toString() {
             return "ReceiverData{intent=" + intent + " packageName=" +
                     info.packageName + " resultCode=" + getResultCode()
@@ -302,8 +304,9 @@
         }
     }
 
-    private static final class CreateBackupAgentData {
+    static final class CreateBackupAgentData {
         ApplicationInfo appInfo;
+        CompatibilityInfo compatInfo;
         int backupMode;
         public String toString() {
             return "CreateBackupAgentData{appInfo=" + appInfo
@@ -312,9 +315,10 @@
         }
     }
 
-    private static final class CreateServiceData {
+    static final class CreateServiceData {
         IBinder token;
         ServiceInfo info;
+        CompatibilityInfo compatInfo;
         Intent intent;
         public String toString() {
             return "CreateServiceData{token=" + token + " className="
@@ -323,7 +327,7 @@
         }
     }
 
-    private static final class BindServiceData {
+    static final class BindServiceData {
         IBinder token;
         Intent intent;
         boolean rebind;
@@ -332,7 +336,7 @@
         }
     }
 
-    private static final class ServiceArgsData {
+    static final class ServiceArgsData {
         IBinder token;
         int startId;
         int flags;
@@ -343,7 +347,7 @@
         }
     }
 
-    private static final class AppBindData {
+    static final class AppBindData {
         LoadedApk info;
         String processName;
         ApplicationInfo appInfo;
@@ -355,13 +359,14 @@
         int debugMode;
         boolean restrictedBackupMode;
         Configuration config;
+        CompatibilityInfo compatInfo;
         boolean handlingProfiling;
         public String toString() {
             return "AppBindData{appInfo=" + appInfo + "}";
         }
     }
 
-    private static final class DumpComponentInfo {
+    static final class DumpComponentInfo {
         FileDescriptor fd;
         IBinder token;
         String prefix;
@@ -369,7 +374,7 @@
         boolean dumped;
     }
 
-    private static final class ResultData {
+    static final class ResultData {
         IBinder token;
         List<ResultInfo> results;
         public String toString() {
@@ -377,22 +382,27 @@
         }
     }
 
-    private static final class ContextCleanupInfo {
+    static final class ContextCleanupInfo {
         ContextImpl context;
         String what;
         String who;
     }
 
-    private static final class ProfilerControlData {
+    static final class ProfilerControlData {
         String path;
         ParcelFileDescriptor fd;
     }
 
-    private static final class DumpHeapData {
+    static final class DumpHeapData {
         String path;
         ParcelFileDescriptor fd;
     }
 
+    static final class UpdateCompatibilityData {
+        String pkg;
+        CompatibilityInfo info;
+    }
+
     private final class ApplicationThread extends ApplicationThreadNative {
         private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s";
         private static final String ONE_COUNT_COLUMN = "%17s %8d";
@@ -443,7 +453,8 @@
         // we use token to identify this activity without having to send the
         // activity itself back to the activity manager. (matters more with ipc)
         public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
-                ActivityInfo info, Bundle state, List<ResultInfo> pendingResults,
+                ActivityInfo info, CompatibilityInfo compatInfo, Bundle state,
+                List<ResultInfo> pendingResults,
                 List<Intent> pendingNewIntents, boolean notResumed, boolean isForward) {
             ActivityClientRecord r = new ActivityClientRecord();
 
@@ -451,6 +462,7 @@
             r.ident = ident;
             r.intent = intent;
             r.activityInfo = info;
+            r.compatInfo = compatInfo;
             r.state = state;
 
             r.pendingResults = pendingResults;
@@ -484,33 +496,40 @@
         }
 
         public final void scheduleReceiver(Intent intent, ActivityInfo info,
-                int resultCode, String data, Bundle extras, boolean sync) {
+                CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
+                boolean sync) {
             ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
                     sync, false, mAppThread.asBinder());
             r.info = info;
+            r.compatInfo = compatInfo;
             queueOrSendMessage(H.RECEIVER, r);
         }
 
-        public final void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode) {
+        public final void scheduleCreateBackupAgent(ApplicationInfo app,
+                CompatibilityInfo compatInfo, int backupMode) {
             CreateBackupAgentData d = new CreateBackupAgentData();
             d.appInfo = app;
+            d.compatInfo = compatInfo;
             d.backupMode = backupMode;
 
             queueOrSendMessage(H.CREATE_BACKUP_AGENT, d);
         }
 
-        public final void scheduleDestroyBackupAgent(ApplicationInfo app) {
+        public final void scheduleDestroyBackupAgent(ApplicationInfo app,
+                CompatibilityInfo compatInfo) {
             CreateBackupAgentData d = new CreateBackupAgentData();
             d.appInfo = app;
+            d.compatInfo = compatInfo;
 
             queueOrSendMessage(H.DESTROY_BACKUP_AGENT, d);
         }
 
         public final void scheduleCreateService(IBinder token,
-                ServiceInfo info) {
+                ServiceInfo info, CompatibilityInfo compatInfo) {
             CreateServiceData s = new CreateServiceData();
             s.token = token;
             s.info = info;
+            s.compatInfo = compatInfo;
 
             queueOrSendMessage(H.CREATE_SERVICE, s);
         }
@@ -553,7 +572,8 @@
                 ComponentName instrumentationName, String profileFile,
                 Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
                 int debugMode, boolean isRestrictedBackupMode, Configuration config,
-                Map<String, IBinder> services, Bundle coreSettings) {
+                CompatibilityInfo compatInfo, Map<String, IBinder> services,
+                Bundle coreSettings) {
 
             if (services != null) {
                 // Setup the service cache in the ServiceManager
@@ -573,6 +593,7 @@
             data.debugMode = debugMode;
             data.restrictedBackupMode = isRestrictedBackupMode;
             data.config = config;
+            data.compatInfo = compatInfo;
             queueOrSendMessage(H.BIND_APPLICATION, data);
         }
 
@@ -903,6 +924,13 @@
         public void setCoreSettings(Bundle coreSettings) {
             queueOrSendMessage(H.SET_CORE_SETTINGS, coreSettings);
         }
+
+        public void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) {
+            UpdateCompatibilityData ucd = new UpdateCompatibilityData();
+            ucd.pkg = pkg;
+            ucd.info = info;
+            queueOrSendMessage(H.UPDATE_PACKAGE_COMPATIBILITY_INFO, ucd);
+        }
     }
 
     private final class H extends Handler {
@@ -945,6 +973,7 @@
         public static final int DUMP_ACTIVITY           = 136;
         public static final int SLEEPING                = 137;
         public static final int SET_CORE_SETTINGS       = 138;
+        public static final int UPDATE_PACKAGE_COMPATIBILITY_INFO = 139;
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
                 switch (code) {
@@ -987,6 +1016,7 @@
                     case DUMP_ACTIVITY: return "DUMP_ACTIVITY";
                     case SLEEPING: return "SLEEPING";
                     case SET_CORE_SETTINGS: return "SET_CORE_SETTINGS";
+                    case UPDATE_PACKAGE_COMPATIBILITY_INFO: return "UPDATE_PACKAGE_COMPATIBILITY_INFO";
                 }
             }
             return "(unknown)";
@@ -998,7 +1028,7 @@
                     ActivityClientRecord r = (ActivityClientRecord)msg.obj;
 
                     r.packageInfo = getPackageInfoNoCheck(
-                            r.activityInfo.applicationInfo);
+                            r.activityInfo.applicationInfo, r.compatInfo);
                     handleLaunchActivity(r, null);
                 } break;
                 case RELAUNCH_ACTIVITY: {
@@ -1072,7 +1102,7 @@
                     handleRequestThumbnail((IBinder)msg.obj);
                     break;
                 case CONFIGURATION_CHANGED:
-                    handleConfigurationChanged((Configuration)msg.obj);
+                    handleConfigurationChanged((Configuration)msg.obj, null);
                     break;
                 case CLEAN_UP_CONTEXT:
                     ContextCleanupInfo cci = (ContextCleanupInfo)msg.obj;
@@ -1125,6 +1155,8 @@
                 case SET_CORE_SETTINGS:
                     handleSetCoreSettings((Bundle) msg.obj);
                     break;
+                case UPDATE_PACKAGE_COMPATIBILITY_INFO:
+                    handleUpdatePackageCompatibilityInfo((UpdateCompatibilityData)msg.obj);
             }
             if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what);
         }
@@ -1335,7 +1367,8 @@
         return mH;
     }
 
-    public final LoadedApk getPackageInfo(String packageName, int flags) {
+    public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
+            int flags) {
         synchronized (mPackages) {
             WeakReference<LoadedApk> ref;
             if ((flags&Context.CONTEXT_INCLUDE_CODE) != 0) {
@@ -1369,13 +1402,14 @@
         }
 
         if (ai != null) {
-            return getPackageInfo(ai, flags);
+            return getPackageInfo(ai, compatInfo, flags);
         }
 
         return null;
     }
 
-    public final LoadedApk getPackageInfo(ApplicationInfo ai, int flags) {
+    public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo,
+            int flags) {
         boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
         boolean securityViolation = includeCode && ai.uid != 0
                 && ai.uid != Process.SYSTEM_UID && (mBoundApplication != null
@@ -1394,14 +1428,27 @@
                 throw new SecurityException(msg);
             }
         }
-        return getPackageInfo(ai, null, securityViolation, includeCode);
+        return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode);
     }
 
-    public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai) {
-        return getPackageInfo(ai, null, false, true);
+    public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
+            CompatibilityInfo compatInfo) {
+        return getPackageInfo(ai, compatInfo, null, false, true);
     }
 
-    private final LoadedApk getPackageInfo(ApplicationInfo aInfo,
+    public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) {
+        synchronized (mPackages) {
+            WeakReference<LoadedApk> ref;
+            if (includeCode) {
+                ref = mPackages.get(packageName);
+            } else {
+                ref = mResourcePackages.get(packageName);
+            }
+            return ref != null ? ref.get() : null;
+        }
+    }
+
+    private final LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
             ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {
         synchronized (mPackages) {
             WeakReference<LoadedApk> ref;
@@ -1419,7 +1466,7 @@
                                 ? mBoundApplication.processName : null)
                         + ")");
                 packageInfo =
-                    new LoadedApk(this, aInfo, this, baseLoader,
+                    new LoadedApk(this, aInfo, compatInfo, this, baseLoader,
                             securityViolation, includeCode &&
                             (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0);
                 if (includeCode) {
@@ -1476,7 +1523,8 @@
             if (mSystemContext == null) {
                 ContextImpl context =
                     ContextImpl.createSystemContext(this);
-                LoadedApk info = new LoadedApk(this, "android", context, null);
+                LoadedApk info = new LoadedApk(this, "android", context, null,
+                        CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
                 context.init(info, null, this);
                 context.getResources().updateConfiguration(
                         getConfiguration(), getDisplayMetricsLocked(false));
@@ -1491,7 +1539,8 @@
     public void installSystemApplicationInfo(ApplicationInfo info) {
         synchronized (this) {
             ContextImpl context = getSystemContext();
-            context.init(new LoadedApk(this, "android", context, info), null, this);
+            context.init(new LoadedApk(this, "android", context, info,
+                    new CompatibilityInfo(info, 0, false)), null, this);
         }
     }
 
@@ -1641,7 +1690,7 @@
 
         ActivityInfo aInfo = r.activityInfo;
         if (r.packageInfo == null) {
-            r.packageInfo = getPackageInfo(aInfo.applicationInfo,
+            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                     Context.CONTEXT_INCLUDE_CODE);
         }
 
@@ -1865,7 +1914,7 @@
         String component = data.intent.getComponent().getClassName();
 
         LoadedApk packageInfo = getPackageInfoNoCheck(
-                data.info.applicationInfo);
+                data.info.applicationInfo, data.compatInfo);
 
         IActivityManager mgr = ActivityManagerNative.getDefault();
 
@@ -1926,7 +1975,7 @@
         unscheduleGcIdler();
 
         // instantiate the BackupAgent class named in the manifest
-        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo);
+        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
         String packageName = packageInfo.mPackageName;
         if (mBackupAgents.get(packageName) != null) {
             Slog.d(TAG, "BackupAgent " + "  for " + packageName
@@ -1988,7 +2037,7 @@
     private final void handleDestroyBackupAgent(CreateBackupAgentData data) {
         if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data);
 
-        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo);
+        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
         String packageName = packageInfo.mPackageName;
         BackupAgent agent = mBackupAgents.get(packageName);
         if (agent != null) {
@@ -2010,7 +2059,7 @@
         unscheduleGcIdler();
 
         LoadedApk packageInfo = getPackageInfoNoCheck(
-                data.info.applicationInfo);
+                data.info.applicationInfo, data.compatInfo);
         Service service = null;
         try {
             java.lang.ClassLoader cl = packageInfo.getClassLoader();
@@ -2727,6 +2776,18 @@
         }
     }
 
+    private void handleUpdatePackageCompatibilityInfo(UpdateCompatibilityData data) {
+        LoadedApk apk = peekPackageInfo(data.pkg, false);
+        if (apk != null) {
+            apk.mCompatibilityInfo = data.info;
+        }
+        apk = peekPackageInfo(data.pkg, true);
+        if (apk != null) {
+            apk.mCompatibilityInfo = data.info;
+        }
+        handleConfigurationChanged(mConfiguration, data.info);
+    }
+
     private final void deliverResults(ActivityClientRecord r, List<ResultInfo> results) {
         final int N = results.size();
         for (int i=0; i<N; i++) {
@@ -3064,7 +3125,7 @@
         
         // If there was a pending configuration change, execute it first.
         if (changedConfig != null) {
-            handleConfigurationChanged(changedConfig);
+            handleConfigurationChanged(changedConfig, null);
         }
 
         ActivityClientRecord r = mActivities.get(tmp.token);
@@ -3234,11 +3295,12 @@
         }
     }
 
-    final boolean applyConfigurationToResourcesLocked(Configuration config) {
+    final boolean applyConfigurationToResourcesLocked(Configuration config,
+            CompatibilityInfo compat) {
         if (mResConfiguration == null) {
             mResConfiguration = new Configuration();
         }
-        if (!mResConfiguration.isOtherSeqNewer(config)) {
+        if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
             if (DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
                     + mResConfiguration.seq + ", newSeq=" + config.seq);
             return false;
@@ -3251,7 +3313,7 @@
             Locale.setDefault(config.locale);
         }
 
-        Resources.updateSystemConfiguration(config, dm);
+        Resources.updateSystemConfiguration(config, dm, compat);
 
         ApplicationPackageManager.configurationChanged();
         //Slog.i(TAG, "Configuration changed in " + currentPackageName());
@@ -3266,7 +3328,7 @@
             if (r != null) {
                 if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
                         + r + " config to: " + config);
-                r.updateConfiguration(config, dm);
+                r.updateConfiguration(config, dm, compat);
                 //Slog.i(TAG, "Updated app resources " + v.getKey()
                 //        + " " + r + ": " + r.getConfiguration());
             } else {
@@ -3278,7 +3340,7 @@
         return changes != 0;
     }
     
-    final void handleConfigurationChanged(Configuration config) {
+    final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
 
         ArrayList<ComponentCallbacks> callbacks = null;
 
@@ -3297,15 +3359,21 @@
             if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: "
                     + config);
         
-            applyConfigurationToResourcesLocked(config);
+            applyConfigurationToResourcesLocked(config, compat);
             
             if (mConfiguration == null) {
                 mConfiguration = new Configuration();
             }
-            if (!mConfiguration.isOtherSeqNewer(config)) {
+            if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
                 return;
             }
             mConfiguration.updateFrom(config);
+            if (compat != null) {
+                // Can't do this here, because it causes us to report the
+                // comatible config back to the am as the current config
+                // of the activity, and much unhappiness results.
+                //compat.applyToConfiguration(mConfiguration);
+            }
 
             callbacks = collectComponentCallbacksLocked(false, config);
         }
@@ -3445,9 +3513,10 @@
          * reflect configuration changes. The configuration object passed
          * in AppBindData can be safely assumed to be up to date
          */
-        Resources.getSystem().updateConfiguration(mConfiguration, null);
+        Resources.getSystem().updateConfiguration(mConfiguration,
+                Resources.getSystem().getDisplayMetrics(), data.compatInfo);
 
-        data.info = getPackageInfoNoCheck(data.appInfo);
+        data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
 
         /**
          * For system applications on userdebug/eng builds, log stack
@@ -3539,7 +3608,7 @@
             instrApp.publicSourceDir = ii.publicSourceDir;
             instrApp.dataDir = ii.dataDir;
             instrApp.nativeLibraryDir = ii.nativeLibraryDir;
-            LoadedApk pi = getPackageInfo(instrApp,
+            LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
                     appContext.getClassLoader(), false, true);
             ContextImpl instrContext = new ContextImpl();
             instrContext.init(pi, null, this);
@@ -3953,7 +4022,7 @@
                     // We need to apply this change to the resources
                     // immediately, because upon returning the view
                     // hierarchy will be informed about it.
-                    if (applyConfigurationToResourcesLocked(newConfig)) {
+                    if (applyConfigurationToResourcesLocked(newConfig, null)) {
                         // This actually changed the resources!  Tell
                         // everyone about it.
                         if (mPendingConfiguration == null ||
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index aa26b04..e1d76a4 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -23,6 +23,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ServiceInfo;
+import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.os.Binder;
 import android.os.Bundle;
@@ -131,12 +132,13 @@
             IBinder b = data.readStrongBinder();
             int ident = data.readInt();
             ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
+            CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
             Bundle state = data.readBundle();
             List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
             List<Intent> pi = data.createTypedArrayList(Intent.CREATOR);
             boolean notResumed = data.readInt() != 0;
             boolean isForward = data.readInt() != 0;
-            scheduleLaunchActivity(intent, b, ident, info, state, ri, pi,
+            scheduleLaunchActivity(intent, b, ident, info, compatInfo, state, ri, pi,
                     notResumed, isForward);
             return true;
         }
@@ -181,11 +183,12 @@
             data.enforceInterface(IApplicationThread.descriptor);
             Intent intent = Intent.CREATOR.createFromParcel(data);
             ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
+            CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
             int resultCode = data.readInt();
             String resultData = data.readString();
             Bundle resultExtras = data.readBundle();
             boolean sync = data.readInt() != 0;
-            scheduleReceiver(intent, info, resultCode, resultData,
+            scheduleReceiver(intent, info, compatInfo, resultCode, resultData,
                     resultExtras, sync);
             return true;
         }
@@ -194,7 +197,8 @@
             data.enforceInterface(IApplicationThread.descriptor);
             IBinder token = data.readStrongBinder();
             ServiceInfo info = ServiceInfo.CREATOR.createFromParcel(data);
-            scheduleCreateService(token, info);
+            CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
+            scheduleCreateService(token, info, compatInfo);
             return true;
         }
 
@@ -256,12 +260,13 @@
             int testMode = data.readInt();
             boolean restrictedBackupMode = (data.readInt() != 0);
             Configuration config = Configuration.CREATOR.createFromParcel(data);
+            CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
             HashMap<String, IBinder> services = data.readHashMap(null);
             Bundle coreSettings = data.readBundle();
             bindApplication(packageName, info,
                             providers, testName, profileName,
                             testArgs, testWatcher, testMode, restrictedBackupMode,
-                            config, services, coreSettings);
+                            config, compatInfo, services, coreSettings);
             return true;
         }
         
@@ -389,8 +394,9 @@
         {
             data.enforceInterface(IApplicationThread.descriptor);
             ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(data);
+            CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
             int backupMode = data.readInt();
-            scheduleCreateBackupAgent(appInfo, backupMode);
+            scheduleCreateBackupAgent(appInfo, compatInfo, backupMode);
             return true;
         }
 
@@ -398,7 +404,8 @@
         {
             data.enforceInterface(IApplicationThread.descriptor);
             ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(data);
-            scheduleDestroyBackupAgent(appInfo);
+            CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
+            scheduleDestroyBackupAgent(appInfo, compatInfo);
             return true;
         }
 
@@ -456,12 +463,20 @@
             return true;
         }
 
-        case SET_CORE_SETTINGS: {
+        case SET_CORE_SETTINGS_TRANSACTION: {
             data.enforceInterface(IApplicationThread.descriptor);
             Bundle settings = data.readBundle();
             setCoreSettings(settings);
             return true;
         }
+
+        case UPDATE_PACKAGE_COMPATIBILITY_INFO_TRANSACTION: {
+            data.enforceInterface(IApplicationThread.descriptor);
+            String pkg = data.readString();
+            CompatibilityInfo compat = CompatibilityInfo.CREATOR.createFromParcel(data);
+            updatePackageCompatibilityInfo(pkg, compat);
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -554,7 +569,8 @@
     }
 
     public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
-            ActivityInfo info, Bundle state, List<ResultInfo> pendingResults,
+            ActivityInfo info, CompatibilityInfo compatInfo, Bundle state,
+            List<ResultInfo> pendingResults,
     		List<Intent> pendingNewIntents, boolean notResumed, boolean isForward)
     		throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -563,6 +579,7 @@
         data.writeStrongBinder(token);
         data.writeInt(ident);
         info.writeToParcel(data, 0);
+        compatInfo.writeToParcel(data, 0);
         data.writeBundle(state);
         data.writeTypedList(pendingResults);
         data.writeTypedList(pendingNewIntents);
@@ -619,12 +636,13 @@
     }
     
     public final void scheduleReceiver(Intent intent, ActivityInfo info,
-            int resultCode, String resultData,
+            CompatibilityInfo compatInfo, int resultCode, String resultData,
             Bundle map, boolean sync) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         intent.writeToParcel(data, 0);
         info.writeToParcel(data, 0);
+        compatInfo.writeToParcel(data, 0);
         data.writeInt(resultCode);
         data.writeString(resultData);
         data.writeBundle(map);
@@ -634,32 +652,36 @@
         data.recycle();
     }
 
-    public final void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode)
-            throws RemoteException {
+    public final void scheduleCreateBackupAgent(ApplicationInfo app,
+            CompatibilityInfo compatInfo, int backupMode) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         app.writeToParcel(data, 0);
+        compatInfo.writeToParcel(data, 0);
         data.writeInt(backupMode);
         mRemote.transact(SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
     }
 
-    public final void scheduleDestroyBackupAgent(ApplicationInfo app) throws RemoteException {
+    public final void scheduleDestroyBackupAgent(ApplicationInfo app,
+            CompatibilityInfo compatInfo) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         app.writeToParcel(data, 0);
+        compatInfo.writeToParcel(data, 0);
         mRemote.transact(SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
     }
     
-    public final void scheduleCreateService(IBinder token, ServiceInfo info)
-            throws RemoteException {
+    public final void scheduleCreateService(IBinder token, ServiceInfo info,
+            CompatibilityInfo compatInfo) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeStrongBinder(token);
         info.writeToParcel(data, 0);
+        compatInfo.writeToParcel(data, 0);
         mRemote.transact(SCHEDULE_CREATE_SERVICE_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
@@ -719,7 +741,7 @@
     public final void bindApplication(String packageName, ApplicationInfo info,
             List<ProviderInfo> providers, ComponentName testName,
             String profileName, Bundle testArgs, IInstrumentationWatcher testWatcher, int debugMode,
-            boolean restrictedBackupMode, Configuration config,
+            boolean restrictedBackupMode, Configuration config, CompatibilityInfo compatInfo,
             Map<String, IBinder> services, Bundle coreSettings) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
@@ -738,6 +760,7 @@
         data.writeInt(debugMode);
         data.writeInt(restrictedBackupMode ? 1 : 0);
         config.writeToParcel(data, 0);
+        compatInfo.writeToParcel(data, 0);
         data.writeMap(services);
         data.writeBundle(coreSettings);
         mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null,
@@ -952,6 +975,16 @@
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeBundle(coreSettings);
-        mRemote.transact(SET_CORE_SETTINGS, data, null, IBinder.FLAG_ONEWAY);
+        mRemote.transact(SET_CORE_SETTINGS_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+    }
+
+    public void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeString(pkg);
+        info.writeToParcel(data, 0);
+        mRemote.transact(UPDATE_PACKAGE_COMPATIBILITY_INFO_TRANSACTION, data, null,
+                IBinder.FLAG_ONEWAY);
     }
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index cc1f81c..36b9d72 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1372,7 +1372,7 @@
         }
 
         LoadedApk pi =
-            mMainThread.getPackageInfo(packageName, flags);
+            mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(), flags);
         if (pi != null) {
             ContextImpl c = new ContextImpl();
             c.mRestricted = (flags & CONTEXT_RESTRICTED) == CONTEXT_RESTRICTED;
@@ -1454,7 +1454,7 @@
                         " compatiblity info:" + container.getDisplayMetrics());
             }
             mResources = mainThread.getTopLevelResources(
-                    mPackageInfo.getResDir(), container.getCompatibilityInfo().copy());
+                    mPackageInfo.getResDir(), container.getCompatibilityInfo());
         }
         mMainThread = mainThread;
         mContentResolver = new ApplicationContentResolver(this, mainThread);
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 61e6fc8..4c2ccf4 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -342,6 +342,9 @@
     public int startActivitiesInPackage(int uid,
             Intent[] intents, String[] resolvedTypes, IBinder resultTo) throws RemoteException;
 
+    public void setPackageScreenCompatMode(String packageName, boolean compatEnabled)
+            throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -557,4 +560,5 @@
     int START_ACTIVITIES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+120;
     int START_ACTIVITIES_IN_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+121;
     int ACTIVITY_SLEPT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+122;
+    int SET_PACKAGE_SCREEN_COMPAT_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+123;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 55177a9..93a8ff3 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -23,6 +23,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ProviderInfo;
 import android.content.pm.ServiceInfo;
+import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.Debug;
@@ -52,7 +53,8 @@
     void scheduleResumeActivity(IBinder token, boolean isForward) throws RemoteException;
     void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException;
     void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
-            ActivityInfo info, Bundle state, List<ResultInfo> pendingResults,
+            ActivityInfo info, CompatibilityInfo compatInfo, Bundle state,
+            List<ResultInfo> pendingResults,
     		List<Intent> pendingNewIntents, boolean notResumed, boolean isForward)
     		throws RemoteException;
     void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults,
@@ -61,14 +63,17 @@
     void scheduleNewIntent(List<Intent> intent, IBinder token) throws RemoteException;
     void scheduleDestroyActivity(IBinder token, boolean finished,
             int configChanges) throws RemoteException;
-    void scheduleReceiver(Intent intent, ActivityInfo info, int resultCode,
-            String data, Bundle extras, boolean sync) throws RemoteException;
+    void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo,
+            int resultCode, String data, Bundle extras, boolean sync) throws RemoteException;
     static final int BACKUP_MODE_INCREMENTAL = 0;
     static final int BACKUP_MODE_FULL = 1;
     static final int BACKUP_MODE_RESTORE = 2;
-    void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode) throws RemoteException;
-    void scheduleDestroyBackupAgent(ApplicationInfo app) throws RemoteException;
-    void scheduleCreateService(IBinder token, ServiceInfo info) throws RemoteException;
+    void scheduleCreateBackupAgent(ApplicationInfo app, CompatibilityInfo compatInfo,
+            int backupMode) throws RemoteException;
+    void scheduleDestroyBackupAgent(ApplicationInfo app, CompatibilityInfo compatInfo)
+            throws RemoteException;
+    void scheduleCreateService(IBinder token, ServiceInfo info,
+            CompatibilityInfo compatInfo) throws RemoteException;
     void scheduleBindService(IBinder token,
             Intent intent, boolean rebind) throws RemoteException;
     void scheduleUnbindService(IBinder token,
@@ -82,7 +87,7 @@
     void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers,
             ComponentName testName, String profileName, Bundle testArguments, 
             IInstrumentationWatcher testWatcher, int debugMode, boolean restrictedBackupMode,
-            Configuration config, Map<String, IBinder> services,
+            Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
             Bundle coreSettings) throws RemoteException;
     void scheduleExit() throws RemoteException;
     void scheduleSuicide() throws RemoteException;
@@ -112,6 +117,7 @@
     void dumpActivity(FileDescriptor fd, IBinder servicetoken, String prefix, String[] args)
             throws RemoteException;
     void setCoreSettings(Bundle coreSettings) throws RemoteException;
+    void updatePackageCompatibilityInfo(String pkg, CompatibilityInfo info) throws RemoteException;
 
     String descriptor = "android.app.IApplicationThread";
 
@@ -153,5 +159,6 @@
     int DUMP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+36;
     int CLEAR_DNS_CACHE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+37;
     int SET_HTTP_PROXY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+38;
-    int SET_CORE_SETTINGS = IBinder.FIRST_CALL_TRANSACTION+39;
+    int SET_CORE_SETTINGS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+39;
+    int UPDATE_PACKAGE_COMPATIBILITY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+40;
 }
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index c406524..5307696 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -99,6 +99,7 @@
     }
 
     public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
+            CompatibilityInfo compatInfo,
             ActivityThread mainThread, ClassLoader baseLoader,
             boolean securityViolation, boolean includeCode) {
         mActivityThread = activityThread;
@@ -114,7 +115,7 @@
         mBaseClassLoader = baseLoader;
         mSecurityViolation = securityViolation;
         mIncludeCode = includeCode;
-        mCompatibilityInfo = new CompatibilityInfo(aInfo);
+        mCompatibilityInfo = compatInfo;
 
         if (mAppDir == null) {
             if (ActivityThread.mSystemContext == null) {
@@ -122,7 +123,8 @@
                     ContextImpl.createSystemContext(mainThread);
                 ActivityThread.mSystemContext.getResources().updateConfiguration(
                          mainThread.getConfiguration(),
-                         mainThread.getDisplayMetricsLocked(false));
+                         mainThread.getDisplayMetricsLocked(false),
+                         compatInfo);
                 //Slog.i(TAG, "Created system resources "
                 //        + mSystemContext.getResources() + ": "
                 //        + mSystemContext.getResources().getConfiguration());
@@ -133,7 +135,7 @@
     }
 
     public LoadedApk(ActivityThread activityThread, String name,
-            Context systemContext, ApplicationInfo info) {
+            Context systemContext, ApplicationInfo info, CompatibilityInfo compatInfo) {
         mActivityThread = activityThread;
         mApplicationInfo = info != null ? info : new ApplicationInfo();
         mApplicationInfo.packageName = name;
@@ -149,7 +151,7 @@
         mIncludeCode = true;
         mClassLoader = systemContext.getClassLoader();
         mResources = systemContext.getResources();
-        mCompatibilityInfo = new CompatibilityInfo(mApplicationInfo);
+        mCompatibilityInfo = compatInfo;
     }
 
     public String getPackageName() {
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index e403ac2..ab9bfce 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -21,9 +21,9 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
@@ -34,32 +34,27 @@
  * 
  *  {@hide} 
  */
-public class CompatibilityInfo {
-    private static final boolean DBG = false;
-    private static final String TAG = "CompatibilityInfo";
-    
+public class CompatibilityInfo implements Parcelable {
     /** default compatibility info object for compatible applications */
     public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo() {
-        @Override
-        public void setExpandable(boolean expandable) {
-            throw new UnsupportedOperationException("trying to change default compatibility info");
-        }
     };
 
     /**
-     * The default width of the screen in portrait mode. 
+     * This is the number of pixels we would like to have along the
+     * short axis of an app that needs to run on a normal size screen.
      */
-    public static final int DEFAULT_PORTRAIT_WIDTH = 320;
+    public static final int DEFAULT_NORMAL_SHORT_DIMENSION = 320;
 
     /**
-     * The default height of the screen in portrait mode. 
-     */    
-    public static final int DEFAULT_PORTRAIT_HEIGHT = 480;
+     * This is the maximum aspect ratio we will allow while keeping
+     * applications in a compatible screen size.
+     */
+    public static final float MAXIMUM_ASPECT_RATIO = (854f/480f);
 
     /**
      *  A compatibility flags
      */
-    private int mCompatibilityFlags;
+    private final int mCompatibilityFlags;
     
     /**
      * A flag mask to tell if the application needs scaling (when mApplicationScale != 1.0f)
@@ -68,54 +63,27 @@
     private static final int SCALING_REQUIRED = 1; 
 
     /**
-     * A flag mask to indicates that the application can expand over the original size.
-     * The flag is set to true if
-     * 1) Application declares its expandable in manifest file using <supports-screens> or
-     * 2) Configuration.SCREENLAYOUT_COMPAT_NEEDED is not set
-     * {@see compatibilityFlag}
+     * Has the application said that its UI is expandable?  Based on the
+     * <supports-screen> android:expandible in the manifest.
      */
     private static final int EXPANDABLE = 2;
     
     /**
-     * A flag mask to tell if the application is configured to be expandable. This differs
-     * from EXPANDABLE in that the application that is not expandable will be 
-     * marked as expandable if Configuration.SCREENLAYOUT_COMPAT_NEEDED is not set.
-     */
-    private static final int CONFIGURED_EXPANDABLE = 4; 
-
-    /**
-     * A flag mask to indicates that the application supports large screens.
-     * The flag is set to true if
-     * 1) Application declares it supports large screens in manifest file using <supports-screens> or
-     * 2) The screen size is not large
-     * {@see compatibilityFlag}
+     * Has the application said that its UI supports large screens?  Based on the
+     * <supports-screen> android:largeScreens in the manifest.
      */
     private static final int LARGE_SCREENS = 8;
     
     /**
-     * A flag mask to tell if the application supports large screens. This differs
-     * from LARGE_SCREENS in that the application that does not support large
-     * screens will be marked as supporting them if the current screen is not
-     * large.
-     */
-    private static final int CONFIGURED_LARGE_SCREENS = 16; 
-
-    /**
-     * A flag mask to indicates that the application supports xlarge screens.
-     * The flag is set to true if
-     * 1) Application declares it supports xlarge screens in manifest file using <supports-screens> or
-     * 2) The screen size is not xlarge
-     * {@see compatibilityFlag}
+     * Has the application said that its UI supports xlarge screens?  Based on the
+     * <supports-screen> android:xlargeScreens in the manifest.
      */
     private static final int XLARGE_SCREENS = 32;
     
     /**
-     * A flag mask to tell if the application supports xlarge screens. This differs
-     * from XLARGE_SCREENS in that the application that does not support xlarge
-     * screens will be marked as supporting them if the current screen is not
-     * xlarge.
+     * Set if the application needs to run in screen size compatibility mode.
      */
-    private static final int CONFIGURED_XLARGE_SCREENS = 64;
+    private static final int NEEDS_SCREEN_COMPAT = 128;
 
     /**
      * The effective screen density we have selected for this application.
@@ -132,28 +100,55 @@
      */
     public final float applicationInvertedScale;
 
-    /**
-     * The flags from ApplicationInfo.
-     */
-    public final int appFlags;
-    
-    public CompatibilityInfo(ApplicationInfo appInfo) {
-        appFlags = appInfo.flags;
-        
+    public CompatibilityInfo(ApplicationInfo appInfo, int screenLayout, boolean forceCompat) {
+        int compatFlags = 0;
+
         if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
-            // Saying you support large screens also implies you support xlarge
-            // screens; there is no compatibility mode for a large app on an
-            // xlarge screen.
-            mCompatibilityFlags |= LARGE_SCREENS | CONFIGURED_LARGE_SCREENS
-                    | XLARGE_SCREENS | CONFIGURED_XLARGE_SCREENS
-                    | EXPANDABLE | CONFIGURED_EXPANDABLE;
+            compatFlags |= LARGE_SCREENS;
+            if (!forceCompat) {
+                // If we aren't forcing the app into compatibility mode, then
+                // assume if it supports large screens that we should allow it
+                // to use the full space of an xlarge screen as well.
+                compatFlags |= XLARGE_SCREENS | EXPANDABLE;
+            }
         }
         if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
-            mCompatibilityFlags |= XLARGE_SCREENS | CONFIGURED_XLARGE_SCREENS
-                    | EXPANDABLE | CONFIGURED_EXPANDABLE;
+            compatFlags |= XLARGE_SCREENS | EXPANDABLE;
         }
-        if ((appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
-            mCompatibilityFlags |= EXPANDABLE | CONFIGURED_EXPANDABLE;
+        if (!forceCompat) {
+            // If we are forcing compatibility mode, then ignore an app that
+            // just says it is resizable for screens.  We'll only have it fill
+            // the screen if it explicitly says it supports the screen size we
+            // are running in.
+            if ((appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
+                compatFlags |= EXPANDABLE;
+            }
+        }
+
+        boolean supportsScreen = false;
+        switch (screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK) {
+            case Configuration.SCREENLAYOUT_SIZE_XLARGE:
+                if ((compatFlags&XLARGE_SCREENS) != 0) {
+                    supportsScreen = true;
+                }
+                break;
+            case Configuration.SCREENLAYOUT_SIZE_LARGE:
+                if ((compatFlags&LARGE_SCREENS) != 0) {
+                    supportsScreen = true;
+                }
+                break;
+        }
+
+        if ((screenLayout&Configuration.SCREENLAYOUT_COMPAT_NEEDED) == 0) {
+            if ((compatFlags&EXPANDABLE) != 0) {
+                supportsScreen = true;
+            }
+        }
+
+        if (supportsScreen) {
+            compatFlags &= ~NEEDS_SCREEN_COMPAT;
+        } else {
+            compatFlags |= NEEDS_SCREEN_COMPAT;
         }
         
         if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
@@ -165,13 +160,14 @@
             applicationScale = DisplayMetrics.DENSITY_DEVICE
                     / (float) DisplayMetrics.DENSITY_DEFAULT;
             applicationInvertedScale = 1.0f / applicationScale;
-            mCompatibilityFlags |= SCALING_REQUIRED;
+            compatFlags |= SCALING_REQUIRED;
         }
+
+        mCompatibilityFlags = compatFlags;
     }
 
-    private CompatibilityInfo(int appFlags, int compFlags,
+    private CompatibilityInfo(int compFlags,
             int dens, float scale, float invertedScale) {
-        this.appFlags = appFlags;
         mCompatibilityFlags = compFlags;
         applicationDensity = dens;
         applicationScale = scale;
@@ -179,81 +175,13 @@
     }
 
     private CompatibilityInfo() {
-        this(ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS
-                | ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS
-                | ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS
-                | ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS
-                | ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS,
-                EXPANDABLE | CONFIGURED_EXPANDABLE,
+        this(XLARGE_SCREENS | LARGE_SCREENS | EXPANDABLE,
                 DisplayMetrics.DENSITY_DEVICE,
                 1.0f,
                 1.0f);
     }
 
     /**
-     * Returns the copy of this instance.
-     */
-    public CompatibilityInfo copy() {
-        CompatibilityInfo info = new CompatibilityInfo(appFlags, mCompatibilityFlags,
-                applicationDensity, applicationScale, applicationInvertedScale);
-        return info;
-    }
- 
-    /**
-     * Sets expandable bit in the compatibility flag.
-     */
-    public void setExpandable(boolean expandable) {
-        if (expandable) {
-            mCompatibilityFlags |= CompatibilityInfo.EXPANDABLE;
-        } else {
-            mCompatibilityFlags &= ~CompatibilityInfo.EXPANDABLE;
-        }
-    }
-
-    /**
-     * Sets large screen bit in the compatibility flag.
-     */
-    public void setLargeScreens(boolean expandable) {
-        if (expandable) {
-            mCompatibilityFlags |= CompatibilityInfo.LARGE_SCREENS;
-        } else {
-            mCompatibilityFlags &= ~CompatibilityInfo.LARGE_SCREENS;
-        }
-    }
-
-    /**
-     * Sets large screen bit in the compatibility flag.
-     */
-    public void setXLargeScreens(boolean expandable) {
-        if (expandable) {
-            mCompatibilityFlags |= CompatibilityInfo.XLARGE_SCREENS;
-        } else {
-            mCompatibilityFlags &= ~CompatibilityInfo.XLARGE_SCREENS;
-        }
-    }
-
-    /**
-     * @return true if the application is configured to be expandable.
-     */
-    public boolean isConfiguredExpandable() {
-        return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_EXPANDABLE) != 0;
-    }
-
-    /**
-     * @return true if the application is configured to be expandable.
-     */
-    public boolean isConfiguredLargeScreens() {
-        return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_LARGE_SCREENS) != 0;
-    }
-
-    /**
-     * @return true if the application is configured to be expandable.
-     */
-    public boolean isConfiguredXLargeScreens() {
-        return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_XLARGE_SCREENS) != 0;
-    }
-
-    /**
      * @return true if the scaling is required
      */
     public boolean isScalingRequired() {
@@ -261,14 +189,12 @@
     }
     
     public boolean supportsScreen() {
-        return (mCompatibilityFlags & (EXPANDABLE|LARGE_SCREENS))
-                == (EXPANDABLE|LARGE_SCREENS);
+        return (mCompatibilityFlags&NEEDS_SCREEN_COMPAT) == 0;
     }
     
     @Override
     public String toString() {
-        return "CompatibilityInfo{scale=" + applicationScale +
-                ", supports screen=" + supportsScreen() + "}";
+        return "CompatibilityInfo{scale=" + applicationScale + "}";
     }
 
     /**
@@ -423,24 +349,144 @@
         }
     }
 
+    public void applyToDisplayMetrics(DisplayMetrics inoutDm) {
+        if (!supportsScreen()) {
+            // This is a larger screen device and the app is not
+            // compatible with large screens, so diddle it.
+            CompatibilityInfo.updateCompatibleScreenFrame(inoutDm, null, inoutDm);
+        }
+
+        if (isScalingRequired()) {
+            float invertedRatio = applicationInvertedScale;
+            inoutDm.density *= invertedRatio;
+            inoutDm.densityDpi = (int)((inoutDm.density*DisplayMetrics.DENSITY_DEFAULT)+.5f);
+            inoutDm.scaledDensity *= invertedRatio;
+            inoutDm.xdpi *= invertedRatio;
+            inoutDm.ydpi *= invertedRatio;
+            inoutDm.widthPixels = (int) (inoutDm.widthPixels * invertedRatio + 0.5f);
+            inoutDm.heightPixels = (int) (inoutDm.heightPixels * invertedRatio + 0.5f);
+        }
+    }
+
+    public void applyToConfiguration(Configuration inoutConfig) {
+        if (!supportsScreen()) {
+            // This is a larger screen device and the app is not
+            // compatible with large screens, so we are forcing it to
+            // run as if the screen is normal size.
+            inoutConfig.screenLayout =
+                    (inoutConfig.screenLayout&~Configuration.SCREENLAYOUT_SIZE_MASK)
+                    | Configuration.SCREENLAYOUT_SIZE_NORMAL;
+        }
+    }
+
     /**
-     * Returns the frame Rect for applications runs under compatibility mode.
+     * Compute the frame Rect for applications runs under compatibility mode.
      *
      * @param dm the display metrics used to compute the frame size.
      * @param orientation the orientation of the screen.
      * @param outRect the output parameter which will contain the result.
+     * @return Returns the scaling factor for the window.
      */
-    public static void updateCompatibleScreenFrame(DisplayMetrics dm, int orientation,
-            Rect outRect) {
-        int width = dm.widthPixels;
-        int portraitHeight = (int) (DEFAULT_PORTRAIT_HEIGHT * dm.density + 0.5f);
-        int portraitWidth = (int) (DEFAULT_PORTRAIT_WIDTH * dm.density + 0.5f);
-        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
-            int xOffset = (width - portraitHeight) / 2 ;
-            outRect.set(xOffset, 0, xOffset + portraitHeight, portraitWidth);
+    public static float updateCompatibleScreenFrame(DisplayMetrics dm,
+            Rect outRect, DisplayMetrics outDm) {
+        final int width = dm.realWidthPixels;
+        final int height = dm.realHeightPixels;
+        int shortSize, longSize;
+        if (width < height) {
+            shortSize = width;
+            longSize = height;
         } else {
-            int xOffset = (width - portraitWidth) / 2 ;
-            outRect.set(xOffset, 0, xOffset + portraitWidth, portraitHeight);
+            shortSize = height;
+            longSize = width;
         }
+        int newShortSize = (int)(DEFAULT_NORMAL_SHORT_DIMENSION * dm.density + 0.5f);
+        float aspect = ((float)longSize) / shortSize;
+        if (aspect > MAXIMUM_ASPECT_RATIO) {
+            aspect = MAXIMUM_ASPECT_RATIO;
+        }
+        int newLongSize = (int)(newShortSize * aspect + 0.5f);
+        int newWidth, newHeight;
+        if (width < height) {
+            newWidth = newShortSize;
+            newHeight = newLongSize;
+        } else {
+            newWidth = newLongSize;
+            newHeight = newShortSize;
+        }
+
+        float sw = width/(float)newWidth;
+        float sh = height/(float)newHeight;
+        float scale = sw < sh ? sw : sh;
+        if (scale < 1) {
+            scale = 1;
+        }
+
+        if (outRect != null) {
+            final int left = (int)((width-(newWidth*scale))/2);
+            final int top = (int)((height-(newHeight*scale))/2);
+            outRect.set(left, top, left+newWidth, top+newHeight);
+        }
+
+        if (outDm != null) {
+            outDm.widthPixels = newWidth;
+            outDm.heightPixels = newHeight;
+        }
+
+        return scale;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        try {
+            CompatibilityInfo oc = (CompatibilityInfo)o;
+            if (mCompatibilityFlags != oc.mCompatibilityFlags) return false;
+            if (applicationDensity != oc.applicationDensity) return false;
+            if (applicationScale != oc.applicationScale) return false;
+            if (applicationInvertedScale != oc.applicationInvertedScale) return false;
+            return true;
+        } catch (ClassCastException e) {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        int result = 17;
+        result = 31 * result + mCompatibilityFlags;
+        result = 31 * result + applicationDensity;
+        result = 31 * result + Float.floatToIntBits(applicationScale);
+        result = 31 * result + Float.floatToIntBits(applicationInvertedScale);
+        return result;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mCompatibilityFlags);
+        dest.writeInt(applicationDensity);
+        dest.writeFloat(applicationScale);
+        dest.writeFloat(applicationInvertedScale);
+    }
+
+    public static final Parcelable.Creator<CompatibilityInfo> CREATOR
+            = new Parcelable.Creator<CompatibilityInfo>() {
+        public CompatibilityInfo createFromParcel(Parcel source) {
+            return new CompatibilityInfo(source);
+        }
+
+        public CompatibilityInfo[] newArray(int size) {
+            return new CompatibilityInfo[size];
+        }
+    };
+
+    private CompatibilityInfo(Parcel source) {
+        mCompatibilityFlags = source.readInt();
+        applicationDensity = source.readInt();
+        applicationScale = source.readFloat();
+        applicationInvertedScale = source.readFloat();
     }
 }
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 47c2623..908db11 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -703,11 +703,20 @@
     }
     
     public int hashCode() {
-        return ((int)this.fontScale) + this.mcc + this.mnc
-                + (this.locale != null ? this.locale.hashCode() : 0)
-                + this.touchscreen
-                + this.keyboard + this.keyboardHidden + this.hardKeyboardHidden
-                + this.navigation + this.navigationHidden
-                + this.orientation + this.screenLayout + this.uiMode;
+        int result = 17;
+        result = 31 * result + Float.floatToIntBits(fontScale);
+        result = 31 * result + mcc;
+        result = 31 * result + mnc;
+        result = 31 * result + (locale != null ? locale.hashCode() : 0);
+        result = 31 * result + touchscreen;
+        result = 31 * result + keyboard;
+        result = 31 * result + keyboardHidden;
+        result = 31 * result + hardKeyboardHidden;
+        result = 31 * result + navigation;
+        result = 31 * result + navigationHidden;
+        result = 31 * result + orientation;
+        result = 31 * result + screenLayout;
+        result = 31 * result + uiMode;
+        return result;
     }
 }
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 81eb09c..00b49e8 100755
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -91,6 +91,7 @@
     private static boolean mPreloaded;
 
     /*package*/ final TypedValue mTmpValue = new TypedValue();
+    /*package*/ final Configuration mTmpConfig = new Configuration();
 
     // These are protected by the mTmpValue lock.
     private final LongSparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache
@@ -1400,18 +1401,30 @@
      */
     public void updateConfiguration(Configuration config,
             DisplayMetrics metrics) {
+        updateConfiguration(config, metrics, null);
+    }
+
+    /**
+     * @hide
+     */
+    public void updateConfiguration(Configuration config,
+            DisplayMetrics metrics, CompatibilityInfo compat) {
         synchronized (mTmpValue) {
+            if (compat != null) {
+                mCompatibilityInfo = compat;
+            }
             int configChanges = 0xfffffff;
             if (config != null) {
-                configChanges = mConfiguration.updateFrom(config);
+                mTmpConfig.setTo(config);
+                mCompatibilityInfo.applyToConfiguration(mTmpConfig);
+                configChanges = mConfiguration.updateFrom(mTmpConfig);
             }
             if (mConfiguration.locale == null) {
                 mConfiguration.locale = Locale.getDefault();
             }
             if (metrics != null) {
                 mMetrics.setTo(metrics);
-                mMetrics.updateMetrics(mCompatibilityInfo,
-                        mConfiguration.orientation, mConfiguration.screenLayout);
+                mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
             }
             mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
 
@@ -1500,15 +1513,23 @@
      *
      * @hide
      */
-    public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics) {
+    public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics,
+            CompatibilityInfo compat) {
         if (mSystem != null) {
-            mSystem.updateConfiguration(config, metrics);
+            mSystem.updateConfiguration(config, metrics, compat);
             //Log.i(TAG, "Updated system resources " + mSystem
             //        + ": " + mSystem.getConfiguration());
         }
     }
 
     /**
+     * @hide
+     */
+    public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics) {
+        updateSystemConfiguration(config, metrics, null);
+    }
+    
+    /**
      * Return the current display metrics that are in effect for this resource 
      * object.  The returned object should be treated as read-only.
      * 
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 63baf14..8018ff9 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -16,9 +16,7 @@
 
 package android.util;
 
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.os.*;
+import android.os.SystemProperties;
 
 
 /**
@@ -107,6 +105,11 @@
      */
     public float ydpi;
 
+    /** @hide */
+    public int realWidthPixels;
+    /** @hide */
+    public int realHeightPixels;
+
     public DisplayMetrics() {
     }
     
@@ -118,6 +121,8 @@
         scaledDensity = o.scaledDensity;
         xdpi = o.xdpi;
         ydpi = o.ydpi;
+        realWidthPixels = o.realWidthPixels;
+        realHeightPixels = o.realHeightPixels;
     }
     
     public void setToDefaults() {
@@ -128,101 +133,8 @@
         scaledDensity = density;
         xdpi = DENSITY_DEVICE;
         ydpi = DENSITY_DEVICE;
-    }
-
-    /**
-     * Update the display metrics based on the compatibility info and orientation
-     * NOTE: DO NOT EXPOSE THIS API!  It is introducing a circular dependency
-     * with the higher-level android.res package.
-     * {@hide}
-     */
-    public void updateMetrics(CompatibilityInfo compatibilityInfo, int orientation,
-            int screenLayout) {
-        boolean expandable = compatibilityInfo.isConfiguredExpandable();
-        boolean largeScreens = compatibilityInfo.isConfiguredLargeScreens();
-        boolean xlargeScreens = compatibilityInfo.isConfiguredXLargeScreens();
-        
-        // Note: this assume that configuration is updated before calling
-        // updateMetrics method.
-        if (!expandable) {
-            if ((screenLayout&Configuration.SCREENLAYOUT_COMPAT_NEEDED) == 0) {
-                expandable = true;
-                // the current screen size is compatible with non-resizing apps.
-                compatibilityInfo.setExpandable(true);
-            } else {
-                compatibilityInfo.setExpandable(false);
-            }
-        }
-        if (!largeScreens) {
-            if ((screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK)
-                    != Configuration.SCREENLAYOUT_SIZE_LARGE) {
-                largeScreens = true;
-                // the current screen size is not large.
-                compatibilityInfo.setLargeScreens(true);
-            } else {
-                compatibilityInfo.setLargeScreens(false);
-            }
-        }
-        if (!xlargeScreens) {
-            if ((screenLayout&Configuration.SCREENLAYOUT_SIZE_MASK)
-                    != Configuration.SCREENLAYOUT_SIZE_XLARGE) {
-                xlargeScreens = true;
-                // the current screen size is not large.
-                compatibilityInfo.setXLargeScreens(true);
-            } else {
-                compatibilityInfo.setXLargeScreens(false);
-            }
-        }
-        
-        if (!expandable || (!largeScreens && !xlargeScreens)) {
-            // This is a larger screen device and the app is not 
-            // compatible with large screens, so diddle it.
-            
-            // Figure out the compatibility width and height of the screen.
-            int defaultWidth;
-            int defaultHeight;
-            switch (orientation) {
-                case Configuration.ORIENTATION_LANDSCAPE: {
-                    defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density +
-                            0.5f);
-                    defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density +
-                            0.5f);
-                    break;
-                }
-                case Configuration.ORIENTATION_PORTRAIT:
-                case Configuration.ORIENTATION_SQUARE:
-                default: {
-                    defaultWidth = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_WIDTH * density +
-                            0.5f);
-                    defaultHeight = (int)(CompatibilityInfo.DEFAULT_PORTRAIT_HEIGHT * density +
-                            0.5f);
-                    break;
-                }
-                case Configuration.ORIENTATION_UNDEFINED: {
-                    // don't change
-                    return;
-                }
-            }
-            
-            if (defaultWidth < widthPixels) {
-                // content/window's x offset in original pixels
-                widthPixels = defaultWidth;
-            }
-            if (defaultHeight < heightPixels) {
-                heightPixels = defaultHeight;
-            }
-        }
-        
-        if (compatibilityInfo.isScalingRequired()) {
-            float invertedRatio = compatibilityInfo.applicationInvertedScale;
-            density *= invertedRatio;
-            densityDpi = (int)((density*DisplayMetrics.DENSITY_DEFAULT)+.5f);
-            scaledDensity *= invertedRatio;
-            xdpi *= invertedRatio;
-            ydpi *= invertedRatio;
-            widthPixels = (int) (widthPixels * invertedRatio + 0.5f);
-            heightPixels = (int) (heightPixels * invertedRatio + 0.5f);
-        }
+        realWidthPixels = 0;
+        realHeightPixels = 0;
     }
 
     @Override
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 126f409..89767f2 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -139,6 +139,9 @@
         outMetrics.scaledDensity= outMetrics.density;
         outMetrics.xdpi         = mDpiX;
         outMetrics.ydpi         = mDpiY;
+
+        outMetrics.realWidthPixels  = outMetrics.widthPixels;
+        outMetrics.realHeightPixels = outMetrics.heightPixels;
     }
 
     /*
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 334c68e..2058991 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -458,6 +458,18 @@
     public int getMaxWallpaperLayer();
     
     /**
+     * Return the display width available after excluding the window
+     * decor.
+     */
+    public int getNonDecorDisplayWidth(int fullWidth);
+
+    /**
+     * Return the display height available after excluding the screen
+     * decor.
+     */
+    public int getNonDecorDisplayHeight(int fullHeight);
+
+    /**
      * Return whether the given window should forcibly hide everything
      * behind it.  Typically returns true for the keyguard.
      */