diff options
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 b073004..5c8abe4 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 <port>]\n" + " --gdb: start gdbserv on the given port at crash/ANR\n" + "\n" + + " control screen compatibility: am screen-compat [on|off] [package]\n" + + "\n" + " <INTENT> specifications include these flags:\n" + " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" + " [-c <CATEGORY> [-c <CATEGORY>] ...]\n" + diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 6426635..11e9975 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1398,6 +1398,16 @@ 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 bd83762..67e8839 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<ResultInfo> 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<Intent> 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<ResultInfo> 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<ResultInfo> pendingResults, + ActivityInfo info, CompatibilityInfo compatInfo, Bundle state, + List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward) { ActivityClientRecord r = new ActivityClientRecord(); @@ -451,6 +462,7 @@ 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<String, IBinder> services, Bundle coreSettings) { + CompatibilityInfo compatInfo, Map<String, IBinder> services, + Bundle coreSettings) { if (services != null) { // Setup the service cache in the ServiceManager @@ -573,6 +593,7 @@ 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<LoadedApk> 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<LoadedApk> ref; + if (includeCode) { + ref = mPackages.get(packageName); + } else { + ref = mResourcePackages.get(packageName); + } + return ref != null ? ref.get() : null; + } + } + + private final LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo, ClassLoader baseLoader, boolean securityViolation, boolean includeCode) { synchronized (mPackages) { WeakReference<LoadedApk> ref; @@ -1419,7 +1466,7 @@ 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<ResultInfo> results) { final int N = results.size(); for (int i=0; i<N; i++) { @@ -3064,7 +3125,7 @@ public final class ActivityThread { // If there was a pending configuration change, execute it first. if (changedConfig != null) { - handleConfigurationChanged(changedConfig); + handleConfigurationChanged(changedConfig, null); } ActivityClientRecord r = mActivities.get(tmp.token); @@ -3234,11 +3295,12 @@ public final class ActivityThread { } } - final boolean applyConfigurationToResourcesLocked(Configuration config) { + final boolean applyConfigurationToResourcesLocked(Configuration config, + CompatibilityInfo compat) { if (mResConfiguration == null) { mResConfiguration = new Configuration(); } - if (!mResConfiguration.isOtherSeqNewer(config)) { + if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) { if (DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq=" + mResConfiguration.seq + ", newSeq=" + config.seq); return false; @@ -3251,7 +3313,7 @@ public final class ActivityThread { Locale.setDefault(config.locale); } - Resources.updateSystemConfiguration(config, dm); + Resources.updateSystemConfiguration(config, dm, compat); ApplicationPackageManager.configurationChanged(); //Slog.i(TAG, "Configuration changed in " + currentPackageName()); @@ -3266,7 +3328,7 @@ public final class ActivityThread { if (r != null) { if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources " + r + " config to: " + config); - r.updateConfiguration(config, dm); + r.updateConfiguration(config, dm, compat); //Slog.i(TAG, "Updated app resources " + v.getKey() // + " " + r + ": " + r.getConfiguration()); } else { @@ -3278,7 +3340,7 @@ public final class ActivityThread { return changes != 0; } - final void handleConfigurationChanged(Configuration config) { + final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) { ArrayList<ComponentCallbacks> 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 aa26b04..e1d76a4 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -23,6 +23,7 @@ import android.content.pm.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<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR); List<Intent> pi = data.createTypedArrayList(Intent.CREATOR); boolean notResumed = data.readInt() != 0; boolean isForward = data.readInt() != 0; - scheduleLaunchActivity(intent, b, ident, info, state, ri, pi, + scheduleLaunchActivity(intent, b, ident, info, compatInfo, state, ri, pi, notResumed, isForward); return true; } @@ -181,11 +183,12 @@ 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<String, IBinder> services = data.readHashMap(null); Bundle coreSettings = data.readBundle(); bindApplication(packageName, info, providers, testName, profileName, testArgs, testWatcher, testMode, restrictedBackupMode, - config, services, coreSettings); + config, compatInfo, services, coreSettings); return true; } @@ -389,8 +394,9 @@ 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<ResultInfo> pendingResults, + ActivityInfo info, CompatibilityInfo compatInfo, Bundle state, + List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward) throws RemoteException { Parcel data = Parcel.obtain(); @@ -563,6 +579,7 @@ 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<ProviderInfo> providers, ComponentName testName, String profileName, Bundle testArgs, IInstrumentationWatcher testWatcher, int debugMode, - boolean restrictedBackupMode, Configuration config, + boolean restrictedBackupMode, Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); @@ -738,6 +760,7 @@ 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 cc1f81c..36b9d72 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 61e6fc8..4c2ccf4 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 55177a9..93a8ff3 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -23,6 +23,7 @@ import android.content.pm.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<ResultInfo> results) throws RemoteException; void scheduleLaunchActivity(Intent intent, IBinder token, int ident, - ActivityInfo info, Bundle state, List<ResultInfo> pendingResults, + ActivityInfo info, CompatibilityInfo compatInfo, Bundle state, + List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, boolean isForward) throws RemoteException; void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults, @@ -61,14 +63,17 @@ public interface IApplicationThread extends IInterface { void scheduleNewIntent(List<Intent> intent, IBinder token) throws RemoteException; void scheduleDestroyActivity(IBinder token, boolean finished, int configChanges) throws RemoteException; - void scheduleReceiver(Intent intent, ActivityInfo info, int resultCode, - String data, Bundle extras, boolean sync) throws RemoteException; + void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo, + int resultCode, String data, Bundle extras, boolean sync) throws RemoteException; static final int BACKUP_MODE_INCREMENTAL = 0; static final int BACKUP_MODE_FULL = 1; static final int BACKUP_MODE_RESTORE = 2; - void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode) throws RemoteException; - void scheduleDestroyBackupAgent(ApplicationInfo app) throws RemoteException; - void scheduleCreateService(IBinder token, ServiceInfo info) throws RemoteException; + void scheduleCreateBackupAgent(ApplicationInfo app, CompatibilityInfo compatInfo, + int backupMode) throws RemoteException; + void scheduleDestroyBackupAgent(ApplicationInfo app, CompatibilityInfo compatInfo) + throws RemoteException; + void scheduleCreateService(IBinder token, ServiceInfo info, + CompatibilityInfo compatInfo) throws RemoteException; void scheduleBindService(IBinder token, Intent intent, boolean rebind) throws RemoteException; void scheduleUnbindService(IBinder token, @@ -82,7 +87,7 @@ public interface IApplicationThread extends IInterface { void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers, ComponentName testName, String profileName, Bundle testArguments, IInstrumentationWatcher testWatcher, int debugMode, boolean restrictedBackupMode, - Configuration config, Map<String, IBinder> services, + Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings) throws RemoteException; void scheduleExit() throws RemoteException; void scheduleSuicide() throws RemoteException; @@ -112,6 +117,7 @@ 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 c406524..5307696 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 e403ac2..ab9bfce 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -21,9 +21,9 @@ import android.graphics.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 <supports-screens> or - * 2) Configuration.SCREENLAYOUT_COMPAT_NEEDED is not set - * {@see compatibilityFlag} + * Has the application said that its UI is expandable? Based on the + * <supports-screen> android:expandible in the manifest. */ private static final int EXPANDABLE = 2; /** - * A flag mask to tell if the application is configured to be expandable. This differs - * from EXPANDABLE in that the application that is not expandable will be - * marked as expandable if Configuration.SCREENLAYOUT_COMPAT_NEEDED is not set. - */ - private static final int CONFIGURED_EXPANDABLE = 4; - - /** - * A flag mask to indicates that the application supports large screens. - * The flag is set to true if - * 1) Application declares it supports large screens in manifest file using <supports-screens> or - * 2) The screen size is not large - * {@see compatibilityFlag} + * Has the application said that its UI supports large screens? Based on the + * <supports-screen> android:largeScreens in the manifest. */ private static final int LARGE_SCREENS = 8; /** - * A flag mask to tell if the application supports large screens. This differs - * from LARGE_SCREENS in that the application that does not support large - * screens will be marked as supporting them if the current screen is not - * large. - */ - private static final int CONFIGURED_LARGE_SCREENS = 16; - - /** - * A flag mask to indicates that the application supports xlarge screens. - * The flag is set to true if - * 1) Application declares it supports xlarge screens in manifest file using <supports-screens> or - * 2) The screen size is not xlarge - * {@see compatibilityFlag} + * Has the application said that its UI supports xlarge screens? Based on the + * <supports-screen> android:xlargeScreens in the manifest. */ private static final int XLARGE_SCREENS = 32; /** - * A flag mask to tell if the application supports xlarge screens. This differs - * from XLARGE_SCREENS in that the application that does not support xlarge - * screens will be marked as supporting them if the current screen is not - * xlarge. + * Set if the application needs to run in screen size compatibility mode. */ - private static final int CONFIGURED_XLARGE_SCREENS = 64; + private static final int NEEDS_SCREEN_COMPAT = 128; /** * The effective screen density we have selected for this application. @@ -132,28 +100,55 @@ public 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,81 +175,13 @@ 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 */ public boolean isScalingRequired() { @@ -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<CompatibilityInfo> CREATOR + = new Parcelable.Creator<CompatibilityInfo>() { + public CompatibilityInfo createFromParcel(Parcel source) { + return new CompatibilityInfo(source); + } + + public CompatibilityInfo[] newArray(int size) { + return new CompatibilityInfo[size]; + } + }; + + private CompatibilityInfo(Parcel source) { + mCompatibilityFlags = source.readInt(); + applicationDensity = source.readInt(); + applicationScale = source.readFloat(); + applicationInvertedScale = source.readFloat(); } } diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 47c2623..908db11 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -703,11 +703,20 @@ public final class Configuration implements Parcelable, Comparable<Configuration } public int hashCode() { - return ((int)this.fontScale) + this.mcc + this.mnc - + (this.locale != null ? this.locale.hashCode() : 0) - + this.touchscreen - + this.keyboard + this.keyboardHidden + this.hardKeyboardHidden - + this.navigation + this.navigationHidden - + this.orientation + this.screenLayout + this.uiMode; + int result = 17; + result = 31 * result + Float.floatToIntBits(fontScale); + result = 31 * result + mcc; + result = 31 * result + mnc; + result = 31 * result + (locale != null ? locale.hashCode() : 0); + result = 31 * result + touchscreen; + result = 31 * result + keyboard; + result = 31 * result + keyboardHidden; + result = 31 * result + hardKeyboardHidden; + result = 31 * result + navigation; + result = 31 * result + navigationHidden; + result = 31 * result + orientation; + result = 31 * result + screenLayout; + result = 31 * result + uiMode; + return result; } } diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 81eb09c..00b49e8 100755 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -91,6 +91,7 @@ public class Resources { private static boolean mPreloaded; /*package*/ final TypedValue mTmpValue = new TypedValue(); + /*package*/ final Configuration mTmpConfig = new Configuration(); // These are protected by the mTmpValue lock. private final LongSparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache @@ -1400,18 +1401,30 @@ public 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,15 +1513,23 @@ 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 63baf14..8018ff9 100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -16,9 +16,7 @@ package android.util; -import android.content.res.CompatibilityInfo; -import android.content.res.Configuration; -import android.os.*; +import android.os.SystemProperties; /** @@ -107,6 +105,11 @@ public 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 126f409..89767f2 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 334c68e..2058991 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -458,6 +458,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 d9d77c4..bc5b718 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 e2e698e..19d590a 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 8e18f2a..29c5741 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<WindowState> mStatusBarPanels = new ArrayList<WindowState>(); 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 19295e6..f8a5cfb 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("<empty>"); @@ -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<MotionEntry*>(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 1e118c4..dd89328 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 b552f6d..ccea9e4 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 9c43067..72f1773 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 54cc885..811221e 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; @@ -546,6 +547,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<String> mScreenCompatPackages = new HashSet<String>(); + + /** * Set of PendingResultRecord objects that are currently active. */ final HashSet mPendingResultRecords = new HashSet(); @@ -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 0fb30ff..cb0a0f0 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 c087aec..f385042 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 353ff6d..a63ffae 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 45a78af..57f0799 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 e3eb473..578120e 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 33e6a36..769e423 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<NAT; i++) { - if (mAppTokens.get(i).stepAnimationLocked(currentTime, dw, dh)) { + if (mAppTokens.get(i).stepAnimationLocked(currentTime, + innerDw, innerDh)) { tokensAnimating = true; } } final int NEAT = mExitingAppTokens.size(); for (i=0; i<NEAT; i++) { - if (mExitingAppTokens.get(i).stepAnimationLocked(currentTime, dw, dh)) { + if (mExitingAppTokens.get(i).stepAnimationLocked(currentTime, + innerDw, innerDh)) { tokensAnimating = true; } } @@ -6858,8 +6875,8 @@ public class WindowManagerService extends IWindowManager.Stub final boolean wasAnimating = w.mAnimating; - int animDw = dw; - int animDh = dh; + int animDw = innerDw; + int animDh = innerDh; // If the window has moved due to its containing // content frame changing, then we'd like to animate @@ -7116,6 +7133,7 @@ public class WindowManagerService extends IWindowManager.Stub LayoutParams animLp = null; int bestAnimLayer = -1; boolean fullscreenAnim = false; + boolean needBgFiller = false; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New wallpaper target=" + mWallpaperTarget @@ -7155,9 +7173,10 @@ public class WindowManagerService extends IWindowManager.Stub if (ws != null) { // If this is a compatibility mode // window, we will always use its anim. - if ((ws.mAttrs.flags&FLAG_COMPATIBLE_WINDOW) != 0) { + if (ws.mNeedsBackgroundFiller) { animLp = ws.mAttrs; bestAnimLayer = Integer.MAX_VALUE; + needBgFiller = true; } else if (!fullscreenAnim || ws.mLayer > bestAnimLayer) { animLp = ws.mAttrs; bestAnimLayer = ws.mLayer; @@ -7222,7 +7241,8 @@ public class WindowManagerService extends IWindowManager.Stub wtoken.reportedVisible = false; wtoken.inPendingTransaction = false; wtoken.animation = null; - setTokenVisibilityLocked(wtoken, animLp, true, transit, false); + setTokenVisibilityLocked(wtoken, animLp, true, + transit, false, needBgFiller); wtoken.updateReportedVisibilityLocked(); wtoken.waitingToShow = false; wtoken.showAllWindowsLocked(); @@ -7234,7 +7254,8 @@ public class WindowManagerService extends IWindowManager.Stub "Now closing app" + wtoken); wtoken.inPendingTransaction = false; wtoken.animation = null; - setTokenVisibilityLocked(wtoken, animLp, false, transit, false); + setTokenVisibilityLocked(wtoken, animLp, false, + transit, false, needBgFiller); wtoken.updateReportedVisibilityLocked(); wtoken.waitingToHide = false; // Force the allDrawn flag, because we want to start @@ -7737,12 +7758,14 @@ public class WindowManagerService extends IWindowManager.Stub } boolean opaqueDrawn = canBeSeen && w.isOpaqueDrawn(); - if (opaqueDrawn && w.isFullscreen(dw, dh)) { + if (opaqueDrawn && w.isFullscreen(innerDw, innerDh)) { // This window completely covers everything behind it, // so we want to leave all of them as unblurred (for // performance reasons). obscured = true; - } else if (w.needsBackgroundFiller(dw, dh) && (canBeSeen || w.isAnimating())) { + } else if (w.mNeedsBackgroundFiller && w.mHasDrawn + && w.mViewVisibility == View.VISIBLE + && (canBeSeen || w.isAnimating())) { // This window is in compatibility mode, and needs background filler. obscured = true; mBackgroundFillerTarget = w; diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java index f8ff5f8..72049ec 100644 --- a/services/java/com/android/server/wm/WindowState.java +++ b/services/java/com/android/server/wm/WindowState.java @@ -72,6 +72,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { final boolean mIsImWindow; final boolean mIsWallpaper; final boolean mIsFloatingLayer; + final boolean mEnforceSizeCompat; int mViewVisibility; boolean mPolicyVisibility = true; boolean mPolicyVisibilityAfterAnim = true; @@ -91,6 +92,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { int mLastLayer; boolean mHaveFrame; boolean mObscured; + boolean mNeedsBackgroundFiller; boolean mTurnOnScreen; int mLayoutSeq = -1; @@ -154,6 +156,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { // Current transformation being applied. boolean mHaveMatrix; + float mGlobalScale=1; float mDsDx=1, mDtDx=0, mDsDy=0, mDtDy=1; float mLastDsDx=1, mLastDtDx=0, mLastDsDy=0, mLastDtDy=1; float mHScale=1, mVScale=1; @@ -163,6 +166,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { // "Real" frame that the application sees. final Rect mFrame = new Rect(); final Rect mLastFrame = new Rect(); + final Rect mScaledFrame = new Rect(); final Rect mContainingFrame = new Rect(); final Rect mDisplayFrame = new Rect(); @@ -273,6 +277,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { mViewVisibility = viewVisibility; DeathRecipient deathRecipient = new DeathRecipient(); mAlpha = a.alpha; + mEnforceSizeCompat = (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0; if (WindowManagerService.localLOGV) Slog.v( WindowManagerService.TAG, "Window " + this + " client=" + c.asBinder() + " token=" + token + " (" + mAttrs.token + ")"); @@ -368,7 +373,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { final Rect display = mDisplayFrame; display.set(df); - if ((mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0) { + if (mEnforceSizeCompat) { container.intersect(mService.mCompatibleScreenFrame); if ((mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) == 0) { display.intersect(mService.mCompatibleScreenFrame); @@ -416,6 +421,28 @@ final class WindowState implements WindowManagerPolicy.WindowState { // Now make sure the window fits in the overall display. Gravity.applyDisplay(mAttrs.gravity, df, frame); + int adjRight=0, adjBottom=0; + + if (mEnforceSizeCompat) { + // Adjust window offsets by the scaling factor. + int xoff = (int)((frame.left-mService.mCompatibleScreenFrame.left)*mGlobalScale) + - (frame.left-mService.mCompatibleScreenFrame.left); + int yoff = (int)((frame.top-mService.mCompatibleScreenFrame.top)*mGlobalScale) + - (frame.top-mService.mCompatibleScreenFrame.top); + frame.offset(xoff, yoff); + + // We are temporarily going to apply the compatibility scale + // to the window so that we can correctly associate it with the + // content and visible frame. + adjRight = frame.right - frame.left; + adjRight = (int)((adjRight)*mGlobalScale + .5f) - adjRight; + adjBottom = frame.bottom - frame.top; + adjBottom = (int)((adjBottom)*mGlobalScale + .5f) - adjBottom; + frame.right += adjRight; + frame.bottom += adjBottom; + } + mScaledFrame.set(frame); + // Make sure the content and visible frames are inside of the // final window frame. if (content.left < frame.left) content.left = frame.left; @@ -439,6 +466,22 @@ final class WindowState implements WindowManagerPolicy.WindowState { visibleInsets.right = frame.right-visible.right; visibleInsets.bottom = frame.bottom-visible.bottom; + if (mEnforceSizeCompat) { + // Scale the computed insets back to the window's compatibility + // coordinate space, and put frame back to correct size. + final float invScale = 1.0f/mGlobalScale; + contentInsets.left = (int)(contentInsets.left*invScale); + contentInsets.top = (int)(contentInsets.top*invScale); + contentInsets.right = (int)(contentInsets.right*invScale); + contentInsets.bottom = (int)(contentInsets.bottom*invScale); + visibleInsets.left = (int)(visibleInsets.left*invScale); + visibleInsets.top = (int)(visibleInsets.top*invScale); + visibleInsets.right = (int)(visibleInsets.right*invScale); + visibleInsets.bottom = (int)(visibleInsets.bottom*invScale); + frame.right -= adjRight; + frame.bottom -= adjBottom; + } + if (mIsWallpaper && (fw != frame.width() || fh != frame.height())) { mService.updateWallpaperOffsetLocked(this, mService.mDisplay.getWidth(), mService.mDisplay.getHeight(), false); @@ -819,9 +862,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { if (!mLocalAnimating) { if (WindowManagerService.DEBUG_ANIM) Slog.v( WindowManagerService.TAG, "Starting animation in " + this + - " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() + + " @ " + currentTime + ": ww=" + mScaledFrame.width() + + " wh=" + mScaledFrame.height() + " dw=" + dw + " dh=" + dh + " scale=" + mService.mWindowAnimationScale); - mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh); + mAnimation.initialize(mScaledFrame.width(), mScaledFrame.height(), dw, dh); mAnimation.setStartTime(currentTime); mLocalAnimating = true; mAnimating = true; @@ -988,6 +1032,14 @@ final class WindowState implements WindowManagerPolicy.WindowState { return true; } + void prelayout() { + if (mEnforceSizeCompat) { + mGlobalScale = mService.mCompatibleScreenScale; + } else { + mGlobalScale = 1; + } + } + void computeShownFrameLocked() { final boolean selfTransformation = mHasLocalTransformation; Transformation attachedTransformation = @@ -1031,6 +1083,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { // Compute the desired transformation. tmpMatrix.setTranslate(0, 0); + tmpMatrix.postScale(mGlobalScale, mGlobalScale); if (selfTransformation) { tmpMatrix.postConcat(mTransformation.getMatrix()); } @@ -1105,10 +1158,10 @@ final class WindowState implements WindowManagerPolicy.WindowState { } mShownAlpha = mAlpha; mHaveMatrix = false; - mDsDx = 1; + mDsDx = mGlobalScale; mDtDx = 0; mDsDy = 0; - mDtDy = 1; + mDtDy = mGlobalScale; } /** @@ -1281,12 +1334,14 @@ final class WindowState implements WindowManagerPolicy.WindowState { && mService.mPolicy.isScreenOn(); } - boolean needsBackgroundFiller(int screenWidth, int screenHeight) { - return + void evalNeedsBackgroundFiller(int screenWidth, int screenHeight) { + mNeedsBackgroundFiller = // only if the application is requesting compatible window - (mAttrs.flags & FLAG_COMPATIBLE_WINDOW) != 0 && + mEnforceSizeCompat && // only if it's visible mHasDrawn && mViewVisibility == View.VISIBLE && + // not needed if the compat window is actually full screen + !isFullscreenIgnoringCompat(screenWidth, screenHeight) && // and only if the application fills the compatible screen mFrame.left <= mService.mCompatibleScreenFrame.left && mFrame.top <= mService.mCompatibleScreenFrame.top && @@ -1295,8 +1350,19 @@ final class WindowState implements WindowManagerPolicy.WindowState { } boolean isFullscreen(int screenWidth, int screenHeight) { - return mFrame.left <= 0 && mFrame.top <= 0 && - mFrame.right >= screenWidth && mFrame.bottom >= screenHeight; + if (mEnforceSizeCompat) { + return mFrame.left <= mService.mCompatibleScreenFrame.left && + mFrame.top <= mService.mCompatibleScreenFrame.top && + mFrame.right >= mService.mCompatibleScreenFrame.right && + mFrame.bottom >= mService.mCompatibleScreenFrame.bottom; + } else { + return isFullscreenIgnoringCompat(screenWidth, screenHeight); + } + } + + boolean isFullscreenIgnoringCompat(int screenWidth, int screenHeight) { + return mScaledFrame.left <= 0 && mScaledFrame.top <= 0 && + mScaledFrame.right >= screenWidth && mScaledFrame.bottom >= screenHeight; } void removeLocked() { @@ -1426,30 +1492,38 @@ final class WindowState implements WindowManagerPolicy.WindowState { return true; } + private static void applyScaledInsets(Region outRegion, Rect frame, Rect inset, float scale) { + if (scale != 1) { + outRegion.set(frame.left + (int)(inset.left*scale), + frame.top + (int)(inset.top*scale), + frame.right - (int)(inset.right*scale), + frame.bottom - (int)(inset.bottom*scale)); + } else { + outRegion.set( + frame.left + inset.left, frame.top + inset.top, + frame.right - inset.right, frame.bottom - inset.bottom); + } + } + public void getTouchableRegion(Region outRegion) { - final Rect frame = mFrame; + final Rect frame = mScaledFrame; switch (mTouchableInsets) { default: case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME: outRegion.set(frame); break; - case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: { - final Rect inset = mGivenContentInsets; - outRegion.set( - frame.left + inset.left, frame.top + inset.top, - frame.right - inset.right, frame.bottom - inset.bottom); + case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT: + applyScaledInsets(outRegion, frame, mGivenContentInsets, mGlobalScale); break; - } - case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: { - final Rect inset = mGivenVisibleInsets; - outRegion.set( - frame.left + inset.left, frame.top + inset.top, - frame.right - inset.right, frame.bottom - inset.bottom); + case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE: + applyScaledInsets(outRegion, frame, mGivenVisibleInsets, mGlobalScale); break; - } case ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION: { final Region givenTouchableRegion = mGivenTouchableRegion; outRegion.set(givenTouchableRegion); + if (mGlobalScale != 1) { + outRegion.scale(mGlobalScale); + } outRegion.translate(frame.left, frame.top); break; } @@ -1512,7 +1586,8 @@ final class WindowState implements WindowManagerPolicy.WindowState { } pw.print(prefix); pw.print("Requested w="); pw.print(mRequestedWidth); pw.print(" h="); pw.print(mRequestedHeight); - pw.print(" mLayoutSeq="); pw.println(mLayoutSeq); + pw.print(" mLayoutSeq="); pw.print(mLayoutSeq); + pw.print(" mNeedsBackgroundFiller="); pw.println(mNeedsBackgroundFiller); if (mXOffset != 0 || mYOffset != 0) { pw.print(prefix); pw.print("Offsets x="); pw.print(mXOffset); pw.print(" y="); pw.println(mYOffset); @@ -1533,6 +1608,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { pw.println(); pw.print(prefix); pw.print("mFrame="); mFrame.printShortString(pw); pw.print(" last="); mLastFrame.printShortString(pw); + pw.print(" scaled="); mScaledFrame.printShortString(pw); pw.println(); pw.print(prefix); pw.print("mContainingFrame="); mContainingFrame.printShortString(pw); @@ -1568,8 +1644,9 @@ final class WindowState implements WindowManagerPolicy.WindowState { pw.print(" mAlpha="); pw.print(mAlpha); pw.print(" mLastAlpha="); pw.println(mLastAlpha); } - if (mHaveMatrix) { - pw.print(prefix); pw.print("mDsDx="); pw.print(mDsDx); + if (mHaveMatrix || mGlobalScale != 1) { + pw.print(prefix); pw.print("mGlobalScale="); pw.print(mGlobalScale); + pw.print(" mDsDx="); pw.print(mDsDx); pw.print(" mDtDx="); pw.print(mDtDx); pw.print(" mDsDy="); pw.print(mDsDy); pw.print(" mDtDy="); pw.println(mDtDy); diff --git a/services/jni/com_android_server_InputWindow.cpp b/services/jni/com_android_server_InputWindow.cpp index 8548b47..d36c010 100644 --- a/services/jni/com_android_server_InputWindow.cpp +++ b/services/jni/com_android_server_InputWindow.cpp @@ -40,6 +40,7 @@ static struct { jfieldID frameTop; jfieldID frameRight; jfieldID frameBottom; + jfieldID scaleFactor; jfieldID touchableRegion; jfieldID visible; jfieldID canReceiveKeys; @@ -102,6 +103,8 @@ void android_server_InputWindow_toNative( gInputWindowClassInfo.frameRight); outInputWindow->frameBottom = env->GetIntField(inputWindowObj, gInputWindowClassInfo.frameBottom); + outInputWindow->scaleFactor = env->GetFloatField(inputWindowObj, + gInputWindowClassInfo.scaleFactor); jobject regionObj = env->GetObjectField(inputWindowObj, gInputWindowClassInfo.touchableRegion); @@ -176,6 +179,9 @@ int register_android_server_InputWindow(JNIEnv* env) { GET_FIELD_ID(gInputWindowClassInfo.frameBottom, gInputWindowClassInfo.clazz, "frameBottom", "I"); + GET_FIELD_ID(gInputWindowClassInfo.scaleFactor, gInputWindowClassInfo.clazz, + "scaleFactor", "F"); + GET_FIELD_ID(gInputWindowClassInfo.touchableRegion, gInputWindowClassInfo.clazz, "touchableRegion", "Landroid/graphics/Region;"); diff --git a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java index 6192a3c..2a40c57 100644 --- a/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java +++ b/tests/DpiTest/src/com/google/android/test/dpi/DpiTestActivity.java @@ -62,7 +62,8 @@ public class DpiTestActivity extends Activity { | ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS | ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS | ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES; - app.getResources().setCompatibilityInfo(new CompatibilityInfo(ai)); + app.getResources().setCompatibilityInfo(new CompatibilityInfo(ai, + getResources().getConfiguration().screenLayout, false)); } } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException("ouch", e); |