diff options
18 files changed, 242 insertions, 151 deletions
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java index 668dc6b8ec63..73678d9f2dda 100644 --- a/core/java/android/app/ActivityClient.java +++ b/core/java/android/app/ActivityClient.java @@ -98,6 +98,15 @@ public class ActivityClient { } } + /** Reports the activity starts local relaunch. */ + public void activityLocalRelaunch(IBinder token) { + try { + getActivityClientController().activityLocalRelaunch(token); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + /** Reports the activity has completed relaunched. */ public void activityRelaunched(IBinder token) { try { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index b4cabada0522..681be7e14f71 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3410,26 +3410,12 @@ public final class ActivityThread extends ClientTransactionHandler } } - /** - * Returns {@code true} if the {@link android.app.ActivityManager.ProcessState} of the current - * process is cached. - */ - @Override - @VisibleForTesting - public boolean isCachedProcessState() { - synchronized (mAppThread) { - return mLastProcessState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY; - } - } - @Override public void updateProcessState(int processState, boolean fromIpc) { - final boolean wasCached; synchronized (mAppThread) { if (mLastProcessState == processState) { return; } - wasCached = isCachedProcessState(); mLastProcessState = processState; // Defer the top state for VM to avoid aggressive JIT compilation affecting activity // launch time. @@ -3446,22 +3432,6 @@ public final class ActivityThread extends ClientTransactionHandler + (fromIpc ? " (from ipc" : "")); } } - - // Handle the pending configuration if the process state is changed from cached to - // non-cached. Except the case where there is a launching activity because the - // LaunchActivityItem will handle it. - if (wasCached && !isCachedProcessState() && mNumLaunchingActivities.get() == 0) { - final Configuration pendingConfig = - mConfigurationController.getPendingConfiguration(false /* clearPending */); - if (pendingConfig == null) { - return; - } - if (Looper.myLooper() == mH.getLooper()) { - handleConfigurationChanged(pendingConfig); - } else { - sendMessage(H.CONFIGURATION_CHANGED, pendingConfig); - } - } } /** Update VM state based on ActivityManager.PROCESS_STATE_* constants. */ @@ -5728,6 +5698,7 @@ public final class ActivityThread extends ClientTransactionHandler return; } + ActivityClient.getInstance().activityLocalRelaunch(r.token); // Initialize a relaunch request. final MergedConfiguration mergedConfiguration = new MergedConfiguration( r.createdConfig != null diff --git a/core/java/android/app/ActivityThreadInternal.java b/core/java/android/app/ActivityThreadInternal.java index b9ad5c337813..72506b9fcdbb 100644 --- a/core/java/android/app/ActivityThreadInternal.java +++ b/core/java/android/app/ActivityThreadInternal.java @@ -32,8 +32,6 @@ interface ActivityThreadInternal { boolean isInDensityCompatMode(); - boolean isCachedProcessState(); - Application getApplication(); ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts); diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java index 1a77b65c8ef6..18dc1ce18baf 100644 --- a/core/java/android/app/ConfigurationController.java +++ b/core/java/android/app/ConfigurationController.java @@ -124,12 +124,6 @@ class ConfigurationController { * @param config The new configuration. */ void handleConfigurationChanged(@NonNull Configuration config) { - if (mActivityThread.isCachedProcessState()) { - updatePendingConfiguration(config); - // If the process is in a cached state, delay the handling until the process is no - // longer cached. - return; - } Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged"); handleConfigurationChanged(config, null /* compat */); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl index 130716122ed2..0138186974a6 100644 --- a/core/java/android/app/IActivityClientController.aidl +++ b/core/java/android/app/IActivityClientController.aidl @@ -53,6 +53,7 @@ interface IActivityClientController { oneway void activityStopped(in IBinder token, in Bundle state, in PersistableBundle persistentState, in CharSequence description); oneway void activityDestroyed(in IBinder token); + oneway void activityLocalRelaunch(in IBinder token); oneway void activityRelaunched(in IBinder token); oneway void reportSizeConfigurations(in IBinder token, diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 2a1883d5e0bb..d275c8336251 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -365,8 +365,8 @@ public class ResourcesManager { @NonNull Configuration config) { config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH; config.densityDpi = dm.densityDpi; - config.screenWidthDp = (int) (dm.widthPixels / dm.density); - config.screenHeightDp = (int) (dm.heightPixels / dm.density); + config.screenWidthDp = (int) (dm.widthPixels / dm.density + 0.5f); + config.screenHeightDp = (int) (dm.heightPixels / dm.density + 0.5f); int sl = Configuration.resetScreenLayout(config.screenLayout); if (dm.widthPixels > dm.heightPixels) { config.orientation = Configuration.ORIENTATION_LANDSCAPE; diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index bfb2fd57975f..a2d4bafef41c 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -31,7 +31,6 @@ import static org.junit.Assert.assertTrue; import android.annotation.Nullable; import android.app.Activity; -import android.app.ActivityManager; import android.app.ActivityThread; import android.app.ActivityThread.ActivityClientRecord; import android.app.IApplicationThread; @@ -571,53 +570,6 @@ public class ActivityThreadTest { } @Test - public void testHandleProcessConfigurationChanged_DependOnProcessState() { - final ActivityThread activityThread = ActivityThread.currentActivityThread(); - final Configuration origConfig = activityThread.getConfiguration(); - final int newDpi = origConfig.densityDpi + 10; - final Configuration newConfig = new Configuration(origConfig); - newConfig.seq++; - newConfig.densityDpi = newDpi; - - activityThread.updateProcessState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY, - false /* fromIPC */); - - applyProcessConfiguration(activityThread, newConfig); - try { - // In the cached state, the configuration is only set as pending and not applied. - assertEquals(origConfig.densityDpi, activityThread.getConfiguration().densityDpi); - assertTrue(activityThread.isCachedProcessState()); - } finally { - // The foreground state is the default state of instrumentation. - activityThread.updateProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, - false /* fromIPC */); - } - InstrumentationRegistry.getInstrumentation().waitForIdleSync(); - - try { - // The state becomes non-cached, the pending configuration should be applied. - assertEquals(newConfig.densityDpi, activityThread.getConfiguration().densityDpi); - assertFalse(activityThread.isCachedProcessState()); - } finally { - // Restore to the original configuration. - activityThread.getConfiguration().seq = origConfig.seq - 1; - applyProcessConfiguration(activityThread, origConfig); - } - } - - private static void applyProcessConfiguration(ActivityThread thread, Configuration config) { - final ClientTransaction clientTransaction = newTransaction(thread, - null /* activityToken */); - clientTransaction.addCallback(ConfigurationChangeItem.obtain(config)); - final IApplicationThread appThread = thread.getApplicationThread(); - try { - appThread.scheduleTransaction(clientTransaction); - } catch (Exception ignored) { - } - InstrumentationRegistry.getInstrumentation().waitForIdleSync(); - } - - @Test public void testResumeAfterNewIntent() { final Activity activity = mActivityTestRule.launchActivity(new Intent()); final ActivityThread activityThread = activity.getActivityThread(); diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index beca71486899..1a6155b43f6b 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -121,17 +121,19 @@ public abstract class ApexManager { public final File apexDirectory; public final File preInstalledApexPath; public final File apexFile; + public final boolean activeApexChanged; private ActiveApexInfo(File apexDirectory, File preInstalledApexPath, File apexFile) { - this(null, apexDirectory, preInstalledApexPath, apexFile); + this(null, apexDirectory, preInstalledApexPath, apexFile, false); } private ActiveApexInfo(@Nullable String apexModuleName, File apexDirectory, - File preInstalledApexPath, File apexFile) { + File preInstalledApexPath, File apexFile, boolean activeApexChanged) { this.apexModuleName = apexModuleName; this.apexDirectory = apexDirectory; this.preInstalledApexPath = preInstalledApexPath; this.apexFile = apexFile; + this.activeApexChanged = activeApexChanged; } private ActiveApexInfo(ApexInfo apexInfo) { @@ -140,7 +142,8 @@ public abstract class ApexManager { new File(Environment.getApexDirectory() + File.separator + apexInfo.moduleName), new File(apexInfo.preinstalledModulePath), - new File(apexInfo.modulePath)); + new File(apexInfo.modulePath), + apexInfo.activeApexChanged); } } diff --git a/services/core/java/com/android/server/pm/FileInstallArgs.java b/services/core/java/com/android/server/pm/FileInstallArgs.java index 85c3cc91ecf0..e3ceccd1abb8 100644 --- a/services/core/java/com/android/server/pm/FileInstallArgs.java +++ b/services/core/java/com/android/server/pm/FileInstallArgs.java @@ -172,9 +172,22 @@ class FileInstallArgs extends InstallArgs { return false; } - if (!onIncremental && !SELinux.restoreconRecursive(afterCodeFile)) { - Slog.w(TAG, "Failed to restorecon"); - return false; + if (onIncremental) { + Slog.i(TAG, PackageManagerServiceUtils.SELINUX_BUG + + ": Skipping restorecon for Incremental install of " + beforeCodeFile); + } else { + try { + if (!SELinux.restoreconRecursive(afterCodeFile)) { + Slog.w(TAG, "Failed to restorecon"); + return false; + } + PackageManagerServiceUtils.verifySelinuxLabels(afterCodeFile.getAbsolutePath()); + } catch (Exception e) { + Slog.e(TAG, + PackageManagerServiceUtils.SELINUX_BUG + ": Exception from restorecon on " + + beforeCodeFile, e); + throw e; + } } // Reflect the rename internally diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index 2d8d4f588192..7932897f295a 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -648,6 +648,10 @@ final class InstallPackageHelper { Log.v(TAG, "restoreAndPostInstall userId=" + userId + " package=" + res.mPkg); } + if (res.mPkg != null) { + PackageManagerServiceUtils.verifySelinuxLabels(res.mPkg.getPath()); + } + // A restore should be requested at this point if (a) the install // succeeded, (b) the operation is not an update. final boolean update = res.mRemovedInfo != null @@ -3566,6 +3570,7 @@ final class InstallPackageHelper { @ParsingPackageUtils.ParseFlags int parseFlags, @PackageManagerService.ScanFlags int scanFlags, @Nullable UserHandle user) throws PackageManagerException { + PackageManagerServiceUtils.verifySelinuxLabels(parsedPackage.getPath()); final Pair<ScanResult, Boolean> scanResultPair = scanSystemPackageLI( parsedPackage, parseFlags, scanFlags, user); diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 4d11b13510e9..703be169f14c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -19,6 +19,7 @@ package com.android.server.pm; import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; +import static android.content.pm.SigningDetails.CertCapabilities.SHARED_USER_ID; import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDWR; @@ -60,6 +61,7 @@ import android.os.Debug; import android.os.Environment; import android.os.FileUtils; import android.os.Process; +import android.os.SELinux; import android.os.SystemProperties; import android.os.incremental.IncrementalManager; import android.os.incremental.IncrementalStorage; @@ -564,13 +566,8 @@ public class PackageManagerServiceUtils { // the older ones. We check to see if either the new package is signed by an older cert // with which the current sharedUser is ok, or if it is signed by a newer one, and is ok // with being sharedUser with the existing signing cert. - boolean match = - parsedSignatures.checkCapability( - sharedUserSetting.getSigningDetails(), - SigningDetails.CertCapabilities.SHARED_USER_ID) - || sharedUserSetting.getSigningDetails().checkCapability( - parsedSignatures, - SigningDetails.CertCapabilities.SHARED_USER_ID); + boolean match = canJoinSharedUserId(parsedSignatures, + sharedUserSetting.getSigningDetails()); // Special case: if the sharedUserId capability check failed it could be due to this // being the only package in the sharedUserId so far and the lineage being updated to // deny the sharedUserId capability of the previous key in the lineage. @@ -645,6 +642,28 @@ public class PackageManagerServiceUtils { } /** + * Returns whether the package with {@code packageSigningDetails} can join the sharedUserId + * with {@code sharedUserSigningDetails}. + * <p> + * A sharedUserId maintains a shared {@link SigningDetails} containing the full lineage and + * capabilities for each package in the sharedUserId. A package can join the sharedUserId if + * its current signer is the same as the shared signer, or if the current signer of either + * is in the signing lineage of the other with the {@link + * SigningDetails.CertCapabilities#SHARED_USER_ID} capability granted to that previous signer + * in the lineage. + * + * @param packageSigningDetails the {@code SigningDetails} of the package seeking to join the + * sharedUserId + * @param sharedUserSigningDetails the {@code SigningDetails} of the sharedUserId + * @return true if the package seeking to join the sharedUserId meets the requirements + */ + public static boolean canJoinSharedUserId(@NonNull SigningDetails packageSigningDetails, + @NonNull SigningDetails sharedUserSigningDetails) { + return packageSigningDetails.checkCapability(sharedUserSigningDetails, SHARED_USER_ID) + || sharedUserSigningDetails.checkCapability(packageSigningDetails, SHARED_USER_ID); + } + + /** * Extract native libraries to a target path */ public static int extractNativeBinaries(File dstCodePath, String packageName) { @@ -1388,4 +1407,28 @@ public class PackageManagerServiceUtils { } } } + + // TODO(b/231951809): remove this workaround after figuring out why apk_tmp_file labels stay + // on the installed apps instead of the correct apk_data_file ones + + public static final String SELINUX_BUG = "b/231951809"; + + /** + * A workaround for b/231951809: + * Verifies the SELinux labels of the passed path, and tries to correct them if detects them + * wrong or missing. + */ + public static void verifySelinuxLabels(String path) { + final String expectedCon = SELinux.fileSelabelLookup(path); + final String actualCon = SELinux.getFileContext(path); + Slog.i(TAG, SELINUX_BUG + ": checking selinux labels for " + path + " expected / actual: " + + expectedCon + " / " + actualCon); + if (expectedCon == null || !expectedCon.equals(actualCon)) { + Slog.w(TAG, SELINUX_BUG + ": labels don't match, reapplying for " + path); + if (!SELinux.restoreconRecursive(new File(path))) { + Slog.w(TAG, SELINUX_BUG + ": Failed to reapply restorecon"); + } + // well, if it didn't work now after not working at first, not much else can be done + } + } } diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java index 5fc916f888f3..d6a133e43789 100644 --- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java +++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java @@ -22,11 +22,9 @@ import static android.content.pm.SigningDetails.CapabilityMergeRule.MERGE_RESTRI import static com.android.server.pm.PackageManagerService.SCAN_BOOTING; import static com.android.server.pm.PackageManagerService.SCAN_DONT_KILL_APP; -import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures; import android.content.pm.PackageManager; import android.content.pm.SharedLibraryInfo; -import android.content.pm.Signature; import android.content.pm.SigningDetails; import android.os.SystemProperties; import android.util.ArrayMap; @@ -212,12 +210,10 @@ final class ReconcilePackageUtils { // the signatures on the first package scanned for the shared user (i.e. if the // signaturesChanged state hasn't been initialized yet in SharedUserSetting). if (sharedUserSetting != null) { - final Signature[] sharedUserSignatures = sharedUserSetting - .signatures.mSigningDetails.getSignatures(); if (sharedUserSetting.signaturesChanged != null - && compareSignatures(sharedUserSignatures, - parsedPackage.getSigningDetails().getSignatures()) - != PackageManager.SIGNATURE_MATCH) { + && !PackageManagerServiceUtils.canJoinSharedUserId( + parsedPackage.getSigningDetails(), + sharedUserSetting.getSigningDetails())) { if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) { // Mismatched signatures is an error and silently skipping system // packages will likely break the device in unforeseen ways. diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index d7a0ca0231d1..6113af45d6be 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -246,6 +246,18 @@ class ActivityClientController extends IActivityClientController.Stub { } @Override + public void activityLocalRelaunch(IBinder token) { + final long origId = Binder.clearCallingIdentity(); + synchronized (mGlobalLock) { + final ActivityRecord r = ActivityRecord.forTokenLocked(token); + if (r != null) { + r.startRelaunching(); + } + } + Binder.restoreCallingIdentity(origId); + } + + @Override public void activityRelaunched(IBinder token) { final long origId = Binder.clearCallingIdentity(); synchronized (mGlobalLock) { diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 224b4d37d139..1f73a784932e 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -2212,9 +2212,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final float density = mDisplayMetrics.density; outConfig.screenWidthDp = (int) (mDisplayPolicy.getConfigDisplayWidth(dw, dh, rotation, - uiMode, displayCutout) / density); + uiMode, displayCutout) / density + 0.5f); outConfig.screenHeightDp = (int) (mDisplayPolicy.getConfigDisplayHeight(dw, dh, rotation, - uiMode, displayCutout) / density); + uiMode, displayCutout) / density + 0.5f); outConfig.compatScreenWidthDp = (int) (outConfig.screenWidthDp / mCompatibleScreenScale); outConfig.compatScreenHeightDp = (int) (outConfig.screenHeightDp / mCompatibleScreenScale); diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 40417a4857d3..1c64a06036f0 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY; import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.content.res.Configuration.ASSETS_SEQ_UNDEFINED; @@ -195,6 +196,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio /** Whether the process configuration is waiting to be dispatched to the process. */ private boolean mHasPendingConfigurationChange; + /** If the process state is in (<=) the cached state, then defer delivery of the config. */ + private static final int CACHED_CONFIG_PROC_STATE = PROCESS_STATE_CACHED_ACTIVITY; + /** Whether {@link #mLastReportedConfiguration} is deferred by the cached state. */ + private volatile boolean mHasCachedConfiguration; + /** * Registered {@link DisplayArea} as a listener to override config changes. {@code null} if not * registered. @@ -316,8 +322,27 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return mCurProcState; } + /** + * Sets the computed process state from the oom adjustment calculation. This is frequently + * called in activity manager's lock, so don't use window manager lock here. + */ + @HotPath(caller = HotPath.OOM_ADJUSTMENT) public void setReportedProcState(int repProcState) { + final int prevProcState = mRepProcState; mRepProcState = repProcState; + + // Deliver the cached config if the app changes from cached state to non-cached state. + final IApplicationThread thread = mThread; + if (prevProcState >= CACHED_CONFIG_PROC_STATE && repProcState < CACHED_CONFIG_PROC_STATE + && thread != null && mHasCachedConfiguration) { + final Configuration config; + synchronized (mLastReportedConfiguration) { + config = new Configuration(mLastReportedConfiguration); + } + // Schedule immediately to make sure the app component (e.g. receiver, service) can get + // the latest configuration in their lifecycle callbacks (e.g. onReceive, onCreate). + scheduleConfigurationChange(thread, config); + } } int getReportedProcState() { @@ -1328,12 +1353,22 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio @Override public void onConfigurationChanged(Configuration newGlobalConfig) { super.onConfigurationChanged(newGlobalConfig); - updateConfiguration(); - } + final Configuration config = getConfiguration(); + if (mLastReportedConfiguration.equals(config)) { + // Nothing changed. + if (Build.IS_DEBUGGABLE && mHasImeService) { + // TODO (b/135719017): Temporary log for debugging IME service. + Slog.w(TAG_CONFIGURATION, "Current config: " + config + + " unchanged for IME proc " + mName); + } + return; + } - @Override - public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) { - super.onRequestedOverrideConfigurationChanged(overrideConfiguration); + if (mPauseConfigurationDispatchCount > 0) { + mHasPendingConfigurationChange = true; + return; + } + dispatchConfiguration(config); } @Override @@ -1359,25 +1394,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio resolvedConfig.seq = newParentConfig.seq; } - private void updateConfiguration() { - final Configuration config = getConfiguration(); - if (mLastReportedConfiguration.diff(config) == 0) { - // Nothing changed. - if (Build.IS_DEBUGGABLE && mHasImeService) { - // TODO (b/135719017): Temporary log for debugging IME service. - Slog.w(TAG_CONFIGURATION, "Current config: " + config - + " unchanged for IME proc " + mName); - } - return; - } - - if (mPauseConfigurationDispatchCount > 0) { - mHasPendingConfigurationChange = true; - return; - } - dispatchConfiguration(config); - } - void dispatchConfiguration(Configuration config) { mHasPendingConfigurationChange = false; if (mThread == null) { @@ -1388,29 +1404,47 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } return; } + + config.seq = mAtm.increaseConfigurationSeqLocked(); + setLastReportedConfiguration(config); + + // A cached process doesn't have running application components, so it is unnecessary to + // notify the configuration change. The last-reported-configuration is still set because + // setReportedProcState() should not write any fields that require WM lock. + if (mRepProcState >= CACHED_CONFIG_PROC_STATE) { + mHasCachedConfiguration = true; + // Because there are 2 volatile accesses in setReportedProcState(): mRepProcState and + // mHasCachedConfiguration, check again in case mRepProcState is changed but hasn't + // read the change of mHasCachedConfiguration. + if (mRepProcState >= CACHED_CONFIG_PROC_STATE) { + return; + } + } + + scheduleConfigurationChange(mThread, config); + } + + private void scheduleConfigurationChange(IApplicationThread thread, Configuration config) { ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s new config %s", mName, config); if (Build.IS_DEBUGGABLE && mHasImeService) { // TODO (b/135719017): Temporary log for debugging IME service. Slog.v(TAG_CONFIGURATION, "Sending to IME proc " + mName + " new config " + config); } - + mHasCachedConfiguration = false; try { - config.seq = mAtm.increaseConfigurationSeqLocked(); - mAtm.getLifecycleManager().scheduleTransaction(mThread, + mAtm.getLifecycleManager().scheduleTransaction(thread, ConfigurationChangeItem.obtain(config)); - setLastReportedConfiguration(config); } catch (Exception e) { - Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e); + Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change: " + mOwner, e); } } void setLastReportedConfiguration(Configuration config) { - mLastReportedConfiguration.setTo(config); - } - - Configuration getLastReportedConfiguration() { - return mLastReportedConfiguration; + // Synchronize for the access from setReportedProcState(). + synchronized (mLastReportedConfiguration) { + mLastReportedConfiguration.setTo(config); + } } void pauseConfigurationDispatch() { @@ -1461,6 +1495,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // config seq. This increment ensures that the client won't ignore the configuration. config.seq = mAtm.increaseConfigurationSeqLocked(); } + // LaunchActivityItem includes the latest process configuration. + mHasCachedConfiguration = false; return config; } @@ -1688,7 +1724,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } pw.println(prefix + " Configuration=" + getConfiguration()); pw.println(prefix + " OverrideConfiguration=" + getRequestedOverrideConfiguration()); - pw.println(prefix + " mLastReportedConfiguration=" + mLastReportedConfiguration); + pw.println(prefix + " mLastReportedConfiguration=" + (mHasCachedConfiguration + ? ("(cached) " + mLastReportedConfiguration) : mLastReportedConfiguration)); final int stateFlags = mActivityStateFlags; if (stateFlags != ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER) { diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java index 8f5b0e19f07e..ab292ab5381e 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java @@ -546,6 +546,18 @@ public class ApexManagerTest { assertThat(backingApexFile).isNull(); } + @Test + public void testActiveApexChanged() throws RemoteException { + ApexInfo apex1 = createApexInfo( + "com.apex1", 37, true, true, new File("/data/apex/active/com.apex@37.apex")); + apex1.activeApexChanged = true; + apex1.preinstalledModulePath = apex1.modulePath; + when(mApexService.getActivePackages()).thenReturn(new ApexInfo[]{apex1}); + final ApexManager.ActiveApexInfo activeApex = mApexManager.getActiveApexInfos().get(0); + assertThat(activeApex.apexModuleName).isEqualTo("com.apex1"); + assertThat(activeApex.activeApexChanged).isTrue(); + } + private ApexInfo createApexInfoForTestPkg(boolean isActive, boolean isFactory, int version) { File apexFile = extractResource(TEST_APEX_PKG, TEST_APEX_FILE_NAME); ApexInfo apexInfo = new ApexInfo(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 2938f1b3e8a2..4716d15a1aca 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -2215,23 +2215,28 @@ public class DisplayContentTests extends WindowTestsBase { */ @Test public void testCreateTestDisplayContentFromDimensions() { - final int displayWidth = 1000; - final int displayHeight = 2000; + final int displayWidth = 540; + final int displayHeight = 960; + final int density = 192; + final int expectedWidthDp = 450; // = 540/(192/160) + final int expectedHeightDp = 800; // = 960/(192/160) final int windowingMode = WINDOWING_MODE_FULLSCREEN; final boolean ignoreOrientationRequests = false; final float fixedOrientationLetterboxRatio = 0; final DisplayContent testDisplayContent = new TestDisplayContent.Builder(mAtm, displayWidth, - displayHeight).build(); + displayHeight).setDensityDpi(density).build(); // test display info final DisplayInfo di = testDisplayContent.getDisplayInfo(); assertEquals(displayWidth, di.logicalWidth); assertEquals(displayHeight, di.logicalHeight); - assertEquals(TestDisplayContent.DEFAULT_LOGICAL_DISPLAY_DENSITY, di.logicalDensityDpi); + assertEquals(density, di.logicalDensityDpi); // test configuration - final WindowConfiguration windowConfig = testDisplayContent.getConfiguration() - .windowConfiguration; + final Configuration config = testDisplayContent.getConfiguration(); + assertEquals(expectedWidthDp, config.screenWidthDp); + assertEquals(expectedHeightDp, config.screenHeightDp); + final WindowConfiguration windowConfig = config.windowConfiguration; assertEquals(displayWidth, windowConfig.getBounds().width()); assertEquals(displayHeight, windowConfig.getBounds().height()); assertEquals(windowingMode, windowConfig.getWindowingMode()); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index 746f2b50ac3a..3abf7ce665ae 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; @@ -39,24 +40,30 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import android.Manifest; +import android.app.ActivityManager; +import android.app.ClientTransactionHandler; import android.app.IApplicationThread; +import android.app.servertransaction.ConfigurationChangeItem; import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.os.LocaleList; +import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mockito; @@ -282,6 +289,39 @@ public class WindowProcessControllerTests extends WindowTestsBase { } @Test + public void testCachedStateConfigurationChange() throws RemoteException { + final ClientLifecycleManager clientManager = mAtm.getLifecycleManager(); + doNothing().when(clientManager).scheduleTransaction(any(), any()); + final IApplicationThread thread = mWpc.getThread(); + final Configuration newConfig = new Configuration(mWpc.getConfiguration()); + newConfig.densityDpi += 100; + // Non-cached state will send the change directly. + mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + clearInvocations(clientManager); + mWpc.onConfigurationChanged(newConfig); + verify(clientManager).scheduleTransaction(eq(thread), any()); + + // Cached state won't send the change. + clearInvocations(clientManager); + mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY); + newConfig.densityDpi += 100; + mWpc.onConfigurationChanged(newConfig); + verify(clientManager, never()).scheduleTransaction(eq(thread), any()); + + // Cached -> non-cached will send the previous deferred config immediately. + mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_RECEIVER); + final ArgumentCaptor<ConfigurationChangeItem> captor = + ArgumentCaptor.forClass(ConfigurationChangeItem.class); + verify(clientManager).scheduleTransaction(eq(thread), captor.capture()); + final ClientTransactionHandler client = mock(ClientTransactionHandler.class); + captor.getValue().preExecute(client, null /* token */); + final ArgumentCaptor<Configuration> configCaptor = + ArgumentCaptor.forClass(Configuration.class); + verify(client).updatePendingConfiguration(configCaptor.capture()); + assertEquals(newConfig, configCaptor.getValue()); + } + + @Test public void testComputeOomAdjFromActivities() { final ActivityRecord activity = createActivityRecord(mWpc); activity.mVisibleRequested = true; |