Merge "Implement flag to not relaunch on dock config change for apps without -desk resources" into tm-qpr-dev
This commit is contained in:
@@ -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;
|
||||
@@ -5938,6 +5953,25 @@ public final class ActivityThread extends ClientTransactionHandler
|
||||
return configToReport;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
||||
@@ -1452,6 +1452,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.
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -203,6 +203,10 @@ public class ResourcesImpl {
|
||||
return mAssets.getSizeConfigurations();
|
||||
}
|
||||
|
||||
Configuration[] getSizeAndUiModeConfigurations() {
|
||||
return mAssets.getSizeAndUiModeConfigurations();
|
||||
}
|
||||
|
||||
CompatibilityInfo getCompatibilityInfo() {
|
||||
return mDisplayAdjustments.getCompatibilityInfo();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
@@ -489,6 +490,62 @@ public class ActivityRecordTests extends WindowTestsBase {
|
||||
.scheduleTransaction(any(), any(), isA(ActivityConfigurationChangeItem.class));
|
||||
}
|
||||
|
||||
@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)
|
||||
|
||||
Reference in New Issue
Block a user