summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ActivityThread.java38
-rw-r--r--core/java/android/content/res/AssetManager.java11
-rw-r--r--core/java/android/content/res/Resources.java5
-rw-r--r--core/java/android/content/res/ResourcesImpl.java4
-rw-r--r--core/jni/android_util_AssetManager.cpp15
-rw-r--r--core/res/res/values/config.xml6
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java65
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java12
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java57
10 files changed, 210 insertions, 4 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b26504dd1c49..5985d6e93107 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -29,6 +29,8 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
import static android.content.ContentResolver.DEPRECATE_DATA_COLUMNS;
import static android.content.ContentResolver.DEPRECATE_DATA_PREFIX;
+import static android.content.res.Configuration.UI_MODE_TYPE_DESK;
+import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded;
@@ -194,6 +196,7 @@ import android.window.SplashScreen;
import android.window.SplashScreenView;
import android.window.WindowProviderService;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
@@ -5890,9 +5893,21 @@ public final class ActivityThread extends ClientTransactionHandler
final boolean shouldUpdateResources = hasPublicResConfigChange
|| shouldUpdateResources(activityToken, currentResConfig, newConfig,
amOverrideConfig, movedToDifferentDisplay, hasPublicResConfigChange);
+
+ // TODO(b/266924897): temporary workaround, remove for U.
+ boolean skipActivityRelaunchWhenDocking = activity.getResources().getBoolean(
+ R.bool.config_skipActivityRelaunchWhenDocking);
+ int handledConfigChanges = activity.mActivityInfo.getRealConfigChanged();
+ if (skipActivityRelaunchWhenDocking && onlyDeskInUiModeChanged(activity.mCurrentConfig,
+ newConfig)) {
+ // If we're not relaunching this activity when docking, we should send the configuration
+ // changed event. Pretend as if the activity is handling uiMode config changes in its
+ // manifest so that we'll report any dock changes.
+ handledConfigChanges |= ActivityInfo.CONFIG_UI_MODE;
+ }
+
final boolean shouldReportChange = shouldReportChange(activity.mCurrentConfig, newConfig,
- r != null ? r.mSizeConfigurations : null,
- activity.mActivityInfo.getRealConfigChanged());
+ r != null ? r.mSizeConfigurations : null, handledConfigChanges);
// Nothing significant, don't proceed with updating and reporting.
if (!shouldUpdateResources && !shouldReportChange) {
return null;
@@ -5939,6 +5954,25 @@ public final class ActivityThread extends ClientTransactionHandler
}
/**
+ * Returns true if the uiMode configuration changed, and desk mode
+ * ({@link android.content.res.Configuration#UI_MODE_TYPE_DESK}) was the only change to uiMode.
+ */
+ private boolean onlyDeskInUiModeChanged(Configuration oldConfig, Configuration newConfig) {
+ boolean deskModeChanged = isInDeskUiMode(oldConfig) != isInDeskUiMode(newConfig);
+
+ // UI mode contains fields other than the UI mode type, so determine if any other fields
+ // changed.
+ boolean uiModeOtherFieldsChanged =
+ (oldConfig.uiMode & ~UI_MODE_TYPE_MASK) != (newConfig.uiMode & ~UI_MODE_TYPE_MASK);
+
+ return deskModeChanged && !uiModeOtherFieldsChanged;
+ }
+
+ private static boolean isInDeskUiMode(Configuration config) {
+ return (config.uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_DESK;
+ }
+
+ /**
* Returns {@code true} if {@link Activity#onConfigurationChanged(Configuration)} should be
* dispatched.
*
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index c8bbb0c1994d..6f09e79bdba9 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -1453,6 +1453,16 @@ public final class AssetManager implements AutoCloseable {
}
/**
+ * @hide
+ */
+ Configuration[] getSizeAndUiModeConfigurations() {
+ synchronized (this) {
+ ensureValidLocked();
+ return nativeGetSizeAndUiModeConfigurations(mObject);
+ }
+ }
+
+ /**
* Change the configuration used when retrieving resources. Not for use by
* applications.
* @hide
@@ -1603,6 +1613,7 @@ public final class AssetManager implements AutoCloseable {
private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid);
private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem);
private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr);
+ private static native @Nullable Configuration[] nativeGetSizeAndUiModeConfigurations(long ptr);
private static native void nativeSetResourceResolutionLoggingEnabled(long ptr, boolean enabled);
private static native @Nullable String nativeGetLastResourceResolution(long ptr);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index a03286d3ec6f..9b169499b41f 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -2207,6 +2207,11 @@ public class Resources {
return mResourcesImpl.getSizeConfigurations();
}
+ /** @hide */
+ public Configuration[] getSizeAndUiModeConfigurations() {
+ return mResourcesImpl.getSizeAndUiModeConfigurations();
+ }
+
/**
* Return the compatibility mode information for the application.
* The returned object should be treated as read-only.
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index ff072916292b..3bb237ac1228 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -203,6 +203,10 @@ public class ResourcesImpl {
return mAssets.getSizeConfigurations();
}
+ Configuration[] getSizeAndUiModeConfigurations() {
+ return mAssets.getSizeAndUiModeConfigurations();
+ }
+
CompatibilityInfo getCompatibilityInfo() {
return mDisplayAdjustments.getCompatibilityInfo();
}
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 8c23b214b8fe..206ad17e3c4b 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -93,6 +93,7 @@ static struct configuration_offsets_t {
jfieldID mScreenWidthDpOffset;
jfieldID mScreenHeightDpOffset;
jfieldID mScreenLayoutOffset;
+ jfieldID mUiMode;
} gConfigurationOffsets;
static struct arraymap_offsets_t {
@@ -1027,10 +1028,11 @@ static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config&
env->SetIntField(result, gConfigurationOffsets.mScreenWidthDpOffset, config.screenWidthDp);
env->SetIntField(result, gConfigurationOffsets.mScreenHeightDpOffset, config.screenHeightDp);
env->SetIntField(result, gConfigurationOffsets.mScreenLayoutOffset, config.screenLayout);
+ env->SetIntField(result, gConfigurationOffsets.mUiMode, config.uiMode);
return result;
}
-static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
+static jobjectArray GetSizeAndUiModeConfigurations(JNIEnv* env, jlong ptr) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
auto configurations = assetmanager->GetResourceConfigurations(true /*exclude_system*/,
false /*exclude_mipmap*/);
@@ -1057,6 +1059,14 @@ static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, j
return array;
}
+static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
+ return GetSizeAndUiModeConfigurations(env, ptr);
+}
+
+static jobjectArray NativeGetSizeAndUiModeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
+ return GetSizeAndUiModeConfigurations(env, ptr);
+}
+
static jintArray NativeAttributeResolutionStack(
JNIEnv* env, jclass /*clazz*/, jlong ptr,
jlong theme_ptr, jint xml_style_res,
@@ -1487,6 +1497,8 @@ static const JNINativeMethod gAssetManagerMethods[] = {
{"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales},
{"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;",
(void*)NativeGetSizeConfigurations},
+ {"nativeGetSizeAndUiModeConfigurations", "(J)[Landroid/content/res/Configuration;",
+ (void*)NativeGetSizeAndUiModeConfigurations},
// Style attribute related methods.
{"nativeAttributeResolutionStack", "(JJIII)[I", (void*)NativeAttributeResolutionStack},
@@ -1565,6 +1577,7 @@ int register_android_content_AssetManager(JNIEnv* env) {
GetFieldIDOrDie(env, configurationClass, "screenHeightDp", "I");
gConfigurationOffsets.mScreenLayoutOffset =
GetFieldIDOrDie(env, configurationClass, "screenLayout", "I");
+ gConfigurationOffsets.mUiMode = GetFieldIDOrDie(env, configurationClass, "uiMode", "I");
jclass arrayMapClass = FindClassOrDie(env, "android/util/ArrayMap");
gArrayMapOffsets.classObject = MakeGlobalRefOrDie(env, arrayMapClass);
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 562078c0a654..92d23327e6c4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5430,6 +5430,12 @@
treatment for stretched issues in camera viewfinder. -->
<bool name="config_isCameraCompatControlForStretchedIssuesEnabled">false</bool>
+ <!-- Docking is a uiMode configuration change and will cause activities to relaunch if it's not
+ handled. If true, the configuration change will be sent but activities will not be
+ relaunched upon docking. Apps with desk resources will behave like normal, since they may
+ expect the relaunch upon the desk uiMode change. -->
+ <bool name="config_skipActivityRelaunchWhenDocking">false</bool>
+
<!-- If true, hide the display cutout with display area -->
<bool name="config_hideDisplayCutoutWithDisplayArea">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e21b7f6732f3..d8b261c50cb1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4491,6 +4491,7 @@
<java-symbol type="bool" name="config_isWindowManagerCameraCompatTreatmentEnabled" />
<java-symbol type="bool" name="config_isWindowManagerCameraCompatSplitScreenAspectRatioEnabled" />
<java-symbol type="bool" name="config_isCameraCompatControlForStretchedIssuesEnabled" />
+ <java-symbol type="bool" name="config_skipActivityRelaunchWhenDocking" />
<java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index a8626dfe043a..acadf4a03db5 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -100,6 +100,7 @@ import static android.content.res.Configuration.EMPTY;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
+import static android.content.res.Configuration.UI_MODE_TYPE_DESK;
import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
import static android.content.res.Configuration.UI_MODE_TYPE_VR_HEADSET;
import static android.os.Build.VERSION_CODES.HONEYCOMB;
@@ -827,6 +828,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/** Whether the input to this activity will be dropped during the current playing animation. */
private boolean mIsInputDroppedForAnimation;
+ /**
+ * Whether the application has desk mode resources. Calculated and cached when
+ * {@link #hasDeskResources()} is called.
+ */
+ @Nullable
+ private Boolean mHasDeskResources;
+
boolean mHandleExitSplashScreen;
@TransferSplashScreenState
int mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
@@ -9490,7 +9498,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
configChanged |= CONFIG_UI_MODE;
}
- return (changes&(~configChanged)) != 0;
+ // TODO(b/274944389): remove workaround after long-term solution is implemented
+ // Don't restart due to desk mode change if the app does not have desk resources.
+ if (mWmService.mSkipActivityRelaunchWhenDocking && onlyDeskInUiModeChanged(changesConfig)
+ && !hasDeskResources()) {
+ configChanged |= CONFIG_UI_MODE;
+ }
+
+ return (changes & (~configChanged)) != 0;
}
/**
@@ -9503,6 +9518,50 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
!= isInVrUiMode(lastReportedConfig));
}
+ /**
+ * Returns true if the uiMode configuration changed, and desk mode
+ * ({@link android.content.res.Configuration#UI_MODE_TYPE_DESK}) was the only change to uiMode.
+ */
+ private boolean onlyDeskInUiModeChanged(Configuration lastReportedConfig) {
+ final Configuration currentConfig = getConfiguration();
+
+ boolean deskModeChanged = isInDeskUiMode(currentConfig) != isInDeskUiMode(
+ lastReportedConfig);
+ // UI mode contains fields other than the UI mode type, so determine if any other fields
+ // changed.
+ boolean uiModeOtherFieldsChanged =
+ (currentConfig.uiMode & ~UI_MODE_TYPE_MASK) != (lastReportedConfig.uiMode
+ & ~UI_MODE_TYPE_MASK);
+
+ return deskModeChanged && !uiModeOtherFieldsChanged;
+ }
+
+ /**
+ * Determines whether or not the application has desk mode resources.
+ */
+ boolean hasDeskResources() {
+ if (mHasDeskResources != null) {
+ // We already determined this, return the cached value.
+ return mHasDeskResources;
+ }
+
+ mHasDeskResources = false;
+ try {
+ Resources packageResources = mAtmService.mContext.createPackageContextAsUser(
+ packageName, 0, UserHandle.of(mUserId)).getResources();
+ for (Configuration sizeConfiguration :
+ packageResources.getSizeAndUiModeConfigurations()) {
+ if (isInDeskUiMode(sizeConfiguration)) {
+ mHasDeskResources = true;
+ break;
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "Exception thrown during checking for desk resources " + this, e);
+ }
+ return mHasDeskResources;
+ }
+
private int getConfigurationChanges(Configuration lastReportedConfig) {
// Determine what has changed. May be nothing, if this is a config that has come back from
// the app after going idle. In that case we just want to leave the official config object
@@ -9798,6 +9857,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return (config.uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_VR_HEADSET;
}
+ private static boolean isInDeskUiMode(Configuration config) {
+ return (config.uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_DESK;
+ }
+
String getProcessName() {
return info.applicationInfo.processName;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ccce558b73ce..39165dd0bfa9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -552,6 +552,16 @@ public class WindowManagerService extends IWindowManager.Stub
// everything else on screen). Otherwise, it will be put under always-on-top stacks.
final boolean mAssistantOnTopOfDream;
+ /**
+ * If true, don't relaunch the activity upon receiving a configuration change to transition to
+ * or from the {@link UI_MODE_TYPE_DESK} uiMode, which is sent when docking. The configuration
+ * change will still be sent regardless, only the relaunch is skipped. Apps with desk resources
+ * are exempt from this and will behave like normal, since they may expect the relaunch upon the
+ * desk uiMode change.
+ */
+ @VisibleForTesting
+ boolean mSkipActivityRelaunchWhenDocking;
+
final boolean mLimitedAlphaCompositing;
final int mMaxUiWidth;
@@ -1226,6 +1236,8 @@ public class WindowManagerService extends IWindowManager.Stub
com.android.internal.R.bool.config_perDisplayFocusEnabled);
mAssistantOnTopOfDream = context.getResources().getBoolean(
com.android.internal.R.bool.config_assistantOnTopOfDream);
+ mSkipActivityRelaunchWhenDocking = context.getResources()
+ .getBoolean(R.bool.config_skipActivityRelaunchWhenDocking);
mLetterboxConfiguration = new LetterboxConfiguration(
// Using SysUI context to have access to Material colors extracted from Wallpaper.
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 0300eb06c220..2cc752cd1b1a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -45,6 +45,7 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.UI_MODE_TYPE_DESK;
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.os.Process.NOBODY_UID;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -490,6 +491,62 @@ public class ActivityRecordTests extends WindowTestsBase {
}
@Test
+ public void testDeskModeChange_doesNotRelaunch() throws RemoteException {
+ mWm.mSkipActivityRelaunchWhenDocking = true;
+
+ final ActivityRecord activity = createActivityWithTask();
+ // The activity will already be relaunching out of the gate, finish the relaunch so we can
+ // test properly.
+ activity.finishRelaunching();
+ // Clear out any calls to scheduleTransaction from launching the activity.
+ reset(mAtm.getLifecycleManager());
+
+ final Task task = activity.getTask();
+ activity.setState(RESUMED, "Testing");
+
+ // Send a desk UI mode config update.
+ final Configuration newConfig = new Configuration(task.getConfiguration());
+ newConfig.uiMode |= UI_MODE_TYPE_DESK;
+ task.onRequestedOverrideConfigurationChanged(newConfig);
+ ensureActivityConfiguration(activity);
+
+ // The activity shouldn't start relaunching since it doesn't have any desk resources.
+ assertFalse(activity.isRelaunching());
+
+ // The configuration change is still sent to the activity, even if it doesn't relaunch.
+ final ActivityConfigurationChangeItem expected =
+ ActivityConfigurationChangeItem.obtain(newConfig);
+ verify(mAtm.getLifecycleManager()).scheduleTransaction(
+ eq(activity.app.getThread()), eq(activity.token), eq(expected));
+ }
+
+ @Test
+ public void testDeskModeChange_relaunchesWithDeskResources() {
+ mWm.mSkipActivityRelaunchWhenDocking = true;
+
+ final ActivityRecord activity = createActivityWithTask();
+ // The activity will already be relaunching out of the gate, finish the relaunch so we can
+ // test properly.
+ activity.finishRelaunching();
+
+ // Activities with desk resources should get relaunched when a UI_MODE_TYPE_DESK change
+ // comes in.
+ doReturn(true).when(activity).hasDeskResources();
+
+ final Task task = activity.getTask();
+ activity.setState(RESUMED, "Testing");
+
+ // Send a desk UI mode config update.
+ final Configuration newConfig = new Configuration(task.getConfiguration());
+ newConfig.uiMode |= UI_MODE_TYPE_DESK;
+ task.onRequestedOverrideConfigurationChanged(newConfig);
+ ensureActivityConfiguration(activity);
+
+ // The activity will relaunch since it has desk resources.
+ assertTrue(activity.isRelaunching());
+ }
+
+ @Test
public void testSetRequestedOrientationUpdatesConfiguration() throws Exception {
final ActivityRecord activity = new ActivityBuilder(mAtm)
.setCreateTask(true)