diff options
42 files changed, 1223 insertions, 339 deletions
diff --git a/Android.bp b/Android.bp index d136a5dba998..121decbbec50 100644 --- a/Android.bp +++ b/Android.bp @@ -390,7 +390,6 @@ java_library { platform_compat_config { name: "framework-platform-compat-config", - prefix: "framework", src: ":framework-annotation-proc", } diff --git a/api/removed.txt b/api/removed.txt index db784a8d792c..74a934672adb 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -5,10 +5,6 @@ package android.app { method @Deprecated public static int getMaxNumPictureInPictureActions(); } - public class KeyguardManager { - method @Deprecated public void dismissKeyguard(@NonNull android.app.Activity, @Nullable android.app.KeyguardManager.KeyguardDismissCallback, @Nullable android.os.Handler); - } - public class Notification implements android.os.Parcelable { method @Deprecated public String getChannel(); method public static Class<? extends android.app.Notification.Style> getNotificationStyleClass(String); diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp index 68082c2dc4d2..42132ee0daae 100644 --- a/cmds/statsd/src/main.cpp +++ b/cmds/statsd/src/main.cpp @@ -78,7 +78,7 @@ int main(int /*argc*/, char** /*argv*/) { ps->giveThreadPoolName(); IPCThreadState::self()->disableBackgroundScheduling(true); - ::android::hardware::configureRpcThreadpool(1 /*threads*/, false /*willJoin*/); + ::android::hardware::configureRpcThreadpool(4 /*threads*/, false /*willJoin*/); std::shared_ptr<LogEventQueue> eventQueue = std::make_shared<LogEventQueue>(2000 /*buffer limit. Buffer is NOT pre-allocated*/); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 0478ac89fbbb..d74399c54bda 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -127,6 +127,7 @@ public class ApplicationPackageManager extends PackageManager { * @hide */ public static final boolean DEBUG_TRACE_GRANTS = false; + public static final boolean DEBUG_TRACE_PERMISSION_UPDATES = false; private static final int DEFAULT_EPHEMERAL_COOKIE_MAX_SIZE_BYTES = 16384; // 16KB @@ -690,7 +691,7 @@ public class ApplicationPackageManager extends PackageManager { UserHandle user) { if (DEBUG_TRACE_GRANTS && shouldTraceGrant(packageName, permissionName, user.getIdentifier())) { - Log.i(TAG, "App " + mContext.getPackageName() + " is granting " + Log.i(TAG, "App " + mContext.getPackageName() + " is granting " + packageName + " " + permissionName + " for user " + user.getIdentifier(), new RuntimeException()); } try { @@ -708,9 +709,9 @@ public class ApplicationPackageManager extends PackageManager { @Override public void revokeRuntimePermission(String packageName, String permName, UserHandle user) { - if (DEBUG_TRACE_GRANTS + if (DEBUG_TRACE_PERMISSION_UPDATES && shouldTraceGrant(packageName, permName, user.getIdentifier())) { - Log.i(TAG, "App " + mContext.getPackageName() + " is revoking " + Log.i(TAG, "App " + mContext.getPackageName() + " is revoking " + packageName + " " + permName + " for user " + user.getIdentifier(), new RuntimeException()); } try { @@ -734,9 +735,10 @@ public class ApplicationPackageManager extends PackageManager { @Override public void updatePermissionFlags(String permName, String packageName, int flagMask, int flagValues, UserHandle user) { - if (DEBUG_TRACE_GRANTS + if (DEBUG_TRACE_PERMISSION_UPDATES && shouldTraceGrant(packageName, permName, user.getIdentifier())) { Log.i(TAG, "App " + mContext.getPackageName() + " is updating flags for " + + packageName + " " + permName + " for user " + user.getIdentifier() + ": " + DebugUtils.flagsToString(PackageManager.class, "FLAG_PERMISSION_", flagMask) + " := " + DebugUtils.flagsToString( diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index 667758755c99..9b667a118ebc 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -32,7 +32,6 @@ import android.content.pm.ResolveInfo; import android.hardware.biometrics.BiometricPrompt; import android.os.Binder; import android.os.Build; -import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; @@ -508,13 +507,6 @@ public class KeyguardManager { } } - /** @removed */ - @Deprecated - public void dismissKeyguard(@NonNull Activity activity, - @Nullable KeyguardDismissCallback callback, @Nullable Handler handler) { - requestDismissKeyguard(activity, callback); - } - /** * If the device is currently locked (see {@link #isKeyguardLocked()}, requests the Keyguard to * be dismissed. diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index b7676b611fe3..4b9daf1c9486 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -775,8 +775,9 @@ public final class DeviceConfig { * * @param namespace The namespace these properties belong to. * @param keyValueMap A map between property names and property values. + * @hide */ - Properties(@NonNull String namespace, @Nullable Map<String, String> keyValueMap) { + public Properties(@NonNull String namespace, @Nullable Map<String, String> keyValueMap) { Preconditions.checkNotNull(namespace); mNamespace = namespace; mMap = new HashMap(); diff --git a/core/java/android/provider/TEST_MAPPING b/core/java/android/provider/TEST_MAPPING index 52a6a4585581..773be80be270 100644 --- a/core/java/android/provider/TEST_MAPPING +++ b/core/java/android/provider/TEST_MAPPING @@ -7,6 +7,14 @@ "include-annotation": "android.platform.test.annotations.Presubmit" } ] + }, + { + "name": "SettingsProviderTest", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + } + ] } ] } diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java index 68efa6779f04..ce1da4a6ad7d 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/BaseSettingsProviderTest.java @@ -31,6 +31,8 @@ import androidx.test.runner.AndroidJUnit4; import libcore.io.Streams; +import org.junit.After; +import org.junit.Before; import org.junit.runner.RunWith; import java.io.FileInputStream; @@ -60,6 +62,20 @@ abstract class BaseSettingsProviderTest { private int mSecondaryUserId = Integer.MIN_VALUE; + @Before + public void setUp() { + Settings.Global.clearProviderForTest(); + Settings.Secure.clearProviderForTest(); + Settings.System.clearProviderForTest(); + } + + @After + public void tearDown() { + Settings.Global.clearProviderForTest(); + Settings.Secure.clearProviderForTest(); + Settings.System.clearProviderForTest(); + } + protected void setStringViaFrontEndApiSetting(int type, String name, String value, int userId) { ContentResolver contentResolver = getContext().getContentResolver(); diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java index 863b0352913a..ff11f70b8ed1 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/InstallNonMarketAppsDeprecationTest.java @@ -84,8 +84,10 @@ public class InstallNonMarketAppsDeprecationTest extends BaseSettingsProviderTes return line.trim(); } + @Override @Before public void setUp() { + super.setUp(); mUm = (UserManager) getContext().getSystemService(Context.USER_SERVICE); mHasUserRestriction = mUm.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES); mSystemSetUserRestriction = mUm.getUserRestrictionSource( @@ -145,8 +147,10 @@ public class InstallNonMarketAppsDeprecationTest extends BaseSettingsProviderTes assertTrue("Invalid value", value.equals("1") || value.equals("0")); } + @Override @After public void tearDown() { + super.tearDown(); if (!mHasUserRestriction || mSystemSetUserRestriction) { // The test may have modified the user restriction state. Restore it. mUm.setUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java index cf8e1a5c2b14..12ca92f02e7a 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java @@ -64,8 +64,10 @@ public class SettingsBackupAgentTest extends BaseSettingsProviderTest { private TestFriendlySettingsBackupAgent mAgentUnderTest; private Context mContext; + @Override @Before public void setUp() { + super.setUp(); mContext = new ContextWithMockContentResolver(getContext()); mAgentUnderTest = new TestFriendlySettingsBackupAgent(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 1714cd5163e7..aa1d8b4d5287 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -18,6 +18,8 @@ package com.android.keyguard; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.content.Intent.ACTION_USER_REMOVED; +import static android.content.Intent.ACTION_USER_STOPPED; import static android.content.Intent.ACTION_USER_UNLOCKED; import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN; import static android.os.BatteryManager.BATTERY_STATUS_FULL; @@ -162,6 +164,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private static final int MSG_DEVICE_POLICY_MANAGER_STATE_CHANGED = 337; private static final int MSG_TELEPHONY_CAPABLE = 338; private static final int MSG_TIMEZONE_UPDATE = 339; + private static final int MSG_USER_STOPPED = 340; + private static final int MSG_USER_REMOVED = 341; /** Biometric authentication state: Not listening. */ private static final int BIOMETRIC_STATE_STOPPED = 0; @@ -375,7 +379,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { handleDreamingStateChanged(msg.arg1); break; case MSG_USER_UNLOCKED: - handleUserUnlocked(); + handleUserUnlocked(msg.arg1); + break; + case MSG_USER_STOPPED: + handleUserStopped(msg.arg1); + break; + case MSG_USER_REMOVED: + handleUserRemoved(msg.arg1); break; case MSG_ASSISTANT_STACK_CHANGED: setAssistantVisible((boolean) msg.obj); @@ -427,6 +437,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } }; + private SparseBooleanArray mUserIsUnlocked = new SparseBooleanArray(); private SparseBooleanArray mUserHasTrust = new SparseBooleanArray(); private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray(); private SparseBooleanArray mUserFingerprintAuthenticated = new SparseBooleanArray(); @@ -1207,7 +1218,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { .equals(action)) { mHandler.sendEmptyMessage(MSG_DPM_STATE_CHANGED); } else if (ACTION_USER_UNLOCKED.equals(action)) { - mHandler.sendEmptyMessage(MSG_USER_UNLOCKED); + mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_UNLOCKED, + getSendingUserId(), 0)); + } else if (ACTION_USER_STOPPED.equals(action)) { + mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_STOPPED, + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1), 0)); + } else if (ACTION_USER_REMOVED.equals(action)) { + mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_REMOVED, + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1), 0)); } } }; @@ -1558,8 +1576,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } } - private void handleUserUnlocked() { + private void handleUserUnlocked(int userId) { checkIsHandlerThread(); + mUserIsUnlocked.put(userId, true); mNeedsSlowUnlockTransition = resolveNeedsSlowUnlockTransition(); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); @@ -1569,6 +1588,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } } + private void handleUserStopped(int userId) { + checkIsHandlerThread(); + mUserIsUnlocked.put(userId, mUserManager.isUserUnlocked(userId)); + } + + private void handleUserRemoved(int userId) { + checkIsHandlerThread(); + mUserIsUnlocked.delete(userId); + } + @VisibleForTesting protected KeyguardUpdateMonitor(Context context) { mContext = context; @@ -1612,6 +1641,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { allUserFilter.addAction(ACTION_FACE_UNLOCK_STOPPED); allUserFilter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); allUserFilter.addAction(ACTION_USER_UNLOCKED); + allUserFilter.addAction(ACTION_USER_STOPPED); + allUserFilter.addAction(ACTION_USER_REMOVED); context.registerReceiverAsUser(mBroadcastAllReceiver, UserHandle.ALL, allUserFilter, null, mHandler); @@ -1666,6 +1697,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); mUserManager = context.getSystemService(UserManager.class); mIsPrimaryUser = mUserManager.isPrimaryUser(); + int user = ActivityManager.getCurrentUser(); + mUserIsUnlocked.put(user, mUserManager.isUserUnlocked(user)); mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class); mLogoutEnabled = mDevicePolicyManager.isLogoutEnabled(); updateAirplaneModeState(); @@ -1709,6 +1742,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } /** + * If a user is encrypted or not. + * This is NOT related to the lock screen being visible or not. + * + * @param userId The user. + * @return {@code true} when encrypted. + * @see UserManager#isUserUnlocked() + * @see Intent#ACTION_USER_UNLOCKED + */ + public boolean isUserUnlocked(int userId) { + return mUserIsUnlocked.get(userId); + } + + /** * Called whenever passive authentication is requested or aborted by a sensor. * * @param active If the interrupt started or ended. @@ -2297,7 +2343,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } private boolean resolveNeedsSlowUnlockTransition() { - if (mUserManager.isUserUnlocked(getCurrentUser())) { + if (isUserUnlocked(getCurrentUser())) { return false; } Intent homeIntent = new Intent(Intent.ACTION_MAIN) diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index 41203348d7a0..efac147fc6b9 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -35,6 +35,7 @@ import com.android.systemui.plugins.FalsingPlugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.util.AsyncSensorManager; +import com.android.systemui.util.DeviceConfigProxy; import java.io.PrintWriter; @@ -51,18 +52,23 @@ import javax.inject.Singleton; public class FalsingManagerProxy implements FalsingManager { private FalsingManager mInternalFalsingManager; - private final Handler mMainHandler; + private DeviceConfig.OnPropertiesChangedListener mDeviceConfigListener; + private final DeviceConfigProxy mDeviceConfig; private boolean mBrightlineEnabled; @Inject FalsingManagerProxy(Context context, PluginManager pluginManager, - @Named(MAIN_HANDLER_NAME) Handler handler) { - mMainHandler = handler; + @Named(MAIN_HANDLER_NAME) Handler handler, DeviceConfigProxy deviceConfig) { + mDeviceConfig = deviceConfig; + mDeviceConfigListener = + properties -> onDeviceConfigPropertiesChanged(context, properties.getNamespace()); setupFalsingManager(context); - DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, - command -> mMainHandler.post(command), - properties -> onDeviceConfigPropertiesChanged(context, properties.getNamespace()) + mDeviceConfig.addOnPropertiesChangedListener( + DeviceConfig.NAMESPACE_SYSTEMUI, + handler::post, + mDeviceConfigListener ); + final PluginListener<FalsingPlugin> mPluginListener = new PluginListener<FalsingPlugin>() { public void onPluginConnected(FalsingPlugin plugin, Context context) { FalsingManager pluginFalsingManager = plugin.getFalsingManager(context); @@ -91,9 +97,8 @@ public class FalsingManagerProxy implements FalsingManager { /** * Chooses the FalsingManager implementation. */ - @VisibleForTesting - public void setupFalsingManager(Context context) { - boolean brightlineEnabled = DeviceConfig.getBoolean( + private void setupFalsingManager(Context context) { + boolean brightlineEnabled = mDeviceConfig.getBoolean( DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_MANAGER_ENABLED, true); if (brightlineEnabled == mBrightlineEnabled && mInternalFalsingManager != null) { return; @@ -109,10 +114,10 @@ public class FalsingManagerProxy implements FalsingManager { mInternalFalsingManager = new BrightLineFalsingManager( new FalsingDataProvider(context.getResources().getDisplayMetrics()), Dependency.get(AsyncSensorManager.class), - KeyguardUpdateMonitor.getInstance(context) + KeyguardUpdateMonitor.getInstance(context), + mDeviceConfig ); } - } /** @@ -305,6 +310,7 @@ public class FalsingManagerProxy implements FalsingManager { @Override public void cleanup() { + mDeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigListener); mInternalFalsingManager.cleanup(); } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java index 9e0b7025ddf8..9e646b627bd2 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java @@ -33,6 +33,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.classifier.Classifier; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.util.DeviceConfigProxy; import java.io.PrintWriter; import java.util.ArrayList; @@ -88,7 +89,8 @@ public class BrightLineFalsingManager implements FalsingManager { public BrightLineFalsingManager( FalsingDataProvider falsingDataProvider, SensorManager sensorManager, - KeyguardUpdateMonitor keyguardUpdateMonitor) { + KeyguardUpdateMonitor keyguardUpdateMonitor, + DeviceConfigProxy deviceConfigProxy) { mKeyguardUpdateMonitor = keyguardUpdateMonitor; mDataProvider = falsingDataProvider; mSensorManager = sensorManager; @@ -96,15 +98,16 @@ public class BrightLineFalsingManager implements FalsingManager { mMetricsLogger = new MetricsLogger(); mClassifiers = new ArrayList<>(); - DistanceClassifier distanceClassifier = new DistanceClassifier(mDataProvider); - ProximityClassifier proximityClassifier = new ProximityClassifier(distanceClassifier, - mDataProvider); + DistanceClassifier distanceClassifier = + new DistanceClassifier(mDataProvider, deviceConfigProxy); + ProximityClassifier proximityClassifier = + new ProximityClassifier(distanceClassifier, mDataProvider, deviceConfigProxy); mClassifiers.add(new PointerCountClassifier(mDataProvider)); mClassifiers.add(new TypeClassifier(mDataProvider)); - mClassifiers.add(new DiagonalClassifier(mDataProvider)); + mClassifiers.add(new DiagonalClassifier(mDataProvider, deviceConfigProxy)); mClassifiers.add(distanceClassifier); mClassifiers.add(proximityClassifier); - mClassifiers.add(new ZigZagClassifier(mDataProvider)); + mClassifiers.add(new ZigZagClassifier(mDataProvider, deviceConfigProxy)); } private void registerSensors() { diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java index cc6645415fd8..9c03b915000e 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java @@ -23,6 +23,8 @@ import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; import android.provider.DeviceConfig; +import com.android.systemui.util.DeviceConfigProxy; + /** * False on swipes that are too close to 45 degrees. * @@ -42,14 +44,14 @@ class DiagonalClassifier extends FalsingClassifier { private final float mHorizontalAngleRange; private final float mVerticalAngleRange; - DiagonalClassifier(FalsingDataProvider dataProvider) { + DiagonalClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) { super(dataProvider); - mHorizontalAngleRange = DeviceConfig.getFloat( + mHorizontalAngleRange = deviceConfigProxy.getFloat( DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_DIAGONAL_HORIZONTAL_ANGLE_RANGE, HORIZONTAL_ANGLE_RANGE); - mVerticalAngleRange = DeviceConfig.getFloat( + mVerticalAngleRange = deviceConfigProxy.getFloat( DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE, VERTICAL_ANGLE_RANGE); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java index a6a617dc51de..0954f9e6e672 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java @@ -27,6 +27,8 @@ import android.provider.DeviceConfig; import android.view.MotionEvent; import android.view.VelocityTracker; +import com.android.systemui.util.DeviceConfigProxy; + import java.util.List; /** @@ -50,35 +52,35 @@ class DistanceClassifier extends FalsingClassifier { private boolean mDistanceDirty; private DistanceVectors mCachedDistance; - DistanceClassifier(FalsingDataProvider dataProvider) { + DistanceClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) { super(dataProvider); - mVelocityToDistanceMultiplier = DeviceConfig.getFloat( + mVelocityToDistanceMultiplier = deviceConfigProxy.getFloat( DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_DISTANCE_VELOCITY_TO_DISTANCE, VELOCITY_TO_DISTANCE); - float horizontalFlingThresholdIn = DeviceConfig.getFloat( + float horizontalFlingThresholdIn = deviceConfigProxy.getFloat( DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_FLING_THRESHOLD_IN, HORIZONTAL_FLING_THRESHOLD_DISTANCE_IN); - float verticalFlingThresholdIn = DeviceConfig.getFloat( + float verticalFlingThresholdIn = deviceConfigProxy.getFloat( DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_DISTANCE_VERTICAL_FLING_THRESHOLD_IN, VERTICAL_FLING_THRESHOLD_DISTANCE_IN); - float horizontalSwipeThresholdIn = DeviceConfig.getFloat( + float horizontalSwipeThresholdIn = deviceConfigProxy.getFloat( DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_DISTANCE_HORIZONTAL_SWIPE_THRESHOLD_IN, HORIZONTAL_SWIPE_THRESHOLD_DISTANCE_IN); - float verticalSwipeThresholdIn = DeviceConfig.getFloat( + float verticalSwipeThresholdIn = deviceConfigProxy.getFloat( DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_DISTANCE_VERTICAL_SWIPE_THRESHOLD_IN, VERTICAL_SWIPE_THRESHOLD_DISTANCE_IN); - float screenFractionMaxDistance = DeviceConfig.getFloat( + float screenFractionMaxDistance = deviceConfigProxy.getFloat( DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_DISTANCE_SCREEN_FRACTION_MAX_DISTANCE, SCREEN_FRACTION_MAX_DISTANCE); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java index 2644bf9f26ce..182704726129 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java @@ -24,6 +24,8 @@ import android.hardware.SensorEvent; import android.provider.DeviceConfig; import android.view.MotionEvent; +import com.android.systemui.util.DeviceConfigProxy; + /** * False touch if proximity sensor is covered for more than a certain percentage of the gesture. @@ -44,11 +46,11 @@ class ProximityClassifier extends FalsingClassifier { private float mPercentNear; ProximityClassifier(DistanceClassifier distanceClassifier, - FalsingDataProvider dataProvider) { + FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) { super(dataProvider); this.mDistanceClassifier = distanceClassifier; - mPercentCoveredThreshold = DeviceConfig.getFloat( + mPercentCoveredThreshold = deviceConfigProxy.getFloat( DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD, PERCENT_COVERED_THRESHOLD); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java index 82ae30ac4bdf..a0da988b27b5 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java @@ -25,6 +25,8 @@ import android.graphics.Point; import android.provider.DeviceConfig; import android.view.MotionEvent; +import com.android.systemui.util.DeviceConfigProxy; + import java.util.ArrayList; import java.util.List; @@ -48,25 +50,25 @@ class ZigZagClassifier extends FalsingClassifier { private final float mMaxXSecondaryDeviance; private final float mMaxYSecondaryDeviance; - ZigZagClassifier(FalsingDataProvider dataProvider) { + ZigZagClassifier(FalsingDataProvider dataProvider, DeviceConfigProxy deviceConfigProxy) { super(dataProvider); - mMaxXPrimaryDeviance = DeviceConfig.getFloat( + mMaxXPrimaryDeviance = deviceConfigProxy.getFloat( DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_ZIGZAG_X_PRIMARY_DEVIANCE, MAX_X_PRIMARY_DEVIANCE); - mMaxYPrimaryDeviance = DeviceConfig.getFloat( + mMaxYPrimaryDeviance = deviceConfigProxy.getFloat( DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE, MAX_Y_PRIMARY_DEVIANCE); - mMaxXSecondaryDeviance = DeviceConfig.getFloat( + mMaxXSecondaryDeviance = deviceConfigProxy.getFloat( DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_ZIGZAG_X_SECONDARY_DEVIANCE, MAX_X_SECONDARY_DEVIANCE); - mMaxYSecondaryDeviance = DeviceConfig.getFloat( + mMaxYSecondaryDeviance = deviceConfigProxy.getFloat( DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE, MAX_Y_SECONDARY_DEVIANCE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 9ac7ae98ecce..9178fda2b549 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -384,8 +384,7 @@ public class KeyguardIndicationController implements StateListener, int userId = KeyguardUpdateMonitor.getCurrentUser(); String trustGrantedIndication = getTrustGrantedIndication(); String trustManagedIndication = getTrustManagedIndication(); - // TODO(b/140053632) - if (!whitelistIpcs(() -> mUserManager.isUserUnlocked(userId))) { + if (!mKeyguardUpdateMonitor.isUserUnlocked(userId)) { mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked); mTextView.setTextColor(mInitialTextColorState); } else if (!TextUtils.isEmpty(mTransientIndication)) { @@ -757,6 +756,13 @@ public class KeyguardIndicationController implements StateListener, } @Override + public void onUserSwitchComplete(int userId) { + if (mVisible) { + updateIndication(false); + } + } + + @Override public void onUserUnlocked() { if (mVisible) { updateIndication(false); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 23b48141d0cc..ab9162ace817 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.phone; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; -import static com.android.systemui.DejankUtils.whitelistIpcs; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_BUTTON; import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK; @@ -117,6 +116,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private static final int DOZE_ANIMATION_STAGGER_DELAY = 48; private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250; + private final UnlockMethodCache mUnlockMethodCache; private KeyguardAffordanceView mRightAffordanceView; private KeyguardAffordanceView mLeftAffordanceView; private ViewGroup mIndicationArea; @@ -129,7 +129,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL private View mCameraPreview; private ActivityStarter mActivityStarter; - private UnlockMethodCache mUnlockMethodCache; private LockPatternUtils mLockPatternUtils; private FlashlightController mFlashlightController; private PreviewInflater mPreviewInflater; @@ -185,6 +184,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL public KeyguardBottomAreaView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + mUnlockMethodCache = UnlockMethodCache.getInstance(getContext()); } private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() { @@ -242,8 +242,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL mBurnInYOffset = getResources().getDimensionPixelSize( R.dimen.default_burn_in_prevention_offset); updateCameraVisibility(); - mUnlockMethodCache = UnlockMethodCache.getInstance(getContext()); - mUnlockMethodCache.addListener(this); setClipChildren(false); setClipToPadding(false); inflateCameraPreview(); @@ -281,11 +279,13 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL getContext().registerReceiverAsUser(mDevicePolicyReceiver, UserHandle.ALL, filter, null, null); KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback); + mUnlockMethodCache.addListener(this); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); + mUnlockMethodCache.removeListener(this); mAccessibilityController.removeStateChangedCallback(this); mRightExtension.destroy(); mLeftExtension.destroy(); @@ -365,11 +365,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL * Resolves the intent to launch the camera application. */ public ResolveInfo resolveCameraIntent() { - // TODO(b/140057230) - return whitelistIpcs(() -> - mContext.getPackageManager().resolveActivityAsUser(getCameraIntent(), - PackageManager.MATCH_DEFAULT_ONLY, - KeyguardUpdateMonitor.getCurrentUser())); + return mContext.getPackageManager().resolveActivityAsUser(getCameraIntent(), + PackageManager.MATCH_DEFAULT_ONLY, + KeyguardUpdateMonitor.getCurrentUser()); } private void updateCameraVisibility() { @@ -806,11 +804,11 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL @Override public IconState getIcon() { - ResolveInfo resolved = resolveCameraIntent(); boolean isCameraDisabled = (mStatusBar != null) && !mStatusBar.isCameraAllowedByAdmin(); - mIconState.isVisible = !isCameraDisabled && resolved != null + mIconState.isVisible = !isCameraDisabled && getResources().getBoolean(R.bool.config_keyguardShowCameraAffordance) - && mUserSetupComplete; + && mUserSetupComplete + && resolveCameraIntent() != null; mIconState.drawable = mContext.getDrawable(R.drawable.ic_camera_alt_24dp); mIconState.contentDescription = mContext.getString(R.string.accessibility_camera_button); @@ -822,9 +820,7 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); boolean canSkipBouncer = updateMonitor.getUserCanSkipBouncer( KeyguardUpdateMonitor.getCurrentUser()); - // TODO(b/140057230) - boolean secure = whitelistIpcs(() -> - mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())); + boolean secure = mUnlockMethodCache.isMethodSecure(); return (secure && !canSkipBouncer) ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT; } } diff --git a/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java b/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java new file mode 100644 index 000000000000..3a2172ae0fae --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util; + +import android.annotation.CallbackExecutor; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.provider.DeviceConfig; +import android.provider.Settings; + +import java.util.concurrent.Executor; + +import javax.inject.Inject; + +/** + * Wrapper around DeviceConfig useful for testing. + */ +public class DeviceConfigProxy { + @Inject + public DeviceConfigProxy() { + } + + /** + * Wrapped version of {@link DeviceConfig#addOnPropertiesChangedListener}. + */ + public void addOnPropertiesChangedListener( + @NonNull String namespace, + @NonNull @CallbackExecutor Executor executor, + @NonNull DeviceConfig.OnPropertiesChangedListener onPropertiesChangedListener) { + DeviceConfig.addOnPropertiesChangedListener( + namespace, executor, onPropertiesChangedListener); + } + + /** + * Wrapped version of {@link DeviceConfig#enforceReadPermission}. + */ + public void enforceReadPermission(Context context, String namespace) { + DeviceConfig.enforceReadPermission(context, namespace); + } + + /** + * Wrapped version of {@link DeviceConfig#getBoolean}. + */ + public boolean getBoolean( + @NonNull String namespace, @NonNull String name, boolean defaultValue) { + return DeviceConfig.getBoolean(namespace, name, defaultValue); + } + + /** + * Wrapped version of {@link DeviceConfig#getFloat}. + */ + public float getFloat( + @NonNull String namespace, @NonNull String name, float defaultValue) { + return DeviceConfig.getFloat(namespace, name, defaultValue); + } + + /** + * Wrapped version of {@link DeviceConfig#getInt}. + */ + public int getInt(@NonNull String namespace, @NonNull String name, int defaultValue) { + return DeviceConfig.getInt(namespace, name, defaultValue); + } + + /** + * Wrapped version of {@link DeviceConfig#getLong}. + */ + public long getLong(@NonNull String namespace, @NonNull String name, long defaultValue) { + return DeviceConfig.getLong(namespace, name, defaultValue); + + } + + /** + * Wrapped version of {@link DeviceConfig#getProperty}. + */ + public String getProperty(@NonNull String namespace, @NonNull String name) { + return DeviceConfig.getProperty(namespace, name); + } + + /** + * Wrapped version of {@link DeviceConfig#getString}. + */ + public String getString( + @NonNull String namespace, @NonNull String name, @Nullable String defaultValue) { + return DeviceConfig.getString(namespace, name, defaultValue); + } + + /** + * Wrapped version of {@link DeviceConfig#removeOnPropertiesChangedListener}. + * + * Like {@link #addOnPropertiesChangedListener}, this operates on a callback type that + * wraps the original callback type provided by {@link DeviceConfig}. + */ + public void removeOnPropertiesChangedListener( + @NonNull DeviceConfig.OnPropertiesChangedListener onPropertiesChangedListener) { + DeviceConfig.removeOnPropertiesChangedListener(onPropertiesChangedListener); + } + + /** + * Wrapped version of {@link DeviceConfig#resetToDefaults}. + */ + public void resetToDefaults(@Settings.ResetMode int resetMode, + @Nullable String namespace) { + DeviceConfig.resetToDefaults(resetMode, namespace); + } + + /** + * Wrapped version of {@link DeviceConfig#setProperty}. + */ + public boolean setProperty( + @NonNull String namespace, + @NonNull String name, + @Nullable String value, + boolean makeDefault) { + return DeviceConfig.setProperty(namespace, name, value, makeDefault); + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index a3cb6c05ca7a..fce79c373c36 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -477,6 +477,16 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { assertThat(listToVerify.get(0)).isEqualTo(TEST_SUBSCRIPTION_2); } + @Test + public void testIsUserUnlocked() { + // mUserManager will report the user as unlocked on @Before + assertThat(mKeyguardUpdateMonitor.isUserUnlocked(KeyguardUpdateMonitor.getCurrentUser())) + .isTrue(); + // Invalid user should not be unlocked. + int randomUser = 99; + assertThat(mKeyguardUpdateMonitor.isUserUnlocked(randomUser)).isFalse(); + } + private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) { int subscription = simInited ? 1/* mock subid=1 */ : SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java index 7ea6493da83d..2073ae17784c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingManagerProxyTest.java @@ -31,6 +31,8 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.brightline.BrightLineFalsingManager; import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.util.DeviceConfigProxy; +import com.android.systemui.util.DeviceConfigProxyFake; import org.junit.After; import org.junit.Before; @@ -45,8 +47,9 @@ import org.mockito.MockitoAnnotations; public class FalsingManagerProxyTest extends SysuiTestCase { @Mock PluginManager mPluginManager; - private boolean mDefaultConfigValue; private Handler mHandler; + private FalsingManagerProxy mProxy; + private DeviceConfigProxy mDeviceConfig; private TestableLooper mTestableLooper; @Before @@ -54,50 +57,47 @@ public class FalsingManagerProxyTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mTestableLooper = TestableLooper.get(this); mHandler = new Handler(mTestableLooper.getLooper()); - mDefaultConfigValue = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, - BRIGHTLINE_FALSING_MANAGER_ENABLED, false); - // In case it runs on a device where it's been set to true, set it to false by hand. - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + mDeviceConfig = new DeviceConfigProxyFake(); + mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false); } @After public void tearDown() { - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, - BRIGHTLINE_FALSING_MANAGER_ENABLED, mDefaultConfigValue ? "true" : "false", false); + if (mProxy != null) { + mProxy.cleanup(); + } } @Test public void test_brightLineFalsingManagerDisabled() { - FalsingManagerProxy proxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler); - - assertThat(proxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class)); + mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mDeviceConfig); + assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class)); } @Test - public void test_brightLineFalsingManagerEnabled() { - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + public void test_brightLineFalsingManagerEnabled() throws InterruptedException { + mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false); - FalsingManagerProxy proxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler); - - assertThat(proxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class)); + mTestableLooper.processAllMessages(); + mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mDeviceConfig); + assertThat(mProxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class)); } @Test - public void test_brightLineFalsingManagerToggled() { - FalsingManagerProxy proxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler); - assertThat(proxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class)); + public void test_brightLineFalsingManagerToggled() throws InterruptedException { + mProxy = new FalsingManagerProxy(getContext(), mPluginManager, mHandler, mDeviceConfig); + assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class)); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_MANAGER_ENABLED, "true", false); mTestableLooper.processAllMessages(); - proxy.setupFalsingManager(getContext()); - assertThat(proxy.getInternalFalsingManager(), instanceOf(BrightLineFalsingManager.class)); + assertThat(mProxy.getInternalFalsingManager(), + instanceOf(BrightLineFalsingManager.class)); - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + mDeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, BRIGHTLINE_FALSING_MANAGER_ENABLED, "false", false); mTestableLooper.processAllMessages(); - proxy.setupFalsingManager(getContext()); - assertThat(proxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class)); + assertThat(mProxy.getInternalFalsingManager(), instanceOf(FalsingManagerImpl.class)); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java index b45d3f2855ee..afe4200a3a43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java @@ -28,6 +28,8 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; +import com.android.systemui.util.DeviceConfigProxyFake; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -58,7 +60,7 @@ public class DiagonalClassifierTest extends ClassifierTest { public void setup() { super.setup(); MockitoAnnotations.initMocks(this); - mClassifier = new DiagonalClassifier(mDataProvider); + mClassifier = new DiagonalClassifier(mDataProvider, new DeviceConfigProxyFake()); } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java index 805bb91591e5..f0f5fc719272 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java @@ -24,6 +24,8 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; +import com.android.systemui.util.DeviceConfigProxyFake; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -41,7 +43,7 @@ public class DistanceClassifierTest extends ClassifierTest { public void setup() { super.setup(); mDataProvider = getDataProvider(); - mClassifier = new DistanceClassifier(mDataProvider); + mClassifier = new DistanceClassifier(mDataProvider, new DeviceConfigProxyFake()); } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java index a6cabbf49458..c76fe74b1af7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java @@ -31,6 +31,8 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; +import com.android.systemui.util.DeviceConfigProxyFake; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -60,7 +62,8 @@ public class ProximityClassifierTest extends ClassifierTest { MockitoAnnotations.initMocks(this); when(mDataProvider.getInteractionType()).thenReturn(GENERIC); when(mDistanceClassifier.isLongSwipe()).thenReturn(false); - mClassifier = new ProximityClassifier(mDistanceClassifier, mDataProvider); + mClassifier = new ProximityClassifier( + mDistanceClassifier, mDataProvider, new DeviceConfigProxyFake()); } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java index fb4c1ec11faa..387c0daad399 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java @@ -24,6 +24,8 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; +import com.android.systemui.util.DeviceConfigProxyFake; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -41,7 +43,7 @@ public class ZigZagClassifierTest extends ClassifierTest { @Before public void setup() { super.setup(); - mClassifier = new ZigZagClassifier(getDataProvider()); + mClassifier = new ZigZagClassifier(getDataProvider(), new DeviceConfigProxyFake()); } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index daee55bd3d61..2427cfc77f39 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -268,12 +268,18 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { public void unlockMethodCache_listenerUpdatesIndication() { createController(); String restingIndication = "Resting indication"; + + mController.setVisible(true); + assertThat(mTextView.getText()).isEqualTo( + mContext.getString(com.android.internal.R.string.lockscreen_storage_locked)); + when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true); + when(mKeyguardUpdateMonitor.isUserUnlocked(anyInt())).thenReturn(true); mController.setRestingIndication(restingIndication); - mController.setVisible(true); assertThat(mTextView.getText()).isEqualTo(mController.getTrustGrantedIndication()); reset(mKeyguardUpdateMonitor); + when(mKeyguardUpdateMonitor.isUserUnlocked(anyInt())).thenReturn(true); when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false); mController.onUnlockMethodStateChanged(); assertThat(mTextView.getText()).isEqualTo(restingIndication); diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java new file mode 100644 index 000000000000..426aba03d5d8 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFake.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util; + +import android.content.Context; +import android.provider.DeviceConfig; +import android.provider.DeviceConfig.OnPropertiesChangedListener; +import android.provider.DeviceConfig.Properties; +import android.util.Pair; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; + +/** + * A Fake of {@link DeviceConfigProxy} useful for testing. + * + * No properties are set by default. No calls to {@link DeviceConfig} are made. Be sure to set any + * properties you rely on ahead of time in your test. + */ +public class DeviceConfigProxyFake extends DeviceConfigProxy { + + private List<Pair<Executor, OnPropertiesChangedListener>> mListeners = new ArrayList<>(); + private Map<String, Map<String, String>> mDefaultProperties = new HashMap<>(); + private Map<String, Map<String, String>> mProperties = new HashMap<>(); + + public DeviceConfigProxyFake() { + } + + @Override + public void addOnPropertiesChangedListener( + String namespace, Executor executor, + OnPropertiesChangedListener onPropertiesChangedListener) { + mListeners.add(Pair.create(executor, onPropertiesChangedListener)); + } + + @Override + public void removeOnPropertiesChangedListener( + OnPropertiesChangedListener onPropertiesChangedListener) { + mListeners.removeIf(listener -> { + if (listener == null) { + return false; + } + return listener.second.equals(onPropertiesChangedListener); + }); + } + + @Override + public boolean setProperty(String namespace, String name, String value, boolean makeDefault) { + setPropertyInternal(namespace, name, value, mProperties); + if (makeDefault) { + setPropertyInternal(namespace, name, value, mDefaultProperties); + } + + for (Pair<Executor, OnPropertiesChangedListener> listener : mListeners) { + listener.first.execute(() -> listener.second.onPropertiesChanged( + new Properties(namespace, mProperties.get(namespace)))); + } + return true; + } + + private void setPropertyInternal(String namespace, String name, String value, + Map<String, Map<String, String>> properties) { + properties.putIfAbsent(namespace, new HashMap<>()); + properties.get(namespace).put(name, value); + } + + @Override + public void enforceReadPermission(Context context, String namespace) { + // no-op + } + + private Properties propsForNamespaceAndName(String namespace, String name) { + if (mProperties.containsKey(namespace) && mProperties.get(namespace).containsKey(name)) { + return new Properties(namespace, mProperties.get(namespace)); + } + if (mDefaultProperties.containsKey(namespace)) { + return new Properties(namespace, mDefaultProperties.get(namespace)); + } + + return null; + } + + @Override + public boolean getBoolean(String namespace, String name, boolean defaultValue) { + Properties props = propsForNamespaceAndName(namespace, name); + if (props != null) { + return props.getBoolean(name, defaultValue); + } + + return defaultValue; + } + + @Override + public int getInt(String namespace, String name, int defaultValue) { + Properties props = propsForNamespaceAndName(namespace, name); + if (props != null) { + return props.getInt(name, defaultValue); + } + + return defaultValue; + } + + @Override + public long getLong(String namespace, String name, long defaultValue) { + Properties props = propsForNamespaceAndName(namespace, name); + if (props != null) { + return props.getLong(name, defaultValue); + } + + return defaultValue; + } + + @Override + public String getProperty(String namespace, String name) { + return getString(namespace, name, null); + } + + @Override + public String getString(String namespace, String name, String defaultValue) { + Properties props = propsForNamespaceAndName(namespace, name); + if (props != null) { + return props.getString(name, defaultValue); + } + + return defaultValue; + } + + @Override + public void resetToDefaults(int resetMode, String namespace) { + if (mProperties.containsKey(namespace)) { + mProperties.get(namespace).clear(); + } + } +} diff --git a/services/Android.bp b/services/Android.bp index 27f8d36894da..6953e862f68b 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -63,6 +63,5 @@ cc_library_shared { platform_compat_config { name: "services-platform-compat-config", - prefix: "services", src: ":services", } diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index d8b7e3a25e04..a338b901d24c 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -66,12 +66,6 @@ public class TouchExplorer extends BaseEventStreamTransformation // Tag for logging received events. private static final String LOG_TAG = "TouchExplorer"; - // States this explorer can be in. - private static final int STATE_TOUCH_EXPLORING = 0x00000001; - private static final int STATE_DRAGGING = 0x00000002; - private static final int STATE_DELEGATING = 0x00000004; - private static final int STATE_GESTURE_DETECTING = 0x00000005; - // The maximum of the cosine between the vectors of two moving // pointers so they can be considered moving in the same direction. private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4) @@ -248,7 +242,13 @@ public class TouchExplorer extends BaseEventStreamTransformation return; } - if (mState.isTouchExploring()) { + // TODO: extract the below functions into separate handlers for each state. + // Right now the number of functions and number of states make the code messy. + if (mState.isClear()) { + handleMotionEventStateClear(event, rawEvent, policyFlags); + } else if (mState.isTouchInteracting()) { + handleMotionEventStateTouchInteracting(event, rawEvent, policyFlags); + } else if (mState.isTouchExploring()) { handleMotionEventStateTouchExploring(event, rawEvent, policyFlags); } else if (mState.isDragging()) { handleMotionEventStateDragging(event, policyFlags); @@ -286,8 +286,8 @@ public class TouchExplorer extends BaseEventStreamTransformation @Override public void onDoubleTapAndHold(MotionEvent event, int policyFlags) { - // Ignore the event if we aren't touch exploring. - if (!mState.isTouchExploring()) { + // Ignore the event if we aren't touch interacting. + if (!mState.isTouchInteracting()) { return; } @@ -304,8 +304,7 @@ public class TouchExplorer extends BaseEventStreamTransformation @Override public boolean onDoubleTap(MotionEvent event, int policyFlags) { - // Ignore the event if we aren't touch exploring. - if (!mState.isTouchExploring()) { + if (!mState.isTouchInteracting()) { return false; } @@ -380,35 +379,26 @@ public class TouchExplorer extends BaseEventStreamTransformation } /** - * Handles a motion event in touch exploring state. - * - * @param event The event to be handled. - * @param rawEvent The raw (unmodified) motion event. - * @param policyFlags The policy flags associated with the event. + * Handles a motion event in the clear state i.e. no fingers are touching the screen. */ - private void handleMotionEventStateTouchExploring( + private void handleMotionEventStateClear( MotionEvent event, MotionEvent rawEvent, int policyFlags) { switch (event.getActionMasked()) { + // The only way to leave the clear state is for a pointer to go down. case MotionEvent.ACTION_DOWN: - handleActionDownStateTouchExploring(event, policyFlags); - break; - case MotionEvent.ACTION_POINTER_DOWN: - handleActionPointerDownStateTouchExploring(); - break; - case MotionEvent.ACTION_MOVE: - handleActionMoveStateTouchExploring(event, rawEvent, policyFlags); + handleActionDown(event, policyFlags); break; - case MotionEvent.ACTION_UP: - handleActionUpStateTouchExploring(event, policyFlags); + default: + // Some other nonsensical event. break; } } /** - * Handles ACTION_DOWN while in the default touch exploring state. This event represents the + * Handles ACTION_DOWN while in the clear or touch interacting states. This event represents the * first finger touching the screen. */ - private void handleActionDownStateTouchExploring(MotionEvent event, int policyFlags) { + private void handleActionDown(MotionEvent event, int policyFlags) { mAms.onTouchInteractionStart(); // If we still have not notified the user for the last @@ -418,13 +408,13 @@ public class TouchExplorer extends BaseEventStreamTransformation mSendHoverExitDelayed.cancel(); // If a touch exploration gesture is in progress send events for its end. - if (mState.isTouchExplorationInProgress()) { + if (mState.isTouchExploring()) { sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); } // Avoid duplicated TYPE_TOUCH_INTERACTION_START event when 2nd tap of double // tap. - if (!mGestureDetector.firstTapDetected()) { + if (!mGestureDetector.firstTapDetected() && mState.isClear()) { mSendTouchExplorationEndDelayed.forceSendAndRemove(); mSendTouchInteractionEndDelayed.forceSendAndRemove(); sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START); @@ -433,13 +423,15 @@ public class TouchExplorer extends BaseEventStreamTransformation mSendTouchInteractionEndDelayed.cancel(); } - if (!mGestureDetector.firstTapDetected() && !mState.isTouchExplorationInProgress()) { + if (!mGestureDetector.firstTapDetected() && !mState.isTouchExploring()) { if (!mSendHoverEnterAndMoveDelayed.isPending()) { - // Deliver hover enter with a delay to have a chance - // to detect what the user is trying to do. + // Queue a delayed transition to STATE_TOUCH_EXPLORING. + // If we do not detect that this is a gesture, delegation or drag the transition + // will fire by default. + // The idea is to avoid getting stuck in STATE_TOUCH_INTERACTING final int pointerId = mReceivedPointerTracker.getPrimaryPointerId(); final int pointerIdBits = (1 << pointerId); - mSendHoverEnterAndMoveDelayed.post(event, true, pointerIdBits, policyFlags); + mSendHoverEnterAndMoveDelayed.post(event, pointerIdBits, policyFlags); } else { // Cache the event until we discern exploration from gesturing. mSendHoverEnterAndMoveDelayed.addEvent(event); @@ -448,10 +440,64 @@ public class TouchExplorer extends BaseEventStreamTransformation } /** + * Handles a motion event in touch interacting state. + * + * @param event The event to be handled. + * @param rawEvent The raw (unmodified) motion event. + * @param policyFlags The policy flags associated with the event. + */ + private void handleMotionEventStateTouchInteracting( + MotionEvent event, MotionEvent rawEvent, int policyFlags) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + // Continue the previous interaction. + mSendTouchInteractionEndDelayed.cancel(); + handleActionDown(event, policyFlags); + break; + case MotionEvent.ACTION_POINTER_DOWN: + handleActionPointerDown(); + break; + case MotionEvent.ACTION_MOVE: + handleActionMoveStateTouchInteracting(event, rawEvent, policyFlags); + break; + case MotionEvent.ACTION_UP: + handleActionUp(event, policyFlags); + break; + } + } + + /** + * Handles a motion event in touch exploring state. + * + * @param event The event to be handled. + * @param rawEvent The raw (unmodified) motion event. + * @param policyFlags The policy flags associated with the event. + */ + private void handleMotionEventStateTouchExploring( + MotionEvent event, MotionEvent rawEvent, int policyFlags) { + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + // We should have already received ACTION_DOWN. Ignore. + break; + case MotionEvent.ACTION_POINTER_DOWN: + handleActionPointerDown(); + break; + case MotionEvent.ACTION_MOVE: + handleActionMoveStateTouchExploring(event, rawEvent, policyFlags); + break; + case MotionEvent.ACTION_UP: + handleActionUp(event, policyFlags); + break; + default: + break; + } + } + + /** * Handles ACTION_POINTER_DOWN when in the touch exploring state. This event represents an * additional finger touching the screen. */ - private void handleActionPointerDownStateTouchExploring() { + private void handleActionPointerDown() { // Another finger down means that if we have not started to deliver // hover events, we will not have to. The code for ACTION_MOVE will // decide what we will actually do next. @@ -459,10 +505,10 @@ public class TouchExplorer extends BaseEventStreamTransformation mSendHoverExitDelayed.cancel(); } /** - * Handles ACTION_MOVE while in the initial touch exploring state. This is where transitions to + * Handles ACTION_MOVE while in the touch interacting state. This is where transitions to * delegating and dragging states are handled. */ - private void handleActionMoveStateTouchExploring( + private void handleActionMoveStateTouchInteracting( MotionEvent event, MotionEvent rawEvent, int policyFlags) { final int pointerId = mReceivedPointerTracker.getPrimaryPointerId(); final int pointerIndex = event.findPointerIndex(pointerId); @@ -474,41 +520,14 @@ public class TouchExplorer extends BaseEventStreamTransformation if (mSendHoverEnterAndMoveDelayed.isPending()) { // Cache the event until we discern exploration from gesturing. mSendHoverEnterAndMoveDelayed.addEvent(event); - } else if (mState.isTouchExplorationInProgress()) { - sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags); - sendMotionEvent( - event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags); } break; case 2: + // Make sure we don't have any pending transitions to touch exploration + mSendHoverEnterAndMoveDelayed.cancel(); + mSendHoverExitDelayed.cancel(); // More than one pointer so the user is not touch exploring // and now we have to decide whether to delegate or drag. - if (mSendHoverEnterAndMoveDelayed.isPending()) { - // We have not started sending events so cancel - // scheduled sending events. - mSendHoverEnterAndMoveDelayed.cancel(); - mSendHoverExitDelayed.cancel(); - } else if (mState.isTouchExplorationInProgress()) { - // If the user is touch exploring the second pointer may be - // performing a double tap to activate an item without need - // for the user to lift his exploring finger. - // It is *important* to use the distance traveled by the pointers - // on the screen which may or may not be magnified. - final float deltaX = - mReceivedPointerTracker.getReceivedPointerDownX(pointerId) - - rawEvent.getX(pointerIndex); - final float deltaY = - mReceivedPointerTracker.getReceivedPointerDownY(pointerId) - - rawEvent.getY(pointerIndex); - final double moveDelta = Math.hypot(deltaX, deltaY); - if (moveDelta < mDoubleTapSlop) { - break; - } - // We are sending events so send exit and gesture - // end since we transition to another state. - sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); - } - // Remove move history before send injected non-move events event = MotionEvent.obtainNoHistory(event); if (isDraggingGesture(event)) { @@ -525,19 +544,6 @@ public class TouchExplorer extends BaseEventStreamTransformation } break; default: - // More than one pointer so the user is not touch exploring - // and now we have to decide whether to delegate or drag. - if (mSendHoverEnterAndMoveDelayed.isPending()) { - // We have not started sending events so cancel - // scheduled sending events. - mSendHoverEnterAndMoveDelayed.cancel(); - mSendHoverExitDelayed.cancel(); - } else { - // We are sending events so send exit and gesture - // end since we transition to another state. - sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); - } - // More than two pointers are delegated to the view hierarchy. mState.startDelegating(); event = MotionEvent.obtainNoHistory(event); @@ -547,14 +553,13 @@ public class TouchExplorer extends BaseEventStreamTransformation } /** - * Handles ACTION_UP while in the initial touch exploring state. This event represents all - * fingers being lifted from the screen. + * Handles ACTION_UP while in the touch interacting state. This event represents all fingers + * being lifted from the screen. */ - private void handleActionUpStateTouchExploring(MotionEvent event, int policyFlags) { + private void handleActionUp(MotionEvent event, int policyFlags) { mAms.onTouchInteractionEnd(); final int pointerId = event.getPointerId(event.getActionIndex()); final int pointerIdBits = (1 << pointerId); - if (mSendHoverEnterAndMoveDelayed.isPending()) { // If we have not delivered the enter schedule an exit. mSendHoverExitDelayed.post(event, pointerIdBits, policyFlags); @@ -562,13 +567,69 @@ public class TouchExplorer extends BaseEventStreamTransformation // The user is touch exploring so we send events for end. sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); } - if (!mSendTouchInteractionEndDelayed.isPending()) { mSendTouchInteractionEndDelayed.post(); } } /** + * Handles move events while touch exploring. this is also where we drag or delegate based on + * the number of fingers moving on the screen. + */ + private void handleActionMoveStateTouchExploring( + MotionEvent event, MotionEvent rawEvent, int policyFlags) { + final int pointerId = mReceivedPointerTracker.getPrimaryPointerId(); + final int pointerIdBits = (1 << pointerId); + final int pointerIndex = event.findPointerIndex(pointerId); + switch (event.getPointerCount()) { + case 1: + // Touch exploration. + sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags); + sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags); + break; + case 2: + if (mSendHoverEnterAndMoveDelayed.isPending()) { + // We have not started sending events so cancel + // scheduled sending events. + mSendHoverEnterAndMoveDelayed.cancel(); + mSendHoverExitDelayed.cancel(); + } + // If the user is touch exploring the second pointer may be + // performing a double tap to activate an item without need + // for the user to lift his exploring finger. + // It is *important* to use the distance traveled by the pointers + // on the screen which may or may not be magnified. + final float deltaX = + mReceivedPointerTracker.getReceivedPointerDownX(pointerId) + - rawEvent.getX(pointerIndex); + final float deltaY = + mReceivedPointerTracker.getReceivedPointerDownY(pointerId) + - rawEvent.getY(pointerIndex); + final double moveDelta = Math.hypot(deltaX, deltaY); + if (moveDelta > mDoubleTapSlop) { + // The user is trying to either delegate or drag. + handleActionMoveStateTouchInteracting(event, rawEvent, policyFlags); + } else { + // Otherwise the double tap will be handled by the gesture detector. + sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); + } + break; + default: + // Three or more fingers is something other than touch exploration. + if (mSendHoverEnterAndMoveDelayed.isPending()) { + // We have not started sending events so cancel + // scheduled sending events. + mSendHoverEnterAndMoveDelayed.cancel(); + mSendHoverExitDelayed.cancel(); + } else { + sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); + } + handleActionMoveStateTouchInteracting(event, rawEvent, policyFlags); + break; + } + } + + /** * Handles a motion event in dragging state. * * @param event The event to be handled. @@ -670,7 +731,6 @@ public class TouchExplorer extends BaseEventStreamTransformation // Send an event to the end of the drag gesture. sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); } - mState.startTouchExploring(); } break; } } @@ -697,7 +757,6 @@ public class TouchExplorer extends BaseEventStreamTransformation mAms.onTouchInteractionEnd(); sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); - mState.startTouchExploring(); } break; default: { // Deliver the event. @@ -717,7 +776,6 @@ public class TouchExplorer extends BaseEventStreamTransformation } mExitGestureDetectionModeDelayed.cancel(); - mState.startTouchExploring(); } /** @@ -731,8 +789,13 @@ public class TouchExplorer extends BaseEventStreamTransformation AccessibilityEvent event = AccessibilityEvent.obtain(type); event.setWindowId(mAms.getActiveWindowId()); accessibilityManager.sendAccessibilityEvent(event); - mState.onInjectedAccessibilityEvent(type); + if (DEBUG) { + Slog.d( + LOG_TAG, + "Sending accessibility event" + AccessibilityEvent.eventTypeToString(type)); + } } + mState.onInjectedAccessibilityEvent(type); } /** @@ -915,6 +978,10 @@ public class TouchExplorer extends BaseEventStreamTransformation MAX_DRAGGING_ANGLE_COS); } + public TouchState getState() { + return mState; + } + /** * Class for delayed exiting from gesture detecting mode. */ @@ -947,8 +1014,7 @@ public class TouchExplorer extends BaseEventStreamTransformation private int mPointerIdBits; private int mPolicyFlags; - public void post(MotionEvent event, boolean touchExplorationInProgress, - int pointerIdBits, int policyFlags) { + public void post(MotionEvent event, int pointerIdBits, int policyFlags) { cancel(); addEvent(event); mPointerIdBits = pointerIdBits; diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java index 820c1a794635..17e969a1aa49 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java @@ -41,20 +41,33 @@ public class TouchState { public static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF; // States that the touch explorer can be in. - public static final int STATE_TOUCH_EXPLORING = 0x00000001; - public static final int STATE_DRAGGING = 0x00000002; - public static final int STATE_DELEGATING = 0x00000003; - public static final int STATE_GESTURE_DETECTING = 0x00000004; - - @IntDef({STATE_TOUCH_EXPLORING, STATE_DRAGGING, STATE_DELEGATING, STATE_GESTURE_DETECTING}) + // In the clear state the user is not touching the screen. + public static final int STATE_CLEAR = 0; + // The user is touching the screen and we are trying to figure out their intent. + // This state gets its name from the TYPE_TOUCH_INTERACTION start and end accessibility events. + public static final int STATE_TOUCH_INTERACTING = 1; + // The user is explicitly exploring the screen. + public static final int STATE_TOUCH_EXPLORING = 2; + // the user is dragging with two fingers. + public static final int STATE_DRAGGING = 3; + // The user is performing some other two finger gesture which we pass through to the view + // hierarchy as a one-finger gesture e.g. two-finger scrolling. + public static final int STATE_DELEGATING = 4; + // The user is performing something that might be a gesture. + public static final int STATE_GESTURE_DETECTING = 5; + + @IntDef({ + STATE_CLEAR, + STATE_TOUCH_INTERACTING, + STATE_TOUCH_EXPLORING, + STATE_DRAGGING, + STATE_DELEGATING, + STATE_GESTURE_DETECTING + }) public @interface State {} // The current state of the touch explorer. - private int mState = STATE_TOUCH_EXPLORING; - // Whether touch exploration is in progress. - // TODO: Add separate states to represent intend detection and actual touch exploration so that - // only one variable describes the state. - private boolean mTouchExplorationInProgress; + private int mState = STATE_CLEAR; // Helper class to track received pointers. // Todo: collapse or hide this class so multiple classes don't modify it. private final ReceivedPointerTracker mReceivedPointerTracker; @@ -69,8 +82,7 @@ public class TouchState { /** Clears the internal shared state. */ public void clear() { - mState = STATE_TOUCH_EXPLORING; - mTouchExplorationInProgress = false; + setState(STATE_CLEAR); // Reset the pointer trackers. mReceivedPointerTracker.clear(); mInjectedPointerTracker.clear(); @@ -94,18 +106,33 @@ public class TouchState { mReceivedPointerTracker.onMotionEvent(rawEvent); } - /** - * Updates the state in response to an accessibility event being sent from TouchExplorer. - * - * @param type The event type. - */ public void onInjectedAccessibilityEvent(int type) { + // The below state transitions go here because the related events are often sent on a + // delay. + // This allows state to accurately reflect the state in the moment. + // TODO: replaced the delayed event senders with delayed state transitions + // so that state transitions trigger events rather than events triggering state + // transitions. switch (type) { + case AccessibilityEvent.TYPE_TOUCH_INTERACTION_START: + startTouchInteracting(); + break; + case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END: + clear(); + break; case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START: - mTouchExplorationInProgress = true; + startTouchExploring(); break; case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: - mTouchExplorationInProgress = false; + startTouchInteracting(); + break; + case AccessibilityEvent.TYPE_GESTURE_DETECTION_START: + startGestureDetecting(); + break; + case AccessibilityEvent.TYPE_GESTURE_DETECTION_END: + startTouchInteracting(); + break; + default: break; } } @@ -117,6 +144,7 @@ public class TouchState { /** Transitions to a new state. */ public void setState(@State int state) { + if (mState == state) return; if (DEBUG) { Slog.i(LOG_TAG, getStateSymbolicName(mState) + "->" + getStateSymbolicName(state)); } @@ -159,26 +187,32 @@ public class TouchState { setState(STATE_DRAGGING); } - public boolean isTouchExplorationInProgress() { - return mTouchExplorationInProgress; + public boolean isTouchInteracting() { + return mState == STATE_TOUCH_INTERACTING; } - public void setTouchExplorationInProgress(boolean touchExplorationInProgress) { - mTouchExplorationInProgress = touchExplorationInProgress; + /** + * Transitions to the touch interacting state, where we attempt to figure out what the user is + * doing. + */ + public void startTouchInteracting() { + setState(STATE_TOUCH_INTERACTING); } + public boolean isClear() { + return mState == STATE_CLEAR; + } /** Returns a string representation of the current state. */ public String toString() { - return "TouchState { " - + "mState: " - + getStateSymbolicName(mState) - + ", mTouchExplorationInProgress" - + mTouchExplorationInProgress - + " }"; + return "TouchState { " + "mState: " + getStateSymbolicName(mState) + " }"; } /** Returns a string representation of the specified state. */ public static String getStateSymbolicName(int state) { switch (state) { + case STATE_CLEAR: + return "STATE_CLEAR"; + case STATE_TOUCH_INTERACTING: + return "STATE_TOUCH_INTERACTING"; case STATE_TOUCH_EXPLORING: return "STATE_TOUCH_EXPLORING"; case STATE_DRAGGING: diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java index d7e68f896c6c..5844f9873001 100644 --- a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java +++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java @@ -23,6 +23,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.prediction.AppPredictionContext; import android.app.prediction.AppPredictionSessionId; import android.app.prediction.AppTargetEvent; @@ -61,7 +62,8 @@ public class AppPredictionManagerService extends public AppPredictionManagerService(Context context) { super(context, new FrameworkResourcesServiceNameResolver(context, - com.android.internal.R.string.config_defaultAppPredictionService), null); + com.android.internal.R.string.config_defaultAppPredictionService), null, + PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH); mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class); } @@ -80,6 +82,22 @@ public class AppPredictionManagerService extends getContext().enforceCallingPermission(MANAGE_APP_PREDICTIONS, TAG); } + @Override // from AbstractMasterSystemService + protected void onServicePackageUpdatedLocked(@UserIdInt int userId) { + final AppPredictionPerUserService service = peekServiceForUserLocked(userId); + if (service != null) { + service.onPackageUpdatedLocked(); + } + } + + @Override // from AbstractMasterSystemService + protected void onServicePackageRestartedLocked(@UserIdInt int userId) { + final AppPredictionPerUserService service = peekServiceForUserLocked(userId); + if (service != null) { + service.onPackageRestartedLocked(); + } + } + @Override protected int getMaximumTemporaryServiceDurationMs() { return MAX_TEMP_SERVICE_DURATION_MS; diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java index 03c4542a50d4..4f49fb7578a1 100644 --- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java +++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java @@ -251,6 +251,40 @@ public class AppPredictionPerUserService extends // Do nothing, eventually the system will bind to the remote service again... } + void onPackageUpdatedLocked() { + if (isDebug()) { + Slog.v(TAG, "onPackageUpdatedLocked()"); + } + destroyAndRebindRemoteService(); + } + + void onPackageRestartedLocked() { + if (isDebug()) { + Slog.v(TAG, "onPackageRestartedLocked()"); + } + destroyAndRebindRemoteService(); + } + + private void destroyAndRebindRemoteService() { + if (mRemoteService == null) { + return; + } + + if (isDebug()) { + Slog.d(TAG, "Destroying the old remote service."); + } + mRemoteService.destroy(); + mRemoteService = null; + + mRemoteService = getRemoteServiceLocked(); + if (mRemoteService != null) { + if (isDebug()) { + Slog.d(TAG, "Rebinding to the new remote service."); + } + mRemoteService.reconnect(); + } + } + /** * Called after the remote service connected, it's used to restore state from a 'zombie' * service (i.e., after it died). diff --git a/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java index c82e7a012fff..04e0e7f7102f 100644 --- a/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java +++ b/services/appprediction/java/com/android/server/appprediction/RemoteAppPredictionService.java @@ -135,6 +135,13 @@ public class RemoteAppPredictionService extends } /** + * Schedules a request to bind to the remote service. + */ + public void reconnect() { + super.scheduleBind(); + } + + /** * Failure callback */ public interface RemoteAppPredictionServiceCallbacks diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index 044e41789bb2..027e2fb5ccaa 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -47,11 +47,10 @@ import javax.xml.datatype.DatatypeConfigurationException; public final class CompatConfig { private static final String TAG = "CompatConfig"; - private static final String CONFIG_FILE_SUFFIX = "platform_compat_config.xml"; private static final CompatConfig sInstance = new CompatConfig().initConfigFromLib( Environment.buildPath( - Environment.getRootDirectory(), "etc", "sysconfig")); + Environment.getRootDirectory(), "etc", "compatconfig")); @GuardedBy("mChanges") private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>(); @@ -212,10 +211,9 @@ public final class CompatConfig { return this; } for (File f : libraryDir.listFiles()) { + Slog.d(TAG, "Found a config file: " + f.getPath()); //TODO(b/138222363): Handle duplicate ids across config files. - if (f.getPath().endsWith(CONFIG_FILE_SUFFIX)) { - readConfig(f); - } + readConfig(f); } return this; } @@ -223,7 +221,7 @@ public final class CompatConfig { private void readConfig(File configFile) { try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) { for (Change change : XmlParser.read(in).getCompatChange()) { - Slog.w(TAG, "Adding: " + change.toString()); + Slog.d(TAG, "Adding: " + change.toString()); addChange(new CompatChange(change)); } } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) { diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 4d5dc6aba937..2b849d69ae1c 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -297,6 +297,11 @@ public class HdmiControlService extends SystemService { mHdmiControlBroadcastReceiver = new HdmiControlBroadcastReceiver(); @Nullable + // Save callback when the device is still under logcial address allocation + // Invoke once new local device is ready. + private IHdmiControlCallback mDisplayStatusCallback = null; + + @Nullable private HdmiCecController mCecController; // HDMI port information. Stored in the unmodifiable list to keep the static information @@ -763,6 +768,11 @@ public class HdmiControlService extends SystemService { // Address allocation completed for all devices. Notify each device. if (allocatingDevices.size() == ++finished[0]) { mAddressAllocated = true; + // Reinvoke the saved display status callback once the local device is ready. + if (mDisplayStatusCallback != null) { + queryDisplayStatus(mDisplayStatusCallback); + mDisplayStatusCallback = null; + } if (initiatedBy != INITIATED_BY_HOTPLUG) { // In case of the hotplug we don't call onInitializeCecComplete() // since we reallocate the logical address only. @@ -2192,6 +2202,13 @@ public class HdmiControlService extends SystemService { @ServiceThreadOnly private void queryDisplayStatus(final IHdmiControlCallback callback) { assertRunOnServiceThread(); + if (!mAddressAllocated) { + mDisplayStatusCallback = callback; + Slog.d(TAG, "Local device is under address allocation. " + + "Queue display callback for later process."); + return; + } + HdmiCecLocalDevicePlayback source = playback(); if (source == null) { Slog.w(TAG, "Local playback device not available"); diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java index a9c4d088f203..d71ffb770cc3 100644 --- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java +++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java @@ -80,7 +80,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem S extends AbstractPerUserSystemService<S, M>> extends SystemService { /** On a package update, does not refresh the per-user service in the cache. */ - public static final int PACKAGE_UPDATE_POLICY_NO_REFRESH = 0; + public static final int PACKAGE_UPDATE_POLICY_NO_REFRESH = 0x00000001; /** * On a package update, removes any existing per-user services in the cache. @@ -88,20 +88,40 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem * <p>This does not immediately recreate these services. It is assumed they will be recreated * for the next user request. */ - public static final int PACKAGE_UPDATE_POLICY_REFRESH_LAZY = 1; + public static final int PACKAGE_UPDATE_POLICY_REFRESH_LAZY = 0x00000002; /** * On a package update, removes and recreates any existing per-user services in the cache. */ - public static final int PACKAGE_UPDATE_POLICY_REFRESH_EAGER = 2; + public static final int PACKAGE_UPDATE_POLICY_REFRESH_EAGER = 0x00000004; - @IntDef(flag = true, prefix = { "PACKAGE_UPDATE_POLICY_" }, value = { + /** On a package restart, does not refresh the per-user service in the cache. */ + public static final int PACKAGE_RESTART_POLICY_NO_REFRESH = 0x00000010; + + /** + * On a package restart, removes any existing per-user services in the cache. + * + * <p>This does not immediately recreate these services. It is assumed they will be recreated + * for the next user request. + */ + public static final int PACKAGE_RESTART_POLICY_REFRESH_LAZY = 0x00000020; + + /** + * On a package restart, removes and recreates any existing per-user services in the cache. + */ + public static final int PACKAGE_RESTART_POLICY_REFRESH_EAGER = 0x00000040; + + @IntDef(flag = true, prefix = { "PACKAGE_" }, value = { PACKAGE_UPDATE_POLICY_NO_REFRESH, PACKAGE_UPDATE_POLICY_REFRESH_LAZY, - PACKAGE_UPDATE_POLICY_REFRESH_EAGER + PACKAGE_UPDATE_POLICY_REFRESH_EAGER, + PACKAGE_RESTART_POLICY_NO_REFRESH, + PACKAGE_RESTART_POLICY_REFRESH_LAZY, + PACKAGE_RESTART_POLICY_REFRESH_EAGER }) + @Retention(RetentionPolicy.SOURCE) - public @interface PackageUpdatePolicy {} + public @interface ServicePackagePolicyFlags {} /** * Log tag @@ -154,12 +174,10 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem private final SparseArray<S> mServicesCache = new SparseArray<>(); /** - * Whether the per-user service should be removed from the cache when its apk is updated. - * - * <p>One of {@link #PACKAGE_UPDATE_POLICY_NO_REFRESH}, - * {@link #PACKAGE_UPDATE_POLICY_REFRESH_LAZY} or {@link #PACKAGE_UPDATE_POLICY_REFRESH_EAGER}. + * Value that determines whether the per-user service should be removed from the cache when its + * apk is updated or restarted. */ - private final @PackageUpdatePolicy int mPackageUpdatePolicy; + private final @ServicePackagePolicyFlags int mServicePackagePolicyFlags; /** * Name of the service packages whose APK are being updated, keyed by user id. @@ -191,11 +209,11 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem @Nullable ServiceNameResolver serviceNameResolver, @Nullable String disallowProperty) { this(context, serviceNameResolver, disallowProperty, - /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_REFRESH_LAZY); + PACKAGE_UPDATE_POLICY_REFRESH_LAZY | PACKAGE_RESTART_POLICY_REFRESH_LAZY); } /** - * Full constructor. + * Full Constructor. * * @param context system context. * @param serviceNameResolver resolver for @@ -204,19 +222,32 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem * @param disallowProperty when not {@code null}, defines a {@link UserManager} restriction that * disables the service. <b>NOTE: </b> you'll also need to add it to * {@code UserRestrictionsUtils.USER_RESTRICTIONS}. - * @param packageUpdatePolicy when {@link #PACKAGE_UPDATE_POLICY_REFRESH_LAZY}, the - * {@link AbstractPerUserSystemService} is removed from the cache when the service - * package is updated; when {@link #PACKAGE_UPDATE_POLICY_REFRESH_EAGER}, the - * {@link AbstractPerUserSystemService} is removed from the cache and immediately - * re-added when the service package is updated; when - * {@link #PACKAGE_UPDATE_POLICY_NO_REFRESH}, the service is untouched during the update. + * @param servicePackagePolicyFlags a combination of + * {@link #PACKAGE_UPDATE_POLICY_NO_REFRESH}, + * {@link #PACKAGE_UPDATE_POLICY_REFRESH_LAZY}, + * {@link #PACKAGE_UPDATE_POLICY_REFRESH_EAGER}, + * {@link #PACKAGE_RESTART_POLICY_NO_REFRESH}, + * {@link #PACKAGE_RESTART_POLICY_REFRESH_LAZY} or + * {@link #PACKAGE_RESTART_POLICY_REFRESH_EAGER} */ protected AbstractMasterSystemService(@NonNull Context context, - @Nullable ServiceNameResolver serviceNameResolver, - @Nullable String disallowProperty, @PackageUpdatePolicy int packageUpdatePolicy) { + @Nullable ServiceNameResolver serviceNameResolver, @Nullable String disallowProperty, + @ServicePackagePolicyFlags int servicePackagePolicyFlags) { super(context); - mPackageUpdatePolicy = packageUpdatePolicy; + final int updatePolicyMask = PACKAGE_UPDATE_POLICY_NO_REFRESH + | PACKAGE_UPDATE_POLICY_REFRESH_LAZY | PACKAGE_UPDATE_POLICY_REFRESH_EAGER; + if ((servicePackagePolicyFlags & updatePolicyMask) == 0) { + // If the package update policy is not set, add the default flag + servicePackagePolicyFlags |= PACKAGE_UPDATE_POLICY_REFRESH_LAZY; + } + final int restartPolicyMask = PACKAGE_RESTART_POLICY_NO_REFRESH + | PACKAGE_RESTART_POLICY_REFRESH_LAZY | PACKAGE_RESTART_POLICY_REFRESH_EAGER; + if ((servicePackagePolicyFlags & restartPolicyMask) == 0) { + // If the package restart policy is not set, add the default flag + servicePackagePolicyFlags |= PACKAGE_RESTART_POLICY_REFRESH_LAZY; + } + mServicePackagePolicyFlags = servicePackagePolicyFlags; mServiceNameResolver = serviceNameResolver; if (mServiceNameResolver != null) { @@ -612,6 +643,20 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem } /** + * Called after the package data that provides the service for the given user is cleared. + */ + protected void onServicePackageDataClearedLocked(@UserIdInt int userId) { + if (verbose) Slog.v(mTag, "onServicePackageDataCleared(" + userId + ")"); + } + + /** + * Called after the package that provides the service for the given user is restarted. + */ + protected void onServicePackageRestartedLocked(@UserIdInt int userId) { + if (verbose) Slog.v(mTag, "onServicePackageRestarted(" + userId + ")"); + } + + /** * Called after the service is removed from the cache. */ @SuppressWarnings("unused") @@ -713,7 +758,7 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem final int size = mServicesCache.size(); pw.print(prefix); pw.print("Debug: "); pw.print(realDebug); pw.print(" Verbose: "); pw.println(realVerbose); - pw.print("Refresh on package update: "); pw.println(mPackageUpdatePolicy); + pw.print("Package policy flags: "); pw.println(mServicePackagePolicyFlags); if (mUpdatingPackageNames != null) { pw.print("Packages being updated: "); pw.println(mUpdatingPackageNames); } @@ -768,7 +813,12 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem } mUpdatingPackageNames.put(userId, packageName); onServicePackageUpdatingLocked(userId); - if (mPackageUpdatePolicy != PACKAGE_UPDATE_POLICY_NO_REFRESH) { + if ((mServicePackagePolicyFlags & PACKAGE_UPDATE_POLICY_NO_REFRESH) != 0) { + if (debug) { + Slog.d(mTag, "Holding service for user " + userId + " while package " + + activePackageName + " is being updated"); + } + } else { if (debug) { Slog.d(mTag, "Removing service for user " + userId + " because package " + activePackageName @@ -776,18 +826,14 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem } removeCachedServiceLocked(userId); - if (mPackageUpdatePolicy == PACKAGE_UPDATE_POLICY_REFRESH_EAGER) { + if ((mServicePackagePolicyFlags & PACKAGE_UPDATE_POLICY_REFRESH_EAGER) + != 0) { if (debug) { Slog.d(mTag, "Eagerly recreating service for user " + userId); } getServiceForUserLocked(userId); } - } else { - if (debug) { - Slog.d(mTag, "Holding service for user " + userId + " while package " - + activePackageName + " is being updated"); - } } } } @@ -839,7 +885,13 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem if (!doit) { return true; } - removeCachedServiceLocked(getChangingUserId()); + final String action = intent.getAction(); + final int userId = getChangingUserId(); + if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) { + handleActiveServiceRestartedLocked(activePackageName, userId); + } else { + removeCachedServiceLocked(userId); + } } else { handlePackageUpdateLocked(pkg); } @@ -848,6 +900,23 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem return false; } + @Override + public void onPackageDataCleared(String packageName, int uid) { + if (verbose) Slog.v(mTag, "onPackageDataCleared(): " + packageName); + final int userId = getChangingUserId(); + synchronized (mLock) { + final S service = peekServiceForUserLocked(userId); + if (service != null) { + final ComponentName componentName = service.getServiceComponentName(); + if (componentName != null) { + if (packageName.equals(componentName.getPackageName())) { + onServicePackageDataClearedLocked(userId); + } + } + } + } + } + private void handleActiveServiceRemoved(@UserIdInt int userId) { synchronized (mLock) { removeCachedServiceLocked(userId); @@ -859,6 +928,31 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem } } + private void handleActiveServiceRestartedLocked(String activePackageName, + @UserIdInt int userId) { + if ((mServicePackagePolicyFlags & PACKAGE_RESTART_POLICY_NO_REFRESH) != 0) { + if (debug) { + Slog.d(mTag, "Holding service for user " + userId + " while package " + + activePackageName + " is being restarted"); + } + } else { + if (debug) { + Slog.d(mTag, "Removing service for user " + userId + + " because package " + activePackageName + + " is being restarted"); + } + removeCachedServiceLocked(userId); + + if ((mServicePackagePolicyFlags & PACKAGE_RESTART_POLICY_REFRESH_EAGER) != 0) { + if (debug) { + Slog.d(mTag, "Eagerly recreating service for user " + userId); + } + getServiceForUserLocked(userId); + } + } + onServicePackageRestartedLocked(userId); + } + private String getActiveServicePackageNameLocked() { final int userId = getChangingUserId(); final S service = peekServiceForUserLocked(userId); diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index c7124314cae0..08e55d3b766b 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -34,6 +34,8 @@ import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.os.storage.StorageManager; +import android.provider.DeviceConfig; +import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; import android.util.StatsLog; @@ -84,6 +86,12 @@ public class BackgroundDexOptService extends JobService { // Used for calculating space threshold for downgrading unused apps. private static final int LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE = 2; + private static final int DEFAULT_INACTIVE_APP_THRESHOLD_DAYS = 10; + + private static final String DOWNGRADE_UNUSED_APPS_ENABLED = "downgrade_unused_apps_enabled"; + private static final String INACTIVE_APP_THRESHOLD_DAYS = "inactive_app_threshold_days"; + private static final String LOW_STORAGE_MULTIPLIER_FOR_DOWNGRADE = + "low_storage_threshold_multiplier_for_downgrade"; /** * Set of failed packages remembered across job runs. @@ -103,8 +111,6 @@ public class BackgroundDexOptService extends JobService { private final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false); private final File mDataDir = Environment.getDataDirectory(); - private static final long mDowngradeUnusedAppsThresholdInMillis = - getDowngradeUnusedAppsThresholdInMillis(); public static void schedule(Context context) { if (isBackgroundDexoptDisabled()) { @@ -346,14 +352,14 @@ public class BackgroundDexOptService extends JobService { // Only downgrade apps when space is low on device. // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean // up disk before user hits the actual lowStorageThreshold. - final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE + final long lowStorageThresholdForDowngrade = getLowThresholdMultiplierForDowngrade() * lowStorageThreshold; boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade); Log.d(TAG, "Should Downgrade " + shouldDowngrade); if (shouldDowngrade) { Set<String> unusedPackages = - pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis); - Log.d(TAG, "Unsused Packages " + String.join(",", unusedPackages)); + pm.getUnusedPackages(getDowngradeUnusedAppsThresholdInMillis()); + Log.d(TAG, "Unused Packages " + String.join(",", unusedPackages)); if (!unusedPackages.isEmpty()) { for (String pkg : unusedPackages) { @@ -362,12 +368,9 @@ public class BackgroundDexOptService extends JobService { // Should be aborted by the scheduler. return abortCode; } - if (downgradePackage(pm, pkg, /*isForPrimaryDex*/ true)) { + if (downgradePackage(pm, pkg)) { updatedPackages.add(pkg); } - if (supportSecondaryDex) { - downgradePackage(pm, pkg, /*isForPrimaryDex*/ false); - } } pkgs = new ArraySet<>(pkgs); @@ -415,39 +418,45 @@ public class BackgroundDexOptService extends JobService { * Try to downgrade the package to a smaller compilation filter. * eg. if the package is in speed-profile the package will be downgraded to verify. * @param pm PackageManagerService - * @param pkg The package to be downgraded. - * @param isForPrimaryDex. Apps can have several dex file, primary and secondary. - * @return true if the package was downgraded. + * @param pkg The package to be downgraded + * @return true if the package was downgraded */ - private boolean downgradePackage(PackageManagerService pm, String pkg, - boolean isForPrimaryDex) { + private boolean downgradePackage(PackageManagerService pm, String pkg) { Log.d(TAG, "Downgrading " + pkg); - boolean dex_opt_performed = false; + boolean downgradedPrimary = false; int reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE; int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE | DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB | DexoptOptions.DEXOPT_DOWNGRADE; + long package_size_before = getPackageSize(pm, pkg); + // An aggressive downgrade deletes the oat files. + boolean aggressive = false; + // This applies for system apps or if packages location is not a directory, i.e. + // monolithic install. + if (!pm.canHaveOatDir(pkg)) { + // For apps that don't have the oat directory, instead of downgrading, + // remove their compiler artifacts from dalvik cache. + pm.deleteOatArtifactsOfPackage(pkg); + aggressive = true; + downgradedPrimary = true; + } else { + downgradedPrimary = performDexOptPrimary(pm, pkg, reason, dexoptFlags); - if (isForPrimaryDex) { - // This applies for system apps or if packages location is not a directory, i.e. - // monolithic install. - if (!pm.canHaveOatDir(pkg)) { - // For apps that don't have the oat directory, instead of downgrading, - // remove their compiler artifacts from dalvik cache. - pm.deleteOatArtifactsOfPackage(pkg); - } else { - dex_opt_performed = performDexOptPrimary(pm, pkg, reason, dexoptFlags); + if (supportSecondaryDex()) { + performDexOptSecondary(pm, pkg, reason, dexoptFlags); } - } else { - dex_opt_performed = performDexOptSecondary(pm, pkg, reason, dexoptFlags); } - if (dex_opt_performed) { + // This metric aims to log the storage savings when downgrading. + // The way disk size is measured using getPackageSize only looks at the primary apks. + // Any logs that are due to secondary dex files will show 0% size reduction and pollute + // the metrics. + if (downgradedPrimary) { StatsLog.write(StatsLog.APP_DOWNGRADED, pkg, package_size_before, - getPackageSize(pm, pkg), /*aggressive=*/ false); + getPackageSize(pm, pkg), aggressive); } - return dex_opt_performed; + return downgradedPrimary; } private boolean supportSecondaryDex() { @@ -471,7 +480,7 @@ public class BackgroundDexOptService extends JobService { * concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized. * @param pm An instance of PackageManagerService * @param pkg The package to be downgraded. - * @param isForPrimaryDex. Apps can have several dex file, primary and secondary. + * @param isForPrimaryDex Apps can have several dex file, primary and secondary. * @return true if the package was downgraded. */ private boolean optimizePackage(PackageManagerService pm, String pkg, @@ -588,12 +597,6 @@ public class BackgroundDexOptService extends JobService { // the checks above. This check is not "live" - the value is determined by a background // restart with a period of ~1 minute. PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package"); - if (pm.isStorageLow()) { - if (DEBUG_DEXOPT) { - Log.i(TAG, "Low storage, skipping this run"); - } - return false; - } final ArraySet<String> pkgs = pm.getOptimizablePackages(); if (pkgs.isEmpty()) { @@ -643,17 +646,77 @@ public class BackgroundDexOptService extends JobService { } private static long getDowngradeUnusedAppsThresholdInMillis() { + long defaultValue = Long.MAX_VALUE; + if (isDowngradeFeatureEnabled()) { + return getInactiveAppsThresholdMillis(); + } final String sysPropKey = "pm.dexopt.downgrade_after_inactive_days"; String sysPropValue = SystemProperties.get(sysPropKey); if (sysPropValue == null || sysPropValue.isEmpty()) { Log.w(TAG, "SysProp " + sysPropKey + " not set"); - return Long.MAX_VALUE; + return defaultValue; + } + try { + return TimeUnit.DAYS.toMillis(Long.parseLong(sysPropValue)); + } catch (NumberFormatException e) { + Log.w(TAG, "Couldn't parse long for pm.dexopt.downgrade_after_inactive_days: " + + sysPropValue + ". Returning default value instead."); + return defaultValue; } - return TimeUnit.DAYS.toMillis(Long.parseLong(sysPropValue)); } private static boolean isBackgroundDexoptDisabled() { return SystemProperties.getBoolean("pm.dexopt.disable_bg_dexopt" /* key */, false /* default */); } + + private static boolean isDowngradeFeatureEnabled() { + // DeviceConfig enables the control of on device features via remotely configurable flags, + // compared to SystemProperties which is only a way of sharing info system-widely, but are + // not configurable on the server-side. + String downgradeUnusedAppsEnabledFlag = + DeviceConfig.getProperty( + DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, + DOWNGRADE_UNUSED_APPS_ENABLED); + return !TextUtils.isEmpty(downgradeUnusedAppsEnabledFlag) + && Boolean.parseBoolean(downgradeUnusedAppsEnabledFlag); + } + + private static long getInactiveAppsThresholdMillis() { + long defaultValue = TimeUnit.DAYS.toMillis(DEFAULT_INACTIVE_APP_THRESHOLD_DAYS); + String inactiveAppThresholdDaysFlag = + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, + INACTIVE_APP_THRESHOLD_DAYS); + if (!TextUtils.isEmpty(inactiveAppThresholdDaysFlag)) { + try { + return TimeUnit.DAYS.toMillis(Long.parseLong(inactiveAppThresholdDaysFlag)); + } catch (NumberFormatException e) { + Log.w(TAG, "Couldn't parse long for " + INACTIVE_APP_THRESHOLD_DAYS + " flag: " + + inactiveAppThresholdDaysFlag + ". Returning default value instead."); + return defaultValue; + } + } + return defaultValue; + } + + private static int getLowThresholdMultiplierForDowngrade() { + int defaultValue = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE; + if (isDowngradeFeatureEnabled()) { + String lowStorageThresholdMultiplierFlag = + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, + LOW_STORAGE_MULTIPLIER_FOR_DOWNGRADE); + if (!TextUtils.isEmpty(lowStorageThresholdMultiplierFlag)) { + try { + return Integer.parseInt(lowStorageThresholdMultiplierFlag); + } catch (NumberFormatException e) { + Log.w(TAG, "Couldn't parse long for " + + LOW_STORAGE_MULTIPLIER_FOR_DOWNGRADE + " flag: " + + lowStorageThresholdMultiplierFlag + + ". Returning default value instead."); + return defaultValue; + } + } + } + return defaultValue; + } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 2cfd28d23954..22929d835f80 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -650,9 +650,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void updatePermissionFlagsInternal(String permName, String packageName, int flagMask, int flagValues, int callingUid, int userId, boolean overridePolicy, PermissionCallback callback) { - if (ApplicationPackageManager.DEBUG_TRACE_GRANTS + if (ApplicationPackageManager.DEBUG_TRACE_PERMISSION_UPDATES && ApplicationPackageManager.shouldTraceGrant(packageName, permName, userId)) { - Log.i(TAG, "System is updating flags for " + Log.i(TAG, "System is updating flags for " + packageName + " " + permName + " for user " + userId + " " + DebugUtils.flagsToString( PackageManager.class, "FLAG_PERMISSION_", flagMask) @@ -1185,7 +1185,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { boolean overridePolicy, int callingUid, final int userId, PermissionCallback callback) { if (ApplicationPackageManager.DEBUG_TRACE_GRANTS && ApplicationPackageManager.shouldTraceGrant(packageName, permName, userId)) { - Log.i(TAG, "System is granting " + Log.i(TAG, "System is granting " + packageName + " " + permName + " for user " + userId + " on behalf of uid " + callingUid + " " + mPackageManagerInt.getNameForUid(callingUid), new RuntimeException()); @@ -1345,9 +1345,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { // TODO swap permission name and package name private void revokeRuntimePermissionInternal(String permName, String packageName, boolean overridePolicy, int callingUid, final int userId, PermissionCallback callback) { - if (ApplicationPackageManager.DEBUG_TRACE_GRANTS + if (ApplicationPackageManager.DEBUG_TRACE_PERMISSION_UPDATES && ApplicationPackageManager.shouldTraceGrant(packageName, permName, userId)) { - Log.i(TAG, "System is revoking " + Log.i(TAG, "System is revoking " + packageName + " " + permName + " for user " + userId + " on behalf of uid " + callingUid + " " + mPackageManagerInt.getNameForUid(callingUid), new RuntimeException()); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java index 274ca368baa1..104aacb5ef79 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java @@ -16,15 +16,18 @@ package com.android.server.accessibility.gestures; +import static com.android.server.accessibility.gestures.TouchState.STATE_CLEAR; +import static com.android.server.accessibility.gestures.TouchState.STATE_DELEGATING; +import static com.android.server.accessibility.gestures.TouchState.STATE_DRAGGING; +import static com.android.server.accessibility.gestures.TouchState.STATE_TOUCH_EXPLORING; + import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import android.content.Context; import android.graphics.PointF; import android.os.SystemClock; import android.testing.DexmakerShareClassLoaderRule; -import android.util.DebugUtils; import android.view.InputDevice; import android.view.MotionEvent; @@ -46,10 +49,6 @@ import java.util.List; @RunWith(AndroidJUnit4.class) public class TouchExplorerTest { - public static final int STATE_TOUCH_EXPLORING = 0x00000001; - public static final int STATE_DRAGGING = 0x00000002; - public static final int STATE_DELEGATING = 0x00000004; - private static final int FLAG_1FINGER = 0x8000; private static final int FLAG_2FINGERS = 0x0100; private static final int FLAG_3FINGERS = 0x0200; @@ -112,7 +111,7 @@ public class TouchExplorerTest { @Test public void testTwoFingersMove_shouldDelegatingAndInjectActionDownPointerDown() { - goFromStateIdleTo(STATE_MOVING_2FINGERS); + goFromStateClearTo(STATE_MOVING_2FINGERS); assertState(STATE_DELEGATING); assertCapturedEvents( @@ -123,7 +122,7 @@ public class TouchExplorerTest { @Test public void testTwoFingersDrag_shouldDraggingAndActionDown() { - goFromStateIdleTo(STATE_DRAGGING_2FINGERS); + goFromStateClearTo(STATE_DRAGGING_2FINGERS); assertState(STATE_DRAGGING); assertCapturedEvents(MotionEvent.ACTION_DOWN); @@ -133,7 +132,7 @@ public class TouchExplorerTest { @Test public void testTwoFingersNotDrag_shouldDelegatingAndActionUpDownPointerDown() { // only from dragging state, and withMoveHistory no dragging - goFromStateIdleTo(STATE_PINCH_2FINGERS); + goFromStateClearTo(STATE_PINCH_2FINGERS); assertState(STATE_DELEGATING); assertCapturedEvents( @@ -146,7 +145,7 @@ public class TouchExplorerTest { @Test public void testThreeFingersMove_shouldDelegatingAnd3ActionPointerDown() { - goFromStateIdleTo(STATE_MOVING_3FINGERS); + goFromStateClearTo(STATE_MOVING_3FINGERS); assertState(STATE_DELEGATING); assertCapturedEvents( @@ -165,52 +164,47 @@ public class TouchExplorerTest { return new PointF(x, y); } - private static String stateToString(int state) { - return DebugUtils.valueToString(TouchExplorerTest.class, "STATE_", state); - } - - private void goFromStateIdleTo(int state) { + private void goFromStateClearTo(int state) { try { switch (state) { - case STATE_TOUCH_EXPLORING: { + case STATE_CLEAR: { mTouchExplorer.onDestroy(); } break; case STATE_TOUCH_EXPLORING_1FINGER: { - goFromStateIdleTo(STATE_TOUCH_EXPLORING); send(downEvent()); } break; case STATE_TOUCH_EXPLORING_2FINGER: { - goFromStateIdleTo(STATE_TOUCH_EXPLORING_1FINGER); + goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER); send(pointerDownEvent()); } break; case STATE_TOUCH_EXPLORING_3FINGER: { - goFromStateIdleTo(STATE_TOUCH_EXPLORING_2FINGER); + goFromStateClearTo(STATE_TOUCH_EXPLORING_2FINGER); send(thirdPointerDownEvent()); } break; case STATE_MOVING_2FINGERS: { - goFromStateIdleTo(STATE_TOUCH_EXPLORING_2FINGER); + goFromStateClearTo(STATE_TOUCH_EXPLORING_2FINGER); moveEachPointers(mLastEvent, p(10, 0), p(5, 10)); send(mLastEvent); } break; case STATE_DRAGGING_2FINGERS: { - goFromStateIdleTo(STATE_TOUCH_EXPLORING_2FINGER); + goFromStateClearTo(STATE_TOUCH_EXPLORING_2FINGER); moveEachPointers(mLastEvent, p(10, 0), p(10, 0)); send(mLastEvent); } break; case STATE_PINCH_2FINGERS: { - goFromStateIdleTo(STATE_DRAGGING_2FINGERS); + goFromStateClearTo(STATE_DRAGGING_2FINGERS); moveEachPointers(mLastEvent, p(10, 0), p(-10, 1)); send(mLastEvent); } break; case STATE_MOVING_3FINGERS: { - goFromStateIdleTo(STATE_TOUCH_EXPLORING_3FINGER); + goFromStateClearTo(STATE_TOUCH_EXPLORING_3FINGER); moveEachPointers(mLastEvent, p(1, 0), p(1, 0), p(1, 0)); send(mLastEvent); } @@ -219,7 +213,8 @@ public class TouchExplorerTest { throw new IllegalArgumentException("Illegal state: " + state); } } catch (Throwable t) { - throw new RuntimeException("Failed to go to state " + stateToString(state), t); + throw new RuntimeException("Failed to go to state " + + TouchState.getStateSymbolicName(state), t); } } @@ -234,9 +229,9 @@ public class TouchExplorerTest { } private void assertState(int expect) { - final String expectState = "STATE_" + stateToString(expect); - assertTrue(String.format("Expect state: %s, but: %s", expectState, mTouchExplorer), - mTouchExplorer.toString().contains(expectState)); + assertEquals( + TouchState.getStateSymbolicName(expect), + TouchState.getStateSymbolicName(mTouchExplorer.getState().getState())); } private void assertCapturedEvents(int... actionsInOrder) { diff --git a/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml b/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml index aec9f77cf922..7291dc729541 100644 --- a/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml +++ b/tests/BackgroundDexOptServiceIntegrationTests/AndroidManifest.xml @@ -28,6 +28,8 @@ <uses-permission android:name="android.permission.SET_TIME" /> <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> + <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" /> + <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <application> <uses-library android:name="android.test.runner" /> diff --git a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java index 7d826f7172da..4cd56c3c42be 100644 --- a/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java +++ b/tests/BackgroundDexOptServiceIntegrationTests/src/com/android/server/pm/BackgroundDexOptServiceIntegrationTests.java @@ -22,6 +22,7 @@ import android.os.Environment; import android.os.ParcelFileDescriptor; import android.os.SystemProperties; import android.os.storage.StorageManager; +import android.provider.DeviceConfig; import android.util.Log; import androidx.test.InstrumentationRegistry; @@ -30,7 +31,9 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -52,6 +55,13 @@ import java.util.concurrent.TimeUnit; * 3. Under low storage conditions and package is recently used, check * that dexopt upgrades test app to $(getprop pm.dexopt.bg-dexopt). * + * When downgrade feature is on (downgrade_unused_apps_enabled flag is set to true): + * 4 On low storage, check that the inactive packages are downgraded. + * 5. On low storage, check that used packages are upgraded. + * 6. On storage completely full, dexopt fails. + * 7. Not on low storage, unused packages are upgraded. + * 8. Low storage, unused app is downgraded. When app is used again, app is upgraded. + * * Each test case runs "cmd package bg-dexopt-job com.android.frameworks.bgdexopttest". * * The setup for these tests make sure this package has been configured to have been recently used @@ -59,6 +69,10 @@ import java.util.concurrent.TimeUnit; * recently used, it sets the time forward more than * `getprop pm.dexopt.downgrade_after_inactive_days` days. * + * For some of the tests, the DeviceConfig flags inactive_app_threshold_days and + * downgrade_unused_apps_enabled are set. These turn on/off the downgrade unused apps feature for + * all devices and set the time threshold for unused apps. + * * For tests that require low storage, the phone is filled up. * * Run with "atest BackgroundDexOptServiceIntegrationTests". @@ -80,10 +94,14 @@ public final class BackgroundDexOptServiceIntegrationTests { "pm.dexopt.downgrade_after_inactive_days", 0); // Needs to be between 1.0 and 2.0. private static final double LOW_STORAGE_MULTIPLIER = 1.5; + private static final int DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS = 15; // The file used to fill up storage. private File mBigFile; + @Rule + public ExpectedException mExpectedException = ExpectedException.none(); + // Remember start time. @BeforeClass public static void setUpAll() { @@ -196,11 +214,27 @@ public final class BackgroundDexOptServiceIntegrationTests { logSpaceRemaining(); } + private void fillUpStorageCompletely() throws IOException { + fillUpStorage((getStorageLowBytes())); + } + // Fill up storage so that device is in low storage condition. private void fillUpToLowStorage() throws IOException { fillUpStorage((long) (getStorageLowBytes() * LOW_STORAGE_MULTIPLIER)); } + private void setInactivePackageThreshold(int threshold) { + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, + "inactive_app_threshold_days", Integer.toString(threshold), false); + } + + private void enableDowngradeFeature(boolean enabled) { + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, + "downgrade_unused_apps_enabled", Boolean.toString(enabled), false); + } + // TODO(aeubanks): figure out how to get scheduled bg-dexopt to run private static void runBackgroundDexOpt() throws IOException { String result = runShellCommand("cmd package bg-dexopt-job " + PACKAGE_NAME); @@ -244,7 +278,7 @@ public final class BackgroundDexOptServiceIntegrationTests { // Test that background dexopt under normal conditions succeeds. @Test - public void testBackgroundDexOpt() throws IOException { + public void testBackgroundDexOpt_normalConditions_dexOptSucceeds() throws IOException { // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "verify"); Assert.assertEquals("verify", getCompilerFilter(PACKAGE_NAME)); @@ -257,17 +291,16 @@ public final class BackgroundDexOptServiceIntegrationTests { // Test that background dexopt under low storage conditions upgrades used packages. @Test - public void testBackgroundDexOptDowngradeSkipRecentlyUsedPackage() throws IOException { + public void testBackgroundDexOpt_lowStorage_usedPkgsUpgraded() throws IOException { // Should be less than DOWNGRADE_AFTER_DAYS. long deltaDays = DOWNGRADE_AFTER_DAYS - 1; try { + enableDowngradeFeature(false); // Set time to future. setTimeFutureDays(deltaDays); - // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "quicken"); Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); - // Fill up storage to trigger low storage threshold. fillUpToLowStorage(); @@ -282,18 +315,48 @@ public final class BackgroundDexOptServiceIntegrationTests { } // Test that background dexopt under low storage conditions downgrades unused packages. + // This happens if the system property pm.dexopt.downgrade_after_inactive_days is set + // (e.g. on Android Go devices). @Test - public void testBackgroundDexOptDowngradeSuccessful() throws IOException { + public void testBackgroundDexOpt_lowStorage_unusedPkgsDowngraded() + throws IOException { // Should be more than DOWNGRADE_AFTER_DAYS. long deltaDays = DOWNGRADE_AFTER_DAYS + 1; try { + enableDowngradeFeature(false); // Set time to future. setTimeFutureDays(deltaDays); - // Set filter to quicken. compilePackageWithFilter(PACKAGE_NAME, "quicken"); Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); + // Fill up storage to trigger low storage threshold. + fillUpToLowStorage(); + + runBackgroundDexOpt(); + + // Verify that downgrade is successful. + Assert.assertEquals(DOWNGRADE_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); + } finally { + // Reset time. + setTimeFutureDays(-deltaDays); + } + } + // Test that the background dexopt downgrades inactive packages when the downgrade feature is + // enabled. + @Test + public void testBackgroundDexOpt_downgradeFeatureEnabled_lowStorage_inactivePkgsDowngraded() + throws IOException { + // Should be more than DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS. + long deltaDays = DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS + 1; + try { + enableDowngradeFeature(true); + setInactivePackageThreshold(DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS); + // Set time to future. + setTimeFutureDays(deltaDays); + // Set filter to quicken. + compilePackageWithFilter(PACKAGE_NAME, "quicken"); + Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); // Fill up storage to trigger low storage threshold. fillUpToLowStorage(); @@ -307,4 +370,106 @@ public final class BackgroundDexOptServiceIntegrationTests { } } + // Test that the background dexopt upgrades used packages when the downgrade feature is enabled. + // This test doesn't fill the device storage completely, but to a multiplier of the low storage + // threshold and this is why apps can still be optimized. + @Test + public void testBackgroundDexOpt_downgradeFeatureEnabled_lowStorage_usedPkgsUpgraded() + throws IOException { + enableDowngradeFeature(true); + // Set filter to quicken. + compilePackageWithFilter(PACKAGE_NAME, "quicken"); + Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); + // Fill up storage to trigger low storage threshold. + fillUpToLowStorage(); + + runBackgroundDexOpt(); + + /// Verify that bg-dexopt is successful in upgrading the used packages. + Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); + } + + // Test that the background dexopt fails and doesn't change the compilation filter of used + // packages when the downgrade feature is enabled and the storage is filled up completely. + // The bg-dexopt shouldn't optimise nor downgrade these packages. + @Test + public void testBackgroundDexOpt_downgradeFeatureEnabled_fillUpStorageCompletely_dexOptFails() + throws IOException { + enableDowngradeFeature(true); + String previousCompilerFilter = getCompilerFilter(PACKAGE_NAME); + + // Fill up storage completely, without using a multiplier for the low storage threshold. + fillUpStorageCompletely(); + + // When the bg dexopt runs with the storage filled up completely, it will fail. + mExpectedException.expect(IllegalStateException.class); + runBackgroundDexOpt(); + + /// Verify that bg-dexopt doesn't change the compilation filter of used apps. + Assert.assertEquals(previousCompilerFilter, getCompilerFilter(PACKAGE_NAME)); + } + + // Test that the background dexopt upgrades the unused packages when the downgrade feature is + // on if the device is not low on storage. + @Test + public void testBackgroundDexOpt_downgradeFeatureEnabled_notLowStorage_unusedPkgsUpgraded() + throws IOException { + // Should be more than DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS. + long deltaDays = DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS + 1; + try { + enableDowngradeFeature(true); + setInactivePackageThreshold(DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS); + // Set time to future. + setTimeFutureDays(deltaDays); + // Set filter to quicken. + compilePackageWithFilter(PACKAGE_NAME, "quicken"); + Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); + + runBackgroundDexOpt(); + + // Verify that bg-dexopt is successful in upgrading the unused packages when the device + // is not low on storage. + Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); + } finally { + // Reset time. + setTimeFutureDays(-deltaDays); + } + } + + // Test that when an unused package (which was downgraded) is used again, it's re-optimized when + // bg-dexopt runs again. + @Test + public void testBackgroundDexOpt_downgradeFeatureEnabled_downgradedPkgsUpgradedAfterUse() + throws IOException { + // Should be more than DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS. + long deltaDays = DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS + 1; + try { + enableDowngradeFeature(true); + setInactivePackageThreshold(DOWNGRADE_FEATURE_PKG_INACTIVE_AFTER_DAYS); + // Set time to future. + setTimeFutureDays(deltaDays); + // Fill up storage to trigger low storage threshold. + fillUpToLowStorage(); + // Set filter to quicken. + compilePackageWithFilter(PACKAGE_NAME, "quicken"); + Assert.assertEquals("quicken", getCompilerFilter(PACKAGE_NAME)); + + runBackgroundDexOpt(); + + // Verify that downgrade is successful. + Assert.assertEquals(DOWNGRADE_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); + + // Reset time. + setTimeFutureDays(-deltaDays); + deltaDays = 0; + runBackgroundDexOpt(); + + // Verify that bg-dexopt is successful in upgrading the unused packages that were used + // again. + Assert.assertEquals(BG_DEXOPT_COMPILER_FILTER, getCompilerFilter(PACKAGE_NAME)); + } finally { + // Reset time. + setTimeFutureDays(-deltaDays); + } + } } |