From e2515eebf42c763c0a2d9f873a153711778cfc17 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 27 Apr 2011 18:52:56 -0400 Subject: 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 --- cmds/am/src/com/android/commands/am/Am.java | 27 ++ core/java/android/app/ActivityManagerNative.java | 23 ++ core/java/android/app/ActivityThread.java | 167 +++++++--- core/java/android/app/ApplicationThreadNative.java | 65 +++- core/java/android/app/ContextImpl.java | 4 +- core/java/android/app/IActivityManager.java | 4 + core/java/android/app/IApplicationThread.java | 23 +- core/java/android/app/LoadedApk.java | 10 +- .../android/content/res/CompatibilityInfo.java | 358 ++++++++++++--------- core/java/android/content/res/Configuration.java | 21 +- core/java/android/content/res/Resources.java | 31 +- core/java/android/util/DisplayMetrics.java | 108 +------ core/java/android/view/Display.java | 3 + core/java/android/view/WindowManagerPolicy.java | 12 + include/ui/Input.h | 2 + libs/ui/Input.cpp | 47 ++- .../internal/policy/impl/PhoneWindowManager.java | 13 +- services/input/InputDispatcher.cpp | 71 +++- services/input/InputDispatcher.h | 7 +- services/input/InputWindow.cpp | 3 +- services/input/InputWindow.h | 6 + .../android/server/am/ActivityManagerService.java | 103 +++++- .../java/com/android/server/am/ActivityRecord.java | 4 +- .../java/com/android/server/am/ActivityStack.java | 11 +- .../java/com/android/server/am/ProcessRecord.java | 1 + .../java/com/android/server/wm/InputMonitor.java | 11 +- .../java/com/android/server/wm/InputWindow.java | 4 + .../android/server/wm/WindowManagerService.java | 65 ++-- .../java/com/android/server/wm/WindowState.java | 129 ++++++-- services/jni/com_android_server_InputWindow.cpp | 6 + .../google/android/test/dpi/DpiTestActivity.java | 3 +- 31 files changed, 891 insertions(+), 451 deletions(-) diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index b07300411b9d..5c8abe493aba 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -106,6 +106,8 @@ public class Am { runDumpHeap(); } else if (op.equals("monitor")) { runMonitor(); + } else if (op.equals("screen-compat")) { + runScreenCompat(); } else { throw new IllegalArgumentException("Unknown command: " + op); } @@ -776,6 +778,29 @@ public class Am { controller.run(); } + private void runScreenCompat() throws Exception { + String mode = nextArgRequired(); + boolean enabled; + if ("on".equals(mode)) { + enabled = true; + } else if ("off".equals(mode)) { + enabled = false; + } else { + System.err.println("Error: enabled mode must be 'on' or 'off' at " + mode); + showUsage(); + return; + } + + String packageName = nextArgRequired(); + do { + try { + mAm.setPackageScreenCompatMode(packageName, enabled); + } catch (RemoteException e) { + } + packageName = nextArg(); + } while (packageName != null); + } + private class IntentReceiver extends IIntentReceiver.Stub { private boolean mFinished = false; @@ -956,6 +981,8 @@ public class Am { " start monitoring: am monitor [--gdb ]\n" + " --gdb: start gdbserv on the given port at crash/ANR\n" + "\n" + + " control screen compatibility: am screen-compat [on|off] [package]\n" + + "\n" + " specifications include these flags:\n" + " [-a ] [-d ] [-t ]\n" + " [-c [-c ] ...]\n" + diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 64266354edcc..11e9975eb610 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1398,6 +1398,16 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM 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 @@ class ActivityManagerProxy implements IActivityManager 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 bd83762e33d8..67e88396a883 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -202,7 +202,7 @@ public final class ActivityThread { Bundle mCoreSettings = null; - private static final class ActivityClientRecord { + static final class ActivityClientRecord { IBinder token; int ident; Intent intent; @@ -220,6 +220,7 @@ public final class ActivityThread { ActivityClientRecord nextIdle; ActivityInfo activityInfo; + CompatibilityInfo compatInfo; LoadedApk packageInfo; List pendingResults; @@ -260,7 +261,7 @@ public final class ActivityThread { } } - 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 @@ public final class ActivityThread { } } - private static final class NewIntentData { + static final class NewIntentData { List intents; IBinder token; public String toString() { @@ -285,7 +286,7 @@ public final class ActivityThread { } } - 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 @@ public final class ActivityThread { Intent intent; ActivityInfo info; + CompatibilityInfo compatInfo; public String toString() { return "ReceiverData{intent=" + intent + " packageName=" + info.packageName + " resultCode=" + getResultCode() @@ -302,8 +304,9 @@ public final class ActivityThread { } } - 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 @@ public final class ActivityThread { } } - 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 @@ public final class ActivityThread { } } - private static final class BindServiceData { + static final class BindServiceData { IBinder token; Intent intent; boolean rebind; @@ -332,7 +336,7 @@ public final class ActivityThread { } } - private static final class ServiceArgsData { + static final class ServiceArgsData { IBinder token; int startId; int flags; @@ -343,7 +347,7 @@ public final class ActivityThread { } } - private static final class AppBindData { + static final class AppBindData { LoadedApk info; String processName; ApplicationInfo appInfo; @@ -355,13 +359,14 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { boolean dumped; } - private static final class ResultData { + static final class ResultData { IBinder token; List results; public String toString() { @@ -377,22 +382,27 @@ public final class ActivityThread { } } - 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 @@ public final class ActivityThread { // 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 pendingResults, + ActivityInfo info, CompatibilityInfo compatInfo, Bundle state, + List pendingResults, List pendingNewIntents, boolean notResumed, boolean isForward) { ActivityClientRecord r = new ActivityClientRecord(); @@ -451,6 +462,7 @@ public final class ActivityThread { r.ident = ident; r.intent = intent; r.activityInfo = info; + r.compatInfo = compatInfo; r.state = state; r.pendingResults = pendingResults; @@ -484,33 +496,40 @@ public final class ActivityThread { } 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 @@ public final class ActivityThread { ComponentName instrumentationName, String profileFile, Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, int debugMode, boolean isRestrictedBackupMode, Configuration config, - Map services, Bundle coreSettings) { + CompatibilityInfo compatInfo, Map services, + Bundle coreSettings) { if (services != null) { // Setup the service cache in the ServiceManager @@ -573,6 +593,7 @@ public final class ActivityThread { data.debugMode = debugMode; data.restrictedBackupMode = isRestrictedBackupMode; data.config = config; + data.compatInfo = compatInfo; queueOrSendMessage(H.BIND_APPLICATION, data); } @@ -903,6 +924,13 @@ public final class ActivityThread { 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 final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { return mH; } - public final LoadedApk getPackageInfo(String packageName, int flags) { + public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo, + int flags) { synchronized (mPackages) { WeakReference ref; if ((flags&Context.CONTEXT_INCLUDE_CODE) != 0) { @@ -1369,13 +1402,14 @@ public final class ActivityThread { } 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 @@ public final class ActivityThread { 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 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 ref; @@ -1419,7 +1466,7 @@ public final class ActivityThread { ? 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 @@ public final class ActivityThread { 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 final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { } } + 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 results) { final int N = results.size(); for (int i=0; i callbacks = null; @@ -3297,15 +3359,21 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { * 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 @@ public final class ActivityThread { 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 @@ public final class ActivityThread { // 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 aa26b0471fdf..e1d76a44e477 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -23,6 +23,7 @@ import android.content.pm.ActivityInfo; 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 @@ public abstract class ApplicationThreadNative extends Binder 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 ri = data.createTypedArrayList(ResultInfo.CREATOR); List 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 @@ public abstract class ApplicationThreadNative extends Binder 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 @@ public abstract class ApplicationThreadNative extends Binder 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 @@ public abstract class ApplicationThreadNative extends Binder int testMode = data.readInt(); boolean restrictedBackupMode = (data.readInt() != 0); Configuration config = Configuration.CREATOR.createFromParcel(data); + CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data); HashMap 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 @@ public abstract class ApplicationThreadNative extends Binder { 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 @@ public abstract class ApplicationThreadNative extends Binder { 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 @@ public abstract class ApplicationThreadNative extends Binder 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 @@ class ApplicationThreadProxy implements IApplicationThread { } public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident, - ActivityInfo info, Bundle state, List pendingResults, + ActivityInfo info, CompatibilityInfo compatInfo, Bundle state, + List pendingResults, List pendingNewIntents, boolean notResumed, boolean isForward) throws RemoteException { Parcel data = Parcel.obtain(); @@ -563,6 +579,7 @@ class ApplicationThreadProxy implements IApplicationThread { 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 @@ class ApplicationThreadProxy implements IApplicationThread { } 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 @@ class ApplicationThreadProxy implements IApplicationThread { 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 @@ class ApplicationThreadProxy implements IApplicationThread { public final void bindApplication(String packageName, ApplicationInfo info, List providers, ComponentName testName, String profileName, Bundle testArgs, IInstrumentationWatcher testWatcher, int debugMode, - boolean restrictedBackupMode, Configuration config, + boolean restrictedBackupMode, Configuration config, CompatibilityInfo compatInfo, Map services, Bundle coreSettings) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); @@ -738,6 +760,7 @@ class ApplicationThreadProxy implements IApplicationThread { 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 @@ class ApplicationThreadProxy implements IApplicationThread { 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 cc1f81c5ec00..36b9d72de45d 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1372,7 +1372,7 @@ class ContextImpl extends Context { } 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 @@ class ContextImpl extends Context { " 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 61e6fc8466c2..4c2ccf49f707 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -342,6 +342,9 @@ public interface IActivityManager extends IInterface { 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 @@ public interface IActivityManager extends IInterface { 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 55177a90592a..93a8ff33340f 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -23,6 +23,7 @@ import android.content.pm.ActivityInfo; 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 @@ public interface IApplicationThread extends IInterface { void scheduleResumeActivity(IBinder token, boolean isForward) throws RemoteException; void scheduleSendResult(IBinder token, List results) throws RemoteException; void scheduleLaunchActivity(Intent intent, IBinder token, int ident, - ActivityInfo info, Bundle state, List pendingResults, + ActivityInfo info, CompatibilityInfo compatInfo, Bundle state, + List pendingResults, List pendingNewIntents, boolean notResumed, boolean isForward) throws RemoteException; void scheduleRelaunchActivity(IBinder token, List pendingResults, @@ -61,14 +63,17 @@ public interface IApplicationThread extends IInterface { void scheduleNewIntent(List 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 @@ public interface IApplicationThread extends IInterface { void bindApplication(String packageName, ApplicationInfo info, List providers, ComponentName testName, String profileName, Bundle testArguments, IInstrumentationWatcher testWatcher, int debugMode, boolean restrictedBackupMode, - Configuration config, Map services, + Configuration config, CompatibilityInfo compatInfo, Map services, Bundle coreSettings) throws RemoteException; void scheduleExit() throws RemoteException; void scheduleSuicide() throws RemoteException; @@ -112,6 +117,7 @@ public interface IApplicationThread extends IInterface { 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 @@ public interface IApplicationThread extends IInterface { 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 c406524439a7..5307696eb6c3 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -99,6 +99,7 @@ final class LoadedApk { } public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo, + CompatibilityInfo compatInfo, ActivityThread mainThread, ClassLoader baseLoader, boolean securityViolation, boolean includeCode) { mActivityThread = activityThread; @@ -114,7 +115,7 @@ final class LoadedApk { mBaseClassLoader = baseLoader; mSecurityViolation = securityViolation; mIncludeCode = includeCode; - mCompatibilityInfo = new CompatibilityInfo(aInfo); + mCompatibilityInfo = compatInfo; if (mAppDir == null) { if (ActivityThread.mSystemContext == null) { @@ -122,7 +123,8 @@ final class LoadedApk { 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 @@ final class LoadedApk { } 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 @@ final class LoadedApk { 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 e403ac24242e..ab9bfce6e438 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -21,9 +21,9 @@ import android.graphics.Canvas; 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 @@ import android.view.WindowManager.LayoutParams; * * {@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 @@ public class CompatibilityInfo { 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 or - * 2) Configuration.SCREENLAYOUT_COMPAT_NEEDED is not set - * {@see compatibilityFlag} + * Has the application said that its UI is expandable? Based on the + * 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 or - * 2) The screen size is not large - * {@see compatibilityFlag} + * Has the application said that its UI supports large screens? Based on the + * 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 or - * 2) The screen size is not xlarge - * {@see compatibilityFlag} + * Has the application said that its UI supports xlarge screens? Based on the + * 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 class CompatibilityInfo { */ 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 (!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 ((appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) { - mCompatibilityFlags |= EXPANDABLE | CONFIGURED_EXPANDABLE; + + if (supportsScreen) { + compatFlags &= ~NEEDS_SCREEN_COMPAT; + } else { + compatFlags |= NEEDS_SCREEN_COMPAT; } if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) { @@ -165,13 +160,14 @@ public class CompatibilityInfo { 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,80 +175,12 @@ public class CompatibilityInfo { } 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 */ @@ -261,14 +189,12 @@ public class CompatibilityInfo { } 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 class CompatibilityInfo { } } + 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 { + 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 { - int xOffset = (width - portraitWidth) / 2 ; - outRect.set(xOffset, 0, xOffset + portraitWidth, portraitHeight); + 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 CREATOR + = new Parcelable.Creator() { + 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 47c2623c83c3..908db118245e 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -703,11 +703,20 @@ public final class Configuration implements Parcelable, Comparable > mDrawableCache @@ -1400,18 +1401,30 @@ public class Resources { */ 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,14 +1513,22 @@ public class Resources { * * @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 63baf145b991..8018ff901fbe 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 class DisplayMetrics { */ public float ydpi; + /** @hide */ + public int realWidthPixels; + /** @hide */ + public int realHeightPixels; + public DisplayMetrics() { } @@ -118,6 +121,8 @@ public class DisplayMetrics { scaledDensity = o.scaledDensity; xdpi = o.xdpi; ydpi = o.ydpi; + realWidthPixels = o.realWidthPixels; + realHeightPixels = o.realHeightPixels; } public void setToDefaults() { @@ -128,101 +133,8 @@ public class DisplayMetrics { 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 126f409d1dab..89767f2a13e1 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -139,6 +139,9 @@ public class Display 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 334c68e9c06a..2058991aa735 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -457,6 +457,18 @@ public interface WindowManagerPolicy { */ 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. diff --git a/include/ui/Input.h b/include/ui/Input.h index d9d77c4e5089..bc5b718fb8b3 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -203,6 +203,8 @@ struct PointerCoords { status_t setAxisValue(int32_t axis, float value); float* editAxisValue(int32_t axis); + void scale(float scale); + #ifdef HAVE_ANDROID_OS status_t readFromParcel(Parcel* parcel); status_t writeToParcel(Parcel* parcel) const; diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp index e2e698ef7b3b..19d590ab20b3 100644 --- a/libs/ui/Input.cpp +++ b/libs/ui/Input.cpp @@ -298,6 +298,24 @@ float* PointerCoords::editAxisValue(int32_t axis) { return &values[index]; } +static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) { + float* value = c.editAxisValue(axis); + if (value) { + *value *= scaleFactor; + } +} + +void PointerCoords::scale(float scaleFactor) { + // No need to scale pressure or size since they are normalized. + // No need to scale orientation since it is meaningless to do so. + scaleAxisValue(*this, AMOTION_EVENT_AXIS_X, scaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_Y, scaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor); + scaleAxisValue(*this, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor); +} + #ifdef HAVE_ANDROID_OS status_t PointerCoords::readFromParcel(Parcel* parcel) { bits = parcel->readInt64(); @@ -411,11 +429,9 @@ float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { float value = getRawPointerCoords(pointerIndex)->getAxisValue(axis); switch (axis) { case AMOTION_EVENT_AXIS_X: - value += mXOffset; - break; + return value + mXOffset; case AMOTION_EVENT_AXIS_Y: - value += mYOffset; - break; + return value + mYOffset; } return value; } @@ -435,11 +451,9 @@ float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, float value = getHistoricalRawPointerCoords(pointerIndex, historicalIndex)->getAxisValue(axis); switch (axis) { case AMOTION_EVENT_AXIS_X: - value += mXOffset; - break; + return value + mXOffset; case AMOTION_EVENT_AXIS_Y: - value += mYOffset; - break; + return value + mYOffset; } return value; } @@ -449,13 +463,6 @@ void MotionEvent::offsetLocation(float xOffset, float yOffset) { mYOffset += yOffset; } -static inline void scaleAxisValue(PointerCoords& c, int axis, float scaleFactor) { - float* value = c.editAxisValue(axis); - if (value) { - *value *= scaleFactor; - } -} - void MotionEvent::scale(float scaleFactor) { mXOffset *= scaleFactor; mYOffset *= scaleFactor; @@ -464,15 +471,7 @@ void MotionEvent::scale(float scaleFactor) { size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { - PointerCoords& c = mSamplePointerCoords.editItemAt(i); - // No need to scale pressure or size since they are normalized. - // No need to scale orientation since it is meaningless to do so. - scaleAxisValue(c, AMOTION_EVENT_AXIS_X, scaleFactor); - scaleAxisValue(c, AMOTION_EVENT_AXIS_Y, scaleFactor); - scaleAxisValue(c, AMOTION_EVENT_AXIS_TOUCH_MAJOR, scaleFactor); - scaleAxisValue(c, AMOTION_EVENT_AXIS_TOUCH_MINOR, scaleFactor); - scaleAxisValue(c, AMOTION_EVENT_AXIS_TOOL_MAJOR, scaleFactor); - scaleAxisValue(c, AMOTION_EVENT_AXIS_TOOL_MINOR, scaleFactor); + mSamplePointerCoords.editItemAt(i).scale(scaleFactor); } } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 8e18f2a1040e..29c574138f4d 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -35,7 +35,6 @@ import android.database.ContentObserver; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Binder; -import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.LocalPowerManager; @@ -56,7 +55,6 @@ import com.android.internal.telephony.ITelephony; import com.android.internal.view.BaseInputHandler; import com.android.internal.widget.PointerLocationView; -import android.telephony.TelephonyManager; import android.util.Config; import android.util.EventLog; import android.util.Log; @@ -231,6 +229,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mSafeMode; WindowState mStatusBar = null; boolean mStatusBarCanHide; + int mScreenMarginBottom; final ArrayList mStatusBarPanels = new ArrayList(); WindowState mKeyguard = null; KeyguardViewMediator mKeyguardMediator; @@ -1032,6 +1031,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { return STATUS_BAR_LAYER; } + public int getNonDecorDisplayWidth(int fullWidth) { + return fullWidth; + } + + public int getNonDecorDisplayHeight(int fullHeight) { + return fullHeight - mScreenMarginBottom; + } + public boolean doesForceHide(WindowState win, WindowManager.LayoutParams attrs) { return attrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD; } @@ -1181,6 +1188,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // The Configuration will be stable by now, so we can load this mStatusBarCanHide = mContext.getResources().getBoolean( com.android.internal.R.bool.config_statusBarCanHide); + mScreenMarginBottom = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.screen_margin_bottom); break; case TYPE_STATUS_BAR_PANEL: diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp index 19295e6d77ca..f8a5cfb7d02a 100644 --- a/services/input/InputDispatcher.cpp +++ b/services/input/InputDispatcher.cpp @@ -156,6 +156,14 @@ static bool validateMotionEvent(int32_t action, size_t pointerCount, return true; } +static void scalePointerCoords(const PointerCoords* inCoords, size_t count, float scaleFactor, + PointerCoords* outCoords) { + for (size_t i = 0; i < count; i++) { + outCoords[i] = inCoords[i]; + outCoords[i].scale(scaleFactor); + } +} + static void dumpRegion(String8& dump, const SkRegion& region) { if (region.isEmpty()) { dump.append(""); @@ -1494,6 +1502,7 @@ void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t t target.flags = targetFlags; target.xOffset = - window->frameLeft; target.yOffset = - window->frameTop; + target.scaleFactor = window->scaleFactor; target.pointerIds = pointerIds; } @@ -1506,6 +1515,7 @@ void InputDispatcher::addMonitoringTargetsLocked() { target.flags = 0; target.xOffset = 0; target.yOffset = 0; + target.scaleFactor = 1.0f; } } @@ -1607,12 +1617,12 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, bool resumeWithAppendedMotionSample) { #if DEBUG_DISPATCH_CYCLE LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, " - "xOffset=%f, yOffset=%f, " + "xOffset=%f, yOffset=%f, scaleFactor=%f" "pointerIds=0x%x, " "resumeWithAppendedMotionSample=%s", connection->getInputChannelName(), inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset, - inputTarget->pointerIds.value, + inputTarget->scaleFactor, inputTarget->pointerIds.value, toString(resumeWithAppendedMotionSample)); #endif @@ -1693,8 +1703,19 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, // consumed the motion event (or if the channel is broken). MotionEntry* motionEntry = static_cast(eventEntry); MotionSample* appendedMotionSample = motionEntry->lastSample; - status_t status = connection->inputPublisher.appendMotionSample( - appendedMotionSample->eventTime, appendedMotionSample->pointerCoords); + status_t status; + if (motionEventDispatchEntry->scaleFactor == 1.0f) { + status = connection->inputPublisher.appendMotionSample( + appendedMotionSample->eventTime, appendedMotionSample->pointerCoords); + } else { + PointerCoords scaledCoords[MAX_POINTERS]; + for (size_t i = 0; i < motionEntry->pointerCount; i++) { + scaledCoords[i] = appendedMotionSample->pointerCoords[i]; + scaledCoords[i].scale(motionEventDispatchEntry->scaleFactor); + } + status = connection->inputPublisher.appendMotionSample( + appendedMotionSample->eventTime, scaledCoords); + } if (status == OK) { #if DEBUG_BATCHING LOGD("channel '%s' ~ Successfully streamed new motion sample.", @@ -1731,7 +1752,8 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref - inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset); + inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset, + inputTarget->scaleFactor); if (dispatchEntry->hasForegroundTarget()) { incrementPendingForegroundDispatchesLocked(eventEntry); } @@ -1827,24 +1849,34 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, firstMotionSample = & motionEntry->firstSample; } + PointerCoords scaledCoords[MAX_POINTERS]; + const PointerCoords* usingCoords = firstMotionSample->pointerCoords; + // Set the X and Y offset depending on the input source. - float xOffset, yOffset; + float xOffset, yOffset, scaleFactor; if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) { - xOffset = dispatchEntry->xOffset; - yOffset = dispatchEntry->yOffset; + scaleFactor = dispatchEntry->scaleFactor; + xOffset = dispatchEntry->xOffset * scaleFactor; + yOffset = dispatchEntry->yOffset * scaleFactor; + if (scaleFactor != 1.0f) { + for (size_t i = 0; i < motionEntry->pointerCount; i++) { + scaledCoords[i] = firstMotionSample->pointerCoords[i]; + scaledCoords[i].scale(scaleFactor); + } + usingCoords = scaledCoords; + } } else { xOffset = 0.0f; yOffset = 0.0f; + scaleFactor = 1.0f; } // Publish the motion event and the first motion sample. status = connection->inputPublisher.publishMotionEvent(motionEntry->deviceId, motionEntry->source, action, flags, motionEntry->edgeFlags, motionEntry->metaState, - xOffset, yOffset, - motionEntry->xPrecision, motionEntry->yPrecision, + xOffset, yOffset, motionEntry->xPrecision, motionEntry->yPrecision, motionEntry->downTime, firstMotionSample->eventTime, - motionEntry->pointerCount, motionEntry->pointerIds, - firstMotionSample->pointerCoords); + motionEntry->pointerCount, motionEntry->pointerIds, usingCoords); if (status) { LOGE("channel '%s' ~ Could not publish motion event, " @@ -1857,7 +1889,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, MotionSample* nextMotionSample = firstMotionSample->next; for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) { status = connection->inputPublisher.appendMotionSample( - nextMotionSample->eventTime, nextMotionSample->pointerCoords); + nextMotionSample->eventTime, usingCoords); if (status == NO_MEMORY) { #if DEBUG_DISPATCH_CYCLE LOGD("channel '%s' ~ Shared memory buffer full. Some motion samples will " @@ -2095,18 +2127,21 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( } int32_t xOffset, yOffset; + float scaleFactor; const InputWindow* window = getWindowLocked(connection->inputChannel); if (window) { xOffset = -window->frameLeft; yOffset = -window->frameTop; + scaleFactor = window->scaleFactor; } else { xOffset = 0; yOffset = 0; + scaleFactor = 1.0f; } DispatchEntry* cancelationDispatchEntry = mAllocator.obtainDispatchEntry(cancelationEventEntry, // increments ref - 0, xOffset, yOffset); + 0, xOffset, yOffset, scaleFactor); connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry); mAllocator.releaseEventEntry(cancelationEventEntry); @@ -2957,7 +2992,7 @@ void InputDispatcher::dumpDispatchStateLocked(String8& dump) { const InputWindow& window = mWindows[i]; dump.appendFormat(INDENT2 "%d: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, " "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, " - "frame=[%d,%d][%d,%d], " + "frame=[%d,%d][%d,%d], scale=%f, " "touchableRegion=", i, window.name.string(), toString(window.paused), @@ -2968,7 +3003,8 @@ void InputDispatcher::dumpDispatchStateLocked(String8& dump) { window.layoutParamsFlags, window.layoutParamsType, window.layer, window.frameLeft, window.frameTop, - window.frameRight, window.frameBottom); + window.frameRight, window.frameBottom, + window.scaleFactor); dumpRegion(dump, window.touchableRegion); dump.appendFormat(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", window.ownerPid, window.ownerUid, @@ -3460,13 +3496,14 @@ InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsec InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry( EventEntry* eventEntry, - int32_t targetFlags, float xOffset, float yOffset) { + int32_t targetFlags, float xOffset, float yOffset, float scaleFactor) { DispatchEntry* entry = mDispatchEntryPool.alloc(); entry->eventEntry = eventEntry; eventEntry->refCount += 1; entry->targetFlags = targetFlags; entry->xOffset = xOffset; entry->yOffset = yOffset; + entry->scaleFactor = scaleFactor; entry->inProgress = false; entry->headMotionSample = NULL; entry->tailMotionSample = NULL; diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h index 1e118c4d2891..dd89328dd015 100644 --- a/services/input/InputDispatcher.h +++ b/services/input/InputDispatcher.h @@ -112,6 +112,10 @@ struct InputTarget { // (ignored for KeyEvents) float xOffset, yOffset; + // Scaling factor to apply to MotionEvent as it is delivered. + // (ignored for KeyEvents) + float scaleFactor; + // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. BitSet32 pointerIds; @@ -431,6 +435,7 @@ private: int32_t targetFlags; float xOffset; float yOffset; + float scaleFactor; // True if dispatch has started. bool inProgress; @@ -559,7 +564,7 @@ private: nsecs_t downTime, uint32_t pointerCount, const int32_t* pointerIds, const PointerCoords* pointerCoords); DispatchEntry* obtainDispatchEntry(EventEntry* eventEntry, - int32_t targetFlags, float xOffset, float yOffset); + int32_t targetFlags, float xOffset, float yOffset, float scaleFactor); CommandEntry* obtainCommandEntry(Command command); void releaseInjectionState(InjectionState* injectionState); diff --git a/services/input/InputWindow.cpp b/services/input/InputWindow.cpp index b552f6d5b43d..ccea9e45535e 100644 --- a/services/input/InputWindow.cpp +++ b/services/input/InputWindow.cpp @@ -29,8 +29,7 @@ bool InputWindow::touchableRegionContainsPoint(int32_t x, int32_t y) const { } bool InputWindow::frameContainsPoint(int32_t x, int32_t y) const { - return x >= frameLeft && x <= frameRight - && y >= frameTop && y <= frameBottom; + return x <= frameRight || y <= frameBottom; } bool InputWindow::isTrustedOverlay() const { diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h index 9c43067363a9..72f1773ec529 100644 --- a/services/input/InputWindow.h +++ b/services/input/InputWindow.h @@ -131,6 +131,7 @@ struct InputWindow { int32_t frameTop; int32_t frameRight; int32_t frameBottom; + float scaleFactor; SkRegion touchableRegion; bool visible; bool canReceiveKeys; @@ -144,6 +145,11 @@ struct InputWindow { bool touchableRegionContainsPoint(int32_t x, int32_t y) const; bool frameContainsPoint(int32_t x, int32_t y) const; + /* These use the globalScale to convert a given screen offset to the + * corresponding location within the window. + */ + int32_t displayToWindowX(int32_t x) const; + /* Returns true if the window is of a trusted type that is allowed to silently * overlay other windows for the purpose of implementing the secure views feature. * Trusted overlays, such as IME windows, can partly obscure other windows without causing diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 54cc885bdac9..811221e3e5a2 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -74,6 +74,7 @@ import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.net.Proxy; @@ -146,7 +147,7 @@ public final class ActivityManagerService extends ActivityManagerNative implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback { static final String TAG = "ActivityManager"; static final boolean DEBUG = false; - static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV; + static final boolean localLOGV = DEBUG; static final boolean DEBUG_SWITCH = localLOGV || false; static final boolean DEBUG_TASKS = localLOGV || false; static final boolean DEBUG_PAUSE = localLOGV || false; @@ -545,6 +546,12 @@ public final class ActivityManagerService extends ActivityManagerNative */ ProcessRecord mHomeProcess; + /** + * Packages that the user has asked to have run in screen size + * compatibility mode instead of filling the screen. + */ + final HashSet mScreenCompatPackages = new HashSet(); + /** * Set of PendingResultRecord objects that are currently active. */ @@ -2091,6 +2098,74 @@ public final class ActivityManagerService extends ActivityManagerNative } } + CompatibilityInfo compatibilityInfoForPackageLocked(ApplicationInfo ai) { + return new CompatibilityInfo(ai, mConfiguration.screenLayout, + mScreenCompatPackages.contains(ai.packageName)); + } + + public void setPackageScreenCompatMode(String packageName, boolean compatEnabled) { + synchronized (this) { + ApplicationInfo ai = null; + try { + ai = AppGlobals.getPackageManager(). + getApplicationInfo(packageName, STOCK_PM_FLAGS); + } catch (RemoteException e) { + } + if (ai == null) { + Slog.w(TAG, "setPackageScreenCompatMode failed: unknown package " + packageName); + return; + } + boolean changed = false; + if (compatEnabled) { + if (!mScreenCompatPackages.contains(packageName)) { + changed = true; + mScreenCompatPackages.add(packageName); + } + } else { + if (mScreenCompatPackages.contains(packageName)) { + changed = true; + mScreenCompatPackages.remove(packageName); + } + } + if (changed) { + CompatibilityInfo ci = compatibilityInfoForPackageLocked(ai); + + // Tell all processes that loaded this package about the change. + for (int i=mLruProcesses.size()-1; i>=0; i--) { + ProcessRecord app = mLruProcesses.get(i); + if (!app.pkgList.contains(packageName)) { + continue; + } + try { + if (app.thread != null) { + if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc " + + app.processName + " new compat " + ci); + app.thread.updatePackageCompatibilityInfo(packageName, ci); + } + } catch (Exception e) { + } + } + + // All activities that came from the packge must be + // restarted as if there was a config change. + for (int i=mMainStack.mHistory.size()-1; i>=0; i--) { + ActivityRecord a = (ActivityRecord)mMainStack.mHistory.get(i); + if (a.info.packageName.equals(packageName)) { + a.forceNewConfig = true; + } + } + + ActivityRecord starting = mMainStack.topRunningActivityLocked(null); + if (starting != null) { + mMainStack.ensureActivityConfigurationLocked(starting, 0); + // And we need to make sure at this point that all other activities + // are made visible with the correct configuration. + mMainStack.ensureActivitiesVisibleLocked(starting, 0); + } + } + } + } + void reportResumedActivityLocked(ActivityRecord r) { //Slog.i(TAG, "**** REPORT RESUME: " + r); @@ -3589,12 +3664,14 @@ public final class ActivityManagerService extends ActivityManagerNative } if (DEBUG_CONFIGURATION) Slog.v(TAG, "Binding proc " + processName + " with config " + mConfiguration); - thread.bindApplication(processName, app.instrumentationInfo != null - ? app.instrumentationInfo : app.info, providers, + ApplicationInfo appInfo = app.instrumentationInfo != null + ? app.instrumentationInfo : app.info; + thread.bindApplication(processName, appInfo, providers, app.instrumentationClass, app.instrumentationProfileFile, app.instrumentationArguments, app.instrumentationWatcher, testMode, isRestrictedBackupMode || !normalMode, - mConfiguration, getCommonServicesLocked(), + mConfiguration, compatibilityInfoForPackageLocked(appInfo), + getCommonServicesLocked(), mCoreSettingsObserver.getCoreSettingsLocked()); updateLruProcessLocked(app, false, true); app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis(); @@ -3685,7 +3762,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (DEBUG_BACKUP) Slog.v(TAG, "New app is backup target, launching agent for " + app); ensurePackageDexOpt(mBackupTarget.appInfo.packageName); try { - thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, mBackupTarget.backupMode); + thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, + compatibilityInfoForPackageLocked(mBackupTarget.appInfo), + mBackupTarget.backupMode); } catch (Exception e) { Slog.w(TAG, "Exception scheduling backup agent creation: "); e.printStackTrace(); @@ -7776,6 +7855,10 @@ public final class ActivityManagerService extends ActivityManagerNative } pw.println(" mConfiguration: " + mConfiguration); pw.println(" mConfigWillChange: " + mMainStack.mConfigWillChange); + if (mScreenCompatPackages.size() > 0) { + pw.print(" mScreenCompatPackages="); + pw.println(mScreenCompatPackages); + } pw.println(" mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown); if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient || mOrigWaitForDebugger) { @@ -9238,7 +9321,8 @@ public final class ActivityManagerService extends ActivityManagerNative r.stats.startLaunchedLocked(); } ensurePackageDexOpt(r.serviceInfo.packageName); - app.thread.scheduleCreateService(r, r.serviceInfo); + app.thread.scheduleCreateService(r, r.serviceInfo, + compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo)); r.postNotification(); created = true; } finally { @@ -10342,7 +10426,8 @@ public final class ActivityManagerService extends ActivityManagerNative if (proc.thread != null) { if (DEBUG_BACKUP) Slog.v(TAG, "Agent proc already running: " + proc); try { - proc.thread.scheduleCreateBackupAgent(app, backupMode); + proc.thread.scheduleCreateBackupAgent(app, + compatibilityInfoForPackageLocked(app), backupMode); } catch (RemoteException e) { // Will time out on the backup manager side } @@ -10414,7 +10499,8 @@ public final class ActivityManagerService extends ActivityManagerNative // If the app crashed during backup, 'thread' will be null here if (proc.thread != null) { try { - proc.thread.scheduleDestroyBackupAgent(appInfo); + proc.thread.scheduleDestroyBackupAgent(appInfo, + compatibilityInfoForPackageLocked(appInfo)); } catch (Exception e) { Slog.e(TAG, "Exception when unbinding backup agent:"); e.printStackTrace(); @@ -11261,6 +11347,7 @@ public final class ActivityManagerService extends ActivityManagerNative + ": " + r); ensurePackageDexOpt(r.intent.getComponent().getPackageName()); app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver, + compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo), r.resultCode, r.resultData, r.resultExtras, r.ordered); if (DEBUG_BROADCAST) Slog.v(TAG, "Process cur broadcast " + r + " DELIVERED for app " + app); diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 0fb30ff4b172..cb0a0f0c2771 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -111,6 +111,7 @@ class ActivityRecord extends IApplicationToken.Stub { boolean hasBeenLaunched;// has this activity ever been launched? boolean frozenBeforeDestroy;// has been frozen but not yet destroyed. boolean immersive; // immersive mode (don't interrupt if possible) + boolean forceNewConfig; // force re-create with new config next time String stringName; // for caching of toString(). @@ -174,7 +175,8 @@ class ActivityRecord extends IApplicationToken.Stub { pw.print(" immersive="); pw.print(immersive); pw.print(" launchMode="); pw.println(launchMode); pw.print(prefix); pw.print("frozenBeforeDestroy="); pw.print(frozenBeforeDestroy); - pw.print(" thumbnailNeeded="); pw.println(thumbnailNeeded); + pw.print(" thumbnailNeeded="); pw.print(thumbnailNeeded); + pw.print(" forceNewConfig="); pw.println(forceNewConfig); if (launchTime != 0 || startTime != 0) { pw.print(prefix); pw.print("launchTime="); TimeUtils.formatDuration(launchTime, pw); pw.print(" startTime="); diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index c087aecfb594..f38504227d4f 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -537,9 +537,11 @@ public class ActivityStack { } mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName()); r.sleeping = false; + r.forceNewConfig = false; app.thread.scheduleLaunchActivity(new Intent(r.intent), r, System.identityHashCode(r), - r.info, r.icicle, results, newIntents, !andResume, + r.info, mService.compatibilityInfoForPackageLocked(r.info.applicationInfo), + r.icicle, results, newIntents, !andResume, mService.isNextTransitionForward()); if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) { @@ -3750,7 +3752,7 @@ public class ActivityStack { // Short circuit: if the two configurations are the exact same // object (the common case), then there is nothing to do. Configuration newConfig = mService.mConfiguration; - if (r.configuration == newConfig) { + if (r.configuration == newConfig && !r.forceNewConfig) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Configuration unchanged in " + r); return true; @@ -3775,6 +3777,7 @@ public class ActivityStack { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Configuration doesn't matter not running " + r); r.stopFreezingScreenLocked(false); + r.forceNewConfig = false; return true; } @@ -3786,10 +3789,11 @@ public class ActivityStack { + Integer.toHexString(r.info.configChanges) + ", newConfig=" + newConfig); } - if ((changes&(~r.info.configChanges)) != 0) { + if ((changes&(~r.info.configChanges)) != 0 || r.forceNewConfig) { // Aha, the activity isn't handling the change, so DIE DIE DIE. r.configChangeFlags |= changes; r.startFreezingScreenLocked(r.app, globalChanges); + r.forceNewConfig = false; if (r.app == null || r.app.thread == null) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG, "Switch is destroying non-running " + r); @@ -3860,6 +3864,7 @@ public class ActivityStack { try { if (DEBUG_SWITCH) Slog.i(TAG, "Switch is restarting resumed " + r); + r.forceNewConfig = false; r.app.thread.scheduleRelaunchActivity(r, results, newIntents, changes, !andResume, mService.mConfiguration); // Note: don't need to call pauseIfSleepingLocked() here, because diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 353ff6d2b5f6..a63ffaef33c7 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -24,6 +24,7 @@ import android.app.IApplicationThread; import android.app.IInstrumentationWatcher; import android.content.ComponentName; import android.content.pm.ApplicationInfo; +import android.content.res.CompatibilityInfo; import android.os.Bundle; import android.os.IBinder; import android.os.SystemClock; diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java index 45a78af37af3..57f0799b1f24 100644 --- a/services/java/com/android/server/wm/InputMonitor.java +++ b/services/java/com/android/server/wm/InputMonitor.java @@ -205,12 +205,21 @@ final class InputMonitor { inputWindow.ownerPid = child.mSession.mPid; inputWindow.ownerUid = child.mSession.mUid; - final Rect frame = child.mFrame; + final Rect frame = child.mScaledFrame; inputWindow.frameLeft = frame.left; inputWindow.frameTop = frame.top; inputWindow.frameRight = frame.right; inputWindow.frameBottom = frame.bottom; + if (child.mGlobalScale != 1) { + // If we are scaling the window, input coordinates need + // to be inversely scaled to map from what is on screen + // to what is actually being touched in the UI. + inputWindow.scaleFactor = 1.0f/child.mGlobalScale; + } else { + inputWindow.scaleFactor = 1; + } + child.getTouchableRegion(inputWindow.touchableRegion); } diff --git a/services/java/com/android/server/wm/InputWindow.java b/services/java/com/android/server/wm/InputWindow.java index e3eb4732dc81..578120eeaac0 100644 --- a/services/java/com/android/server/wm/InputWindow.java +++ b/services/java/com/android/server/wm/InputWindow.java @@ -46,6 +46,10 @@ public final class InputWindow { public int frameRight; public int frameBottom; + // Global scaling factor applied to touch events when they are dispatched + // to the window + public float scaleFactor; + // Window touchable region. public final Region touchableRegion = new Region(); diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 33e6a36d73b8..769e4239a9da 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -19,7 +19,6 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND; -import static android.view.WindowManager.LayoutParams.FLAG_COMPATIBLE_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND; import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; @@ -586,6 +585,7 @@ public class WindowManagerService extends IWindowManager.Stub // The frame use to limit the size of the app running in compatibility mode. Rect mCompatibleScreenFrame = new Rect(); + float mCompatibleScreenScale; // The surface used to fill the outer rim of the app running in compatibility mode. Surface mBackgroundFillerSurface = null; WindowState mBackgroundFillerTarget = null; @@ -1757,7 +1757,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean rawChanged = false; float wpx = mLastWallpaperX >= 0 ? mLastWallpaperX : 0.5f; float wpxs = mLastWallpaperXStep >= 0 ? mLastWallpaperXStep : -1.0f; - int availw = wallpaperWin.mFrame.right-wallpaperWin.mFrame.left-dw; + int availw = wallpaperWin.mScaledFrame.right-wallpaperWin.mScaledFrame.left-dw; int offset = availw > 0 ? -(int)(availw*wpx+.5f) : 0; changed = wallpaperWin.mXOffset != offset; if (changed) { @@ -2887,14 +2887,14 @@ public class WindowManagerService extends IWindowManager.Stub } private boolean applyAnimationLocked(AppWindowToken wtoken, - WindowManager.LayoutParams lp, int transit, boolean enter) { + WindowManager.LayoutParams lp, int transit, boolean enter, boolean bgFiller) { // Only apply an animation if the display isn't frozen. If it is // frozen, there is no reason to animate and it can cause strange // artifacts when we unfreeze the display if some different animation // is running. if (!mDisplayFrozen && mPolicy.isScreenOn()) { Animation a; - if (lp != null && (lp.flags & FLAG_COMPATIBLE_WINDOW) != 0) { + if (bgFiller) { a = new FadeInOutAnimation(enter); if (DEBUG_ANIM) Slog.v(TAG, "applying FadeInOutAnimation for a window in compatibility mode"); @@ -3680,7 +3680,7 @@ public class WindowManagerService extends IWindowManager.Stub } boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp, - boolean visible, int transit, boolean performLayout) { + boolean visible, int transit, boolean performLayout, boolean bgFiller) { boolean delayed = false; if (wtoken.clientHidden == visible) { @@ -3702,7 +3702,7 @@ public class WindowManagerService extends IWindowManager.Stub if (wtoken.animation == sDummyAnimation) { wtoken.animation = null; } - applyAnimationLocked(wtoken, lp, transit, visible); + applyAnimationLocked(wtoken, lp, transit, visible, bgFiller); changed = true; if (wtoken.animation != null) { delayed = runningAppAnimation = true; @@ -3855,7 +3855,8 @@ public class WindowManagerService extends IWindowManager.Stub } final long origId = Binder.clearCallingIdentity(); - setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_UNSET, true); + setTokenVisibilityLocked(wtoken, null, visible, WindowManagerPolicy.TRANSIT_UNSET, + true, false); wtoken.updateReportedVisibilityLocked(); Binder.restoreCallingIdentity(origId); } @@ -3981,7 +3982,8 @@ public class WindowManagerService extends IWindowManager.Stub WindowToken basewtoken = mTokenMap.remove(token); if (basewtoken != null && (wtoken=basewtoken.appWindowToken) != null) { if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Removing app token: " + wtoken); - delayed = setTokenVisibilityLocked(wtoken, null, false, WindowManagerPolicy.TRANSIT_UNSET, true); + delayed = setTokenVisibilityLocked(wtoken, null, false, + WindowManagerPolicy.TRANSIT_UNSET, true, false); wtoken.inPendingTransaction = false; mOpeningApps.remove(wtoken); wtoken.waitingToShow = false; @@ -4753,8 +4755,8 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { long ident = Binder.clearCallingIdentity(); - dw = mDisplay.getWidth(); - dh = mDisplay.getHeight(); + dw = mPolicy.getNonDecorDisplayWidth(mDisplay.getWidth()); + dh = mPolicy.getNonDecorDisplayHeight(mDisplay.getHeight()); int aboveAppLayer = mPolicy.windowTypeToLayerLw( WindowManager.LayoutParams.TYPE_APPLICATION) * TYPE_LAYER_MULTIPLIER @@ -4802,7 +4804,7 @@ public class WindowManagerService extends IWindowManager.Stub // Don't include wallpaper in bounds calculation if (!ws.mIsWallpaper) { - final Rect wf = ws.mFrame; + final Rect wf = ws.mScaledFrame; final Rect cr = ws.mContentInsets; int left = wf.left + cr.left; int top = wf.top + cr.top; @@ -5447,7 +5449,10 @@ public class WindowManagerService extends IWindowManager.Stub DisplayMetrics dm = new DisplayMetrics(); mDisplay.getMetrics(dm); - CompatibilityInfo.updateCompatibleScreenFrame(dm, orientation, mCompatibleScreenFrame); + dm.realWidthPixels = mPolicy.getNonDecorDisplayWidth(dm.realWidthPixels); + dm.realHeightPixels = mPolicy.getNonDecorDisplayHeight(dm.realHeightPixels); + mCompatibleScreenScale = CompatibilityInfo.updateCompatibleScreenFrame( + dm, mCompatibleScreenFrame, null); if (mScreenLayout == Configuration.SCREENLAYOUT_SIZE_UNDEFINED) { // Note we only do this once because at this point we don't @@ -6582,6 +6587,9 @@ public class WindowManagerService extends IWindowManager.Stub final int dw = mDisplay.getWidth(); final int dh = mDisplay.getHeight(); + final int innerDw = mPolicy.getNonDecorDisplayWidth(dw); + final int innerDh = mPolicy.getNonDecorDisplayHeight(dh); + final int N = mWindows.size(); int i; @@ -6634,7 +6642,9 @@ public class WindowManagerService extends IWindowManager.Stub //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial"); win.mContentChanged = false; } + win.prelayout(); mPolicy.layoutWindowLw(win, win.mAttrs, null); + win.evalNeedsBackgroundFiller(innerDw, innerDh); win.mLayoutSeq = seq; if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame=" + win.mFrame + " mContainingFrame=" @@ -6669,7 +6679,9 @@ public class WindowManagerService extends IWindowManager.Stub //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial"); win.mContentChanged = false; } + win.prelayout(); mPolicy.layoutWindowLw(win, win.mAttrs, win.mAttachedWindow); + win.evalNeedsBackgroundFiller(innerDw, innerDh); win.mLayoutSeq = seq; if (DEBUG_LAYOUT) Slog.v(TAG, "-> mFrame=" + win.mFrame + " mContainingFrame=" @@ -6700,6 +6712,9 @@ public class WindowManagerService extends IWindowManager.Stub final int dw = mDisplay.getWidth(); final int dh = mDisplay.getHeight(); + final int innerDw = mPolicy.getNonDecorDisplayWidth(dw); + final int innerDh = mPolicy.getNonDecorDisplayHeight(dh); + int i; if (mFocusMayChange) { @@ -6799,13 +6814,15 @@ public class WindowManagerService extends IWindowManager.Stub boolean tokensAnimating = false; final int NAT = mAppTokens.size(); for (i=0; i