diff options
25 files changed, 523 insertions, 122 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 29e135f8b0e9..4c90d7b225a5 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -34,6 +34,7 @@ import static android.view.Display.INVALID_DISPLAY; import static android.window.ConfigurationHelper.freeTextLayoutCachesIfNeeded; import static android.window.ConfigurationHelper.isDifferentDisplay; import static android.window.ConfigurationHelper.shouldUpdateResources; +import static android.window.ConfigurationHelper.shouldUpdateWindowMetricsBounds; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; import static com.android.internal.os.SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL; @@ -6116,6 +6117,11 @@ public final class ActivityThread extends ClientTransactionHandler public static boolean shouldReportChange(@Nullable Configuration currentConfig, @NonNull Configuration newConfig, @Nullable SizeConfigurationBuckets sizeBuckets, int handledConfigChanges, boolean alwaysReportChange) { + // Always report changes in window configuration bounds + if (shouldUpdateWindowMetricsBounds(currentConfig, newConfig)) { + return true; + } + final int publicDiff = currentConfig.diffPublicOnly(newConfig); // Don't report the change if there's no public diff between current and new config. if (publicDiff == 0) { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index e0364cbe1d69..e59901b24a65 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -8716,6 +8716,7 @@ public class DevicePolicyManager { */ @RequiresPermission(value = SET_TIME, conditional = true) public void setAutoTimeEnabled(@Nullable ComponentName admin, boolean enabled) { + throwIfParentInstance("setAutoTimeEnabled"); if (mService != null) { try { mService.setAutoTimeEnabled(admin, mContext.getPackageName(), enabled); @@ -16938,6 +16939,7 @@ public class DevicePolicyManager { * android.app.role.RoleManager.ROLE_SYSTEM_SUPERVISION. */ public boolean isDeviceFinanced() { + throwIfParentInstance("isDeviceFinanced"); if (mService != null) { try { return mService.isDeviceFinanced(mContext.getPackageName()); diff --git a/core/java/android/window/ConfigurationHelper.java b/core/java/android/window/ConfigurationHelper.java index e32adcf23a3b..269ce083d205 100644 --- a/core/java/android/window/ConfigurationHelper.java +++ b/core/java/android/window/ConfigurationHelper.java @@ -106,7 +106,7 @@ public class ConfigurationHelper { * @see WindowManager#getCurrentWindowMetrics() * @see WindowManager#getMaximumWindowMetrics() */ - private static boolean shouldUpdateWindowMetricsBounds(@NonNull Configuration currentConfig, + public static boolean shouldUpdateWindowMetricsBounds(@NonNull Configuration currentConfig, @NonNull Configuration newConfig) { final Rect currentBounds = currentConfig.windowConfiguration.getBounds(); final Rect newBounds = newConfig.windowConfiguration.getBounds(); diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index af81957d1e03..b73a87c8f0d9 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -451,10 +451,8 @@ public class ActivityThreadTest { final Rect bounds = activity.getWindowManager().getCurrentWindowMetrics().getBounds(); assertEquals(activityConfigPortrait.windowConfiguration.getBounds(), bounds); - // Ensure that Activity#onConfigurationChanged() not be called because the changes in - // WindowConfiguration shouldn't be reported, and we only apply the latest Configuration - // update in transaction. - assertEquals(numOfConfig, activity.mNumOfConfigChanges); + // Ensure changes in window configuration bounds are reported + assertEquals(numOfConfig + 1, activity.mNumOfConfigChanges); } @Test diff --git a/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml index eecb4bff16ae..685e2595119a 100644 --- a/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml +++ b/packages/SettingsLib/IllustrationPreference/res/layout/illustration_preference.xml @@ -19,7 +19,6 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:layout_width="match_parent" - android:background="?android:attr/colorBackground" android:importantForAccessibility="noHideDescendants" android:gravity="center" android:orientation="horizontal"> diff --git a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml index ab0cf3138d9c..d2e9fbeef073 100644 --- a/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml +++ b/packages/SettingsLib/MainSwitchPreference/res/layout-v33/settingslib_main_switch_bar.xml @@ -19,7 +19,6 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_height="wrap_content" android:layout_width="match_parent" - android:background="?android:attr/colorBackground" android:minHeight="?android:attr/listPreferredItemHeight" android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" android:paddingStart="?android:attr/listPreferredItemPaddingStart" diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java index f5dddc39b21e..bcbd1caa45d3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java +++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java @@ -108,6 +108,12 @@ public class AvatarPickerActivity extends Activity { mWaitingForActivityResult); } + @Override + protected void onResume() { + super.onResume(); + mAdapter.onAdapterResume(); + } + private void setUpButtons() { GlifLayout glifLayout = findViewById(R.id.glif_layout); FooterBarMixin mixin = glifLayout.getMixin(FooterBarMixin.class); @@ -198,6 +204,8 @@ public class AvatarPickerActivity extends Activity { private final int[] mUserIconColors; private int mSelectedPosition = NONE; + private int mLastSelectedPosition = NONE; + AvatarAdapter() { final boolean canTakePhoto = PhotoCapabilityUtils.canTakePhoto(AvatarPickerActivity.this); @@ -226,12 +234,10 @@ public class AvatarPickerActivity extends Activity { if (position == mTakePhotoPosition) { viewHolder.setDrawable(getDrawable(R.drawable.avatar_take_photo_circled)); viewHolder.setContentDescription(getString(R.string.user_image_take_photo)); - viewHolder.setClickListener(view -> mAvatarPhotoController.takePhoto()); } else if (position == mChoosePhotoPosition) { viewHolder.setDrawable(getDrawable(R.drawable.avatar_choose_photo_circled)); viewHolder.setContentDescription(getString(R.string.user_image_choose_photo)); - viewHolder.setClickListener(view -> mAvatarPhotoController.choosePhoto()); } else if (position >= mPreselectedImageStartPosition) { int index = indexFromPosition(position); @@ -240,17 +246,30 @@ public class AvatarPickerActivity extends Activity { if (mImageDescriptions != null) { viewHolder.setContentDescription(mImageDescriptions.get(index)); } else { - viewHolder.setContentDescription( - getString(R.string.default_user_icon_description)); + viewHolder.setContentDescription(getString( + R.string.default_user_icon_description)); + } + } + viewHolder.setClickListener(view -> onViewHolderSelected(position)); + } + + private void onViewHolderSelected(int position) { + if ((mTakePhotoPosition == position) && (mLastSelectedPosition != position)) { + mAvatarPhotoController.takePhoto(); + } else if ((mChoosePhotoPosition == position) && (mLastSelectedPosition != position)) { + mAvatarPhotoController.choosePhoto(); + } else { + if (mSelectedPosition == position) { + deselect(position); + } else { + select(position); } - viewHolder.setClickListener(view -> { - if (mSelectedPosition == position) { - deselect(position); - } else { - select(position); - } - }); } + mLastSelectedPosition = position; + } + + public void onAdapterResume() { + mLastSelectedPosition = NONE; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt index c9579d5e1356..e79b3f4bf3ad 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialog.kt @@ -15,11 +15,12 @@ */ package com.android.systemui.accessibility.fontscaling -import android.annotation.WorkerThread import android.content.Context import android.content.pm.ActivityInfo import android.content.res.Configuration +import android.database.ContentObserver import android.os.Bundle +import android.os.Handler import android.provider.Settings import android.util.TypedValue import android.view.LayoutInflater @@ -27,13 +28,18 @@ import android.widget.Button import android.widget.SeekBar import android.widget.SeekBar.OnSeekBarChangeListener import android.widget.TextView +import androidx.annotation.MainThread +import androidx.annotation.WorkerThread import com.android.systemui.R import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.settings.SystemSettings -import java.util.concurrent.Executor +import com.android.systemui.util.time.SystemClock +import java.util.concurrent.atomic.AtomicInteger import kotlin.math.roundToInt /** The Dialog that contains a seekbar for changing the font size. */ @@ -41,17 +47,31 @@ class FontScalingDialog( context: Context, private val systemSettings: SystemSettings, private val secureSettings: SecureSettings, - @Background private val backgroundExecutor: Executor + private val systemClock: SystemClock, + @Main mainHandler: Handler, + @Background private val backgroundDelayableExecutor: DelayableExecutor ) : SystemUIDialog(context) { + private val MIN_UPDATE_INTERVAL_MS: Long = 800 + private val CHANGE_BY_SEEKBAR_DELAY_MS: Long = 100 + private val CHANGE_BY_BUTTON_DELAY_MS: Long = 300 private val strEntryValues: Array<String> = context.resources.getStringArray(com.android.settingslib.R.array.entryvalues_font_size) private lateinit var title: TextView private lateinit var doneButton: Button private lateinit var seekBarWithIconButtonsView: SeekBarWithIconButtonsView - private var lastProgress: Int = -1 + private var lastProgress: AtomicInteger = AtomicInteger(-1) + private var lastUpdateTime: Long = 0 + private var cancelUpdateFontScaleRunnable: Runnable? = null private val configuration: Configuration = Configuration(context.resources.configuration) + private val fontSizeObserver = + object : ContentObserver(mainHandler) { + override fun onChange(selfChange: Boolean) { + lastUpdateTime = systemClock.elapsedRealtime() + } + } + override fun onCreate(savedInstanceState: Bundle?) { setTitle(R.string.font_scaling_dialog_title) setView(LayoutInflater.from(context).inflate(R.layout.font_scaling_dialog, null)) @@ -79,21 +99,21 @@ class FontScalingDialog( seekBarWithIconButtonsView.setMax((strEntryValues).size - 1) val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, 1.0f) - lastProgress = fontSizeValueToIndex(currentScale) - seekBarWithIconButtonsView.setProgress(lastProgress) + lastProgress.set(fontSizeValueToIndex(currentScale)) + seekBarWithIconButtonsView.setProgress(lastProgress.get()) seekBarWithIconButtonsView.setOnSeekBarChangeListener( object : OnSeekBarChangeListener { var isTrackingTouch = false override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + // Always provide preview configuration for text first when there is a change + // in the seekbar progress. + createTextPreview(progress) + if (!isTrackingTouch) { // The seekbar progress is changed by icon buttons - changeFontSize(progress) - } else { - // Provide preview configuration for text instead of changing the system - // font scale before users release their finger from the seekbar. - createTextPreview(progress) + changeFontSize(progress, CHANGE_BY_BUTTON_DELAY_MS) } } @@ -103,26 +123,50 @@ class FontScalingDialog( override fun onStopTrackingTouch(seekBar: SeekBar) { isTrackingTouch = false - changeFontSize(seekBar.progress) + changeFontSize(seekBar.progress, CHANGE_BY_SEEKBAR_DELAY_MS) } } ) doneButton.setOnClickListener { dismiss() } + systemSettings.registerContentObserver(Settings.System.FONT_SCALE, fontSizeObserver) + } + + /** + * Avoid SeekBar flickers when changing font scale. See the description from Setting at {@link + * TextReadingPreviewController#postCommitDelayed} for the reasons of flickers. + */ + @MainThread + fun updateFontScaleDelayed(delayMsFromSource: Long) { + var delayMs = delayMsFromSource + if (systemClock.elapsedRealtime() - lastUpdateTime < MIN_UPDATE_INTERVAL_MS) { + delayMs += MIN_UPDATE_INTERVAL_MS + } + cancelUpdateFontScaleRunnable?.run() + cancelUpdateFontScaleRunnable = + backgroundDelayableExecutor.executeDelayed({ updateFontScale() }, delayMs) + } + + override fun stop() { + cancelUpdateFontScaleRunnable?.run() + cancelUpdateFontScaleRunnable = null + systemSettings.unregisterContentObserver(fontSizeObserver) } - private fun changeFontSize(progress: Int) { - if (progress != lastProgress) { + @MainThread + private fun changeFontSize(progress: Int, changedWithDelay: Long) { + if (progress != lastProgress.get()) { + lastProgress.set(progress) + if (!fontSizeHasBeenChangedFromTile) { - backgroundExecutor.execute { updateSecureSettingsIfNeeded() } + backgroundDelayableExecutor.execute { updateSecureSettingsIfNeeded() } fontSizeHasBeenChangedFromTile = true } - backgroundExecutor.execute { updateFontScale(strEntryValues[progress]) } - - lastProgress = progress + updateFontScaleDelayed(changedWithDelay) } } + @WorkerThread private fun fontSizeValueToIndex(value: Float): Int { var lastValue = strEntryValues[0].toFloat() for (i in 1 until strEntryValues.size) { @@ -150,8 +194,8 @@ class FontScalingDialog( } @WorkerThread - fun updateFontScale(newScale: String) { - systemSettings.putString(Settings.System.FONT_SCALE, newScale) + fun updateFontScale() { + systemSettings.putString(Settings.System.FONT_SCALE, strEntryValues[lastProgress.get()]) } @WorkerThread diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt index 20666344ade8..f2e47018bf4e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt @@ -31,6 +31,10 @@ open class AuthBiometricFingerprintView( context: Context, attrs: AttributeSet? = null ) : AuthBiometricView(context, attrs) { + /** If this view is for a SFPS sensor. */ + var isSfps = false + private set + /** If this view is for a UDFPS sensor. */ var isUdfps = false private set @@ -40,6 +44,7 @@ open class AuthBiometricFingerprintView( /** Set the [sensorProps] of this sensor so the view can be customized prior to layout. */ fun setSensorProperties(sensorProps: FingerprintSensorPropertiesInternal) { + isSfps = sensorProps.isAnySidefpsType isUdfps = sensorProps.isAnyUdfpsType udfpsAdapter = if (isUdfps) UdfpsDialogMeasureAdapter(this, sensorProps) else null } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt index e776ab44ee42..ae0cf3771ed3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/AuthBiometricFingerprintViewBinder.kt @@ -36,7 +36,9 @@ object AuthBiometricFingerprintViewBinder { viewModel.setRotation(view.context.display?.orientation ?: Surface.ROTATION_0) launch { viewModel.iconAsset.collect { iconAsset -> - view.updateIconViewAnimation(iconAsset) + if (view.isSfps) { + view.updateIconViewAnimation(iconAsset) + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt index 12d98473ff07..c013486b83e4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt @@ -17,7 +17,6 @@ package com.android.systemui.qs.tiles import android.content.Intent import android.os.Handler -import android.os.HandlerExecutor import android.os.Looper import android.provider.Settings import android.view.View @@ -40,8 +39,10 @@ import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.settings.SystemSettings +import com.android.systemui.util.time.SystemClock import javax.inject.Inject class FontScalingTile @@ -50,7 +51,7 @@ constructor( host: QSHost, uiEventLogger: QsEventLogger, @Background backgroundLooper: Looper, - @Main mainHandler: Handler, + @Main private val mainHandler: Handler, falsingManager: FalsingManager, metricsLogger: MetricsLogger, statusBarStateController: StatusBarStateController, @@ -59,7 +60,9 @@ constructor( private val dialogLaunchAnimator: DialogLaunchAnimator, private val systemSettings: SystemSettings, private val secureSettings: SecureSettings, - private val featureFlags: FeatureFlags + private val systemClock: SystemClock, + private val featureFlags: FeatureFlags, + @Background private val backgroundDelayableExecutor: DelayableExecutor ) : QSTileImpl<QSTile.State?>( host, @@ -89,7 +92,9 @@ constructor( mContext, systemSettings, secureSettings, - HandlerExecutor(mHandler) + systemClock, + mainHandler, + backgroundDelayableExecutor ) if (view != null) { dialogLaunchAnimator.showFromView( diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt index 353a7c370ab6..f10c21bc514a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogTest.kt @@ -50,10 +50,14 @@ private const val OFF: Int = 0 @RunWith(AndroidTestingRunner::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class FontScalingDialogTest : SysuiTestCase() { + private val MIN_UPDATE_INTERVAL_MS: Long = 800 + private val CHANGE_BY_SEEKBAR_DELAY_MS: Long = 100 + private val CHANGE_BY_BUTTON_DELAY_MS: Long = 300 private lateinit var fontScalingDialog: FontScalingDialog private lateinit var systemSettings: SystemSettings private lateinit var secureSettings: SecureSettings - private lateinit var backgroundExecutor: FakeExecutor + private lateinit var systemClock: FakeSystemClock + private lateinit var backgroundDelayableExecutor: FakeExecutor private val fontSizeValueArray: Array<String> = mContext .getResources() @@ -67,10 +71,20 @@ class FontScalingDialogTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) val mainHandler = Handler(TestableLooper.get(this).getLooper()) systemSettings = FakeSettings() + // Guarantee that the systemSettings always starts with the default font scale. + systemSettings.putFloat(Settings.System.FONT_SCALE, 1.0f) secureSettings = FakeSettings() - backgroundExecutor = FakeExecutor(FakeSystemClock()) + systemClock = FakeSystemClock() + backgroundDelayableExecutor = FakeExecutor(systemClock) fontScalingDialog = - spy(FontScalingDialog(mContext, systemSettings, secureSettings, backgroundExecutor)) + FontScalingDialog( + mContext, + systemSettings, + secureSettings, + systemClock, + mainHandler, + backgroundDelayableExecutor + ) } @Test @@ -96,9 +110,14 @@ class FontScalingDialogTest : SysuiTestCase() { val seekBar: SeekBar = fontScalingDialog.findViewById(R.id.seekbar)!! seekBarWithIconButtonsView.setProgress(0) + backgroundDelayableExecutor.runAllReady() + backgroundDelayableExecutor.advanceClockToNext() + backgroundDelayableExecutor.runAllReady() iconEndFrame.performClick() - backgroundExecutor.runAllReady() + backgroundDelayableExecutor.runAllReady() + backgroundDelayableExecutor.advanceClockToNext() + backgroundDelayableExecutor.runAllReady() val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def= */ 1.0f) assertThat(seekBar.getProgress()).isEqualTo(1) @@ -117,9 +136,14 @@ class FontScalingDialogTest : SysuiTestCase() { val seekBar: SeekBar = fontScalingDialog.findViewById(R.id.seekbar)!! seekBarWithIconButtonsView.setProgress(fontSizeValueArray.size - 1) + backgroundDelayableExecutor.runAllReady() + backgroundDelayableExecutor.advanceClockToNext() + backgroundDelayableExecutor.runAllReady() iconStartFrame.performClick() - backgroundExecutor.runAllReady() + backgroundDelayableExecutor.runAllReady() + backgroundDelayableExecutor.advanceClockToNext() + backgroundDelayableExecutor.runAllReady() val currentScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def= */ 1.0f) assertThat(seekBar.getProgress()).isEqualTo(fontSizeValueArray.size - 2) @@ -139,7 +163,7 @@ class FontScalingDialogTest : SysuiTestCase() { // Default seekbar progress for font size is 1, set it to another progress 0 seekBarWithIconButtonsView.setProgress(0) - backgroundExecutor.runAllReady() + backgroundDelayableExecutor.runAllReady() val currentSettings = secureSettings.getInt( @@ -153,6 +177,7 @@ class FontScalingDialogTest : SysuiTestCase() { @Test fun dragSeekbar_systemFontSizeSettingsDoesNotChange() { + fontScalingDialog = spy(fontScalingDialog) val slider: SeekBarWithIconButtonsView = spy(SeekBarWithIconButtonsView(mContext)) whenever( fontScalingDialog.findViewById<SeekBarWithIconButtonsView>(R.id.font_scaling_slider) @@ -169,7 +194,9 @@ class FontScalingDialogTest : SysuiTestCase() { // OnSeekBarChangeListener and the seekbar could get updated progress value // in onStopTrackingTouch. seekBar.progress = 0 - backgroundExecutor.runAllReady() + backgroundDelayableExecutor.runAllReady() + backgroundDelayableExecutor.advanceClockToNext() + backgroundDelayableExecutor.runAllReady() // Verify that the scale of font size remains the default value 1.0f. var systemScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def= */ 1.0f) @@ -177,7 +204,9 @@ class FontScalingDialogTest : SysuiTestCase() { // Simulate releasing the finger from the seekbar. seekBarChangeCaptor.value.onStopTrackingTouch(seekBar) - backgroundExecutor.runAllReady() + backgroundDelayableExecutor.runAllReady() + backgroundDelayableExecutor.advanceClockToNext() + backgroundDelayableExecutor.runAllReady() // Verify that the scale of font size has been updated. systemScale = systemSettings.getFloat(Settings.System.FONT_SCALE, /* def= */ 1.0f) @@ -188,6 +217,7 @@ class FontScalingDialogTest : SysuiTestCase() { @Test fun dragSeekBar_createTextPreview() { + fontScalingDialog = spy(fontScalingDialog) val slider: SeekBarWithIconButtonsView = spy(SeekBarWithIconButtonsView(mContext)) whenever( fontScalingDialog.findViewById<SeekBarWithIconButtonsView>(R.id.font_scaling_slider) @@ -205,7 +235,8 @@ class FontScalingDialogTest : SysuiTestCase() { /* progress= */ 0, /* fromUser= */ false ) - backgroundExecutor.runAllReady() + backgroundDelayableExecutor.advanceClockToNext() + backgroundDelayableExecutor.runAllReady() verify(fontScalingDialog).createTextPreview(/* index= */ 0) fontScalingDialog.dismiss() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt index 1d6f225dd0a3..ddbfca57e688 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt @@ -33,10 +33,12 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSHost import com.android.systemui.qs.QsEventLogger import com.android.systemui.qs.logging.QSLogger +import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.nullable import com.android.systemui.util.settings.FakeSettings +import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Before @@ -61,6 +63,8 @@ class FontScalingTileTest : SysuiTestCase() { @Mock private lateinit var uiEventLogger: QsEventLogger private lateinit var testableLooper: TestableLooper + private lateinit var systemClock: FakeSystemClock + private lateinit var backgroundDelayableExecutor: FakeExecutor private lateinit var fontScalingTile: FontScalingTile val featureFlags = FakeFeatureFlags() @@ -70,6 +74,8 @@ class FontScalingTileTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) testableLooper = TestableLooper.get(this) `when`(qsHost.getContext()).thenReturn(mContext) + systemClock = FakeSystemClock() + backgroundDelayableExecutor = FakeExecutor(systemClock) fontScalingTile = FontScalingTile( @@ -85,7 +91,9 @@ class FontScalingTileTest : SysuiTestCase() { dialogLaunchAnimator, FakeSettings(), FakeSettings(), - featureFlags + FakeSystemClock(), + featureFlags, + backgroundDelayableExecutor, ) fontScalingTile.initialize() testableLooper.processAllMessages() diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index c8db662a7f49..7975e49541a0 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -922,7 +922,8 @@ public class CompanionDeviceManagerService extends SystemService { String[] args, ShellCallback callback, ResultReceiver resultReceiver) throws RemoteException { new CompanionDeviceShellCommand(CompanionDeviceManagerService.this, mAssociationStore, - mDevicePresenceMonitor, mTransportManager, mSystemDataTransferRequestStore) + mDevicePresenceMonitor, mTransportManager, mSystemDataTransferRequestStore, + mAssociationRequestsProcessor) .exec(this, in, out, err, args, callback, resultReceiver); } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java index 669686adddb8..c511429a37d8 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java @@ -17,14 +17,19 @@ package com.android.server.companion; import android.companion.AssociationInfo; +import android.companion.ContextSyncMessage; +import android.companion.Telecom; import android.companion.datatransfer.PermissionSyncRequest; import android.net.MacAddress; import android.os.Binder; import android.os.ShellCommand; +import android.util.proto.ProtoOutputStream; import com.android.server.companion.datatransfer.SystemDataTransferRequestStore; +import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncController; import com.android.server.companion.presence.CompanionDevicePresenceMonitor; import com.android.server.companion.transport.CompanionTransportManager; +import com.android.server.companion.transport.Transport; import java.io.PrintWriter; import java.util.List; @@ -33,22 +38,25 @@ class CompanionDeviceShellCommand extends ShellCommand { private static final String TAG = "CDM_CompanionDeviceShellCommand"; private final CompanionDeviceManagerService mService; - private final AssociationStore mAssociationStore; + private final AssociationStoreImpl mAssociationStore; private final CompanionDevicePresenceMonitor mDevicePresenceMonitor; private final CompanionTransportManager mTransportManager; private final SystemDataTransferRequestStore mSystemDataTransferRequestStore; + private final AssociationRequestsProcessor mAssociationRequestsProcessor; CompanionDeviceShellCommand(CompanionDeviceManagerService service, - AssociationStore associationStore, + AssociationStoreImpl associationStore, CompanionDevicePresenceMonitor devicePresenceMonitor, CompanionTransportManager transportManager, - SystemDataTransferRequestStore systemDataTransferRequestStore) { + SystemDataTransferRequestStore systemDataTransferRequestStore, + AssociationRequestsProcessor associationRequestsProcessor) { mService = service; mAssociationStore = associationStore; mDevicePresenceMonitor = devicePresenceMonitor; mTransportManager = transportManager; mSystemDataTransferRequestStore = systemDataTransferRequestStore; + mAssociationRequestsProcessor = associationRequestsProcessor; } @Override @@ -117,12 +125,107 @@ class CompanionDeviceShellCommand extends ShellCommand { } break; - case "create-dummy-transport": + case "create-emulated-transport": // This command creates a RawTransport in order to test Transport listeners associationId = getNextIntArgRequired(); - mTransportManager.createDummyTransport(associationId); + mTransportManager.createEmulatedTransport(associationId); break; + case "send-context-sync-empty-message": { + associationId = getNextIntArgRequired(); + mTransportManager.createEmulatedTransport(associationId) + .processMessage(Transport.MESSAGE_REQUEST_CONTEXT_SYNC, + /* sequence= */ 0, + CrossDeviceSyncController.createEmptyMessage()); + break; + } + + case "send-context-sync-call-create-message": { + associationId = getNextIntArgRequired(); + String callId = getNextArgRequired(); + String address = getNextArgRequired(); + String facilitator = getNextArgRequired(); + mTransportManager.createEmulatedTransport(associationId) + .processMessage(Transport.MESSAGE_REQUEST_CONTEXT_SYNC, + /* sequence= */ 0, + CrossDeviceSyncController.createCallCreateMessage(callId, + address, facilitator)); + break; + } + + case "send-context-sync-call-control-message": { + associationId = getNextIntArgRequired(); + String callId = getNextArgRequired(); + int control = getNextIntArgRequired(); + mTransportManager.createEmulatedTransport(associationId) + .processMessage(Transport.MESSAGE_REQUEST_CONTEXT_SYNC, + /* sequence= */ 0, + CrossDeviceSyncController.createCallControlMessage(callId, + control)); + break; + } + + case "send-context-sync-call-facilitators-message": { + associationId = getNextIntArgRequired(); + int numberOfFacilitators = getNextIntArgRequired(); + final ProtoOutputStream pos = new ProtoOutputStream(); + pos.write(ContextSyncMessage.VERSION, 1); + final long telecomToken = pos.start(ContextSyncMessage.TELECOM); + for (int i = 0; i < numberOfFacilitators; i++) { + final long facilitatorsToken = pos.start(Telecom.FACILITATORS); + pos.write(Telecom.CallFacilitator.NAME, "Call Facilitator App #" + i); + pos.write(Telecom.CallFacilitator.IDENTIFIER, "com.android.test" + i); + pos.end(facilitatorsToken); + } + pos.end(telecomToken); + mTransportManager.createEmulatedTransport(associationId) + .processMessage(Transport.MESSAGE_REQUEST_CONTEXT_SYNC, + /* sequence= */ 0, pos.getBytes()); + break; + } + + case "send-context-sync-call-message": { + associationId = getNextIntArgRequired(); + String callId = getNextArgRequired(); + String facilitatorId = getNextArgRequired(); + final ProtoOutputStream pos = new ProtoOutputStream(); + pos.write(ContextSyncMessage.VERSION, 1); + final long telecomToken = pos.start(ContextSyncMessage.TELECOM); + final long callsToken = pos.start(Telecom.CALLS); + pos.write(Telecom.Call.ID, callId); + final long originToken = pos.start(Telecom.Call.ORIGIN); + pos.write(Telecom.Call.Origin.CALLER_ID, "Caller Name"); + final long facilitatorToken = pos.start( + Telecom.Request.CreateAction.FACILITATOR); + pos.write(Telecom.CallFacilitator.NAME, "Test App Name"); + pos.write(Telecom.CallFacilitator.IDENTIFIER, facilitatorId); + pos.end(facilitatorToken); + pos.end(originToken); + pos.write(Telecom.Call.STATUS, Telecom.Call.RINGING); + pos.write(Telecom.Call.CONTROLS, Telecom.ACCEPT); + pos.write(Telecom.Call.CONTROLS, Telecom.REJECT); + pos.end(callsToken); + pos.end(telecomToken); + mTransportManager.createEmulatedTransport(associationId) + .processMessage(Transport.MESSAGE_REQUEST_CONTEXT_SYNC, + /* sequence= */ 0, pos.getBytes()); + break; + } + + case "disable-context-sync": { + associationId = getNextIntArgRequired(); + int flag = getNextIntArgRequired(); + mAssociationRequestsProcessor.disableSystemDataSync(associationId, flag); + break; + } + + case "enable-context-sync": { + associationId = getNextIntArgRequired(); + int flag = getNextIntArgRequired(); + mAssociationRequestsProcessor.enableSystemDataSync(associationId, flag); + break; + } + case "allow-permission-sync": { int userId = getNextIntArgRequired(); associationId = getNextIntArgRequired(); @@ -202,7 +305,7 @@ class CompanionDeviceShellCommand extends ShellCommand { pw.println(" \"debug.cdm.cdmservice.cleanup_time_window\" system property). "); pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); - pw.println(" create-dummy-transport <ASSOCIATION_ID>"); - pw.println(" Create a dummy RawTransport for testing puspose only"); + pw.println(" create-emulated-transport <ASSOCIATION_ID>"); + pw.println(" Create an EmulatedTransport for testing purposes only"); } } diff --git a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java index a3e095e4475b..17bdb6085c45 100644 --- a/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java +++ b/services/companion/java/com/android/server/companion/transport/CompanionTransportManager.java @@ -360,15 +360,42 @@ public class CompanionTransportManager { /** * For testing purpose only. * - * Create a dummy RawTransport and notify onTransportChanged listeners. + * Create an emulated RawTransport and notify onTransportChanged listeners. */ - public void createDummyTransport(int associationId) { + public EmulatedTransport createEmulatedTransport(int associationId) { synchronized (mTransports) { FileDescriptor fd = new FileDescriptor(); ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd); - Transport transport = new RawTransport(associationId, pfd, mContext); + EmulatedTransport transport = new EmulatedTransport(associationId, pfd, mContext); + addMessageListenersToTransport(transport); mTransports.put(associationId, transport); notifyOnTransportsChanged(); + return transport; + } + } + + /** + * For testing purposes only. + * + * Emulates a transport for incoming messages but black-holes all messages sent back through it. + */ + public static class EmulatedTransport extends RawTransport { + + EmulatedTransport(int associationId, ParcelFileDescriptor fd, Context context) { + super(associationId, fd, context); + } + + /** Process an incoming message for testing purposes. */ + public void processMessage(int messageType, int sequence, byte[] data) throws IOException { + handleMessage(messageType, sequence, data); + } + + @Override + protected void sendMessage(int messageType, int sequence, @NonNull byte[] data) + throws IOException { + Slog.e(TAG, "Black-holing emulated message type 0x" + Integer.toHexString(messageType) + + " sequence " + sequence + " length " + data.length + + " to association " + mAssociationId); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 117f7473252f..a888a0b5d7d9 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -9502,11 +9502,20 @@ public class ActivityManagerService extends IActivityManager.Stub * Check if the calling process has the permission to dump given package, * throw SecurityException if it doesn't have the permission. * - * @return The UID of the given package, or {@link android.os.Process#INVALID_UID} + * @return The real UID of process that can be dumped, or {@link android.os.Process#INVALID_UID} * if the package is not found. */ int enforceDumpPermissionForPackage(String packageName, int userId, int callingUid, String function) { + // Allow SDK sandbox process to dump for its own process (under SDK sandbox package) + try { + if (Process.isSdkSandboxUid(callingUid) + && getPackageManager().getSdkSandboxPackageName().equals(packageName)) { + return callingUid; + } + } catch (RemoteException e) { + Log.e(TAG, "Could not get SDK sandbox package name"); + } final long identity = Binder.clearCallingIdentity(); int uid = INVALID_UID; try { diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java index 4c0dd115a3cc..666e5600a8b6 100644 --- a/services/core/java/com/android/server/am/AppExitInfoTracker.java +++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java @@ -467,6 +467,13 @@ public final class AppExitInfoTracker { addExitInfoInnerLocked(packages[i], uid, info, recoverable); } + // SDK sandbox exits are stored under both real and package UID + if (Process.isSdkSandboxUid(uid)) { + for (int i = 0; i < packages.length; i++) { + addExitInfoInnerLocked(packages[i], raw.getPackageUid(), info, recoverable); + } + } + schedulePersistProcessExitInfo(false); return info; @@ -1400,11 +1407,20 @@ public final class AppExitInfoTracker { } } // Claim the state information if there is any - final int uid = info.getPackageUid(); + int uid = info.getPackageUid(); + // SDK sandbox app states and app traces are stored under real UID + if (Process.isSdkSandboxUid(info.getRealUid())) { + uid = info.getRealUid(); + } final int pid = info.getPid(); - info.setProcessStateSummary(findAndRemoveFromSparse2dArray( - mActiveAppStateSummary, uid, pid)); - info.setTraceFile(findAndRemoveFromSparse2dArray(mActiveAppTraces, uid, pid)); + if (info.getProcessStateSummary() == null) { + info.setProcessStateSummary(findAndRemoveFromSparse2dArray( + mActiveAppStateSummary, uid, pid)); + } + if (info.getTraceFile() == null) { + info.setTraceFile(findAndRemoveFromSparse2dArray(mActiveAppTraces, uid, pid)); + } + info.setAppTraceRetriever(mAppTraceRetriever); map.append(pid, info); } @@ -1905,15 +1921,15 @@ public final class AppExitInfoTracker { } final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); - final int callingUserId = UserHandle.getCallingUserId(); final int userId = UserHandle.getUserId(uid); mService.mUserController.handleIncomingUser(callingPid, callingUid, userId, true, ALLOW_NON_FULL, "getTraceFileDescriptor", null); - if (mService.enforceDumpPermissionForPackage(packageName, userId, - callingUid, "getTraceFileDescriptor") != Process.INVALID_UID) { + final int filterUid = mService.enforceDumpPermissionForPackage(packageName, userId, + callingUid, "getTraceFileDescriptor"); + if (filterUid != Process.INVALID_UID) { synchronized (mLock) { - final ApplicationExitInfo info = getExitInfoLocked(packageName, uid, pid); + final ApplicationExitInfo info = getExitInfoLocked(packageName, filterUid, pid); if (info == null) { return null; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java index c681b884d2ed..522ee341010b 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyCacheImpl.java @@ -25,7 +25,9 @@ import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -48,6 +50,13 @@ public class DevicePolicyCacheImpl extends DevicePolicyCache { @GuardedBy("mLock") private int mScreenCaptureDisallowedUser = UserHandle.USER_NULL; + /** + * Indicates if screen capture is disallowed on a specific user or all users if + * it contains {@link UserHandle#USER_ALL}. + */ + @GuardedBy("mLock") + private Set<Integer> mScreenCaptureDisallowedUsers = new HashSet<>(); + @GuardedBy("mLock") private final SparseIntArray mPasswordQuality = new SparseIntArray(); @@ -70,9 +79,21 @@ public class DevicePolicyCacheImpl extends DevicePolicyCache { @Override public boolean isScreenCaptureAllowed(int userHandle) { + if (DevicePolicyManagerService.isPolicyEngineForFinanceFlagEnabled()) { + return isScreenCaptureAllowedInPolicyEngine(userHandle); + } else { + synchronized (mLock) { + return mScreenCaptureDisallowedUser != UserHandle.USER_ALL + && mScreenCaptureDisallowedUser != userHandle; + } + } + } + + private boolean isScreenCaptureAllowedInPolicyEngine(int userHandle) { + // This won't work if resolution mechanism is not strictest applies, but it's ok for now. synchronized (mLock) { - return mScreenCaptureDisallowedUser != UserHandle.USER_ALL - && mScreenCaptureDisallowedUser != userHandle; + return !mScreenCaptureDisallowedUsers.contains(userHandle) + && !mScreenCaptureDisallowedUsers.contains(UserHandle.USER_ALL); } } @@ -88,6 +109,16 @@ public class DevicePolicyCacheImpl extends DevicePolicyCache { } } + public void setScreenCaptureDisallowedUser(int userHandle, boolean disallowed) { + synchronized (mLock) { + if (disallowed) { + mScreenCaptureDisallowedUsers.add(userHandle); + } else { + mScreenCaptureDisallowedUsers.remove(userHandle); + } + } + } + @Override public int getPasswordQuality(@UserIdInt int userHandle) { synchronized (mLock) { @@ -151,7 +182,11 @@ public class DevicePolicyCacheImpl extends DevicePolicyCache { synchronized (mLock) { pw.println("Device policy cache:"); pw.increaseIndent(); - pw.println("Screen capture disallowed user: " + mScreenCaptureDisallowedUser); + if (DevicePolicyManagerService.isPolicyEngineForFinanceFlagEnabled()) { + pw.println("Screen capture disallowed users: " + mScreenCaptureDisallowedUsers); + } else { + pw.println("Screen capture disallowed user: " + mScreenCaptureDisallowedUser); + } pw.println("Password quality: " + mPasswordQuality); pw.println("Permission policy: " + mPermissionPolicy); pw.println("Admin can grant sensors permission: " + mCanGrantSensorsPermissions.get()); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java index 0c4830afafcc..641a03b931c4 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java @@ -546,8 +546,7 @@ final class DevicePolicyEngine { if (hasLocalPolicyLocked(policyDefinition, userId)) { resolvedValue = getLocalPolicyStateLocked( policyDefinition, userId).getCurrentResolvedPolicy(); - } - if (hasGlobalPolicyLocked(policyDefinition)) { + } else if (hasGlobalPolicyLocked(policyDefinition)) { resolvedValue = getGlobalPolicyStateLocked( policyDefinition).getCurrentResolvedPolicy(); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 3578b16d62c6..97987096fa86 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -8642,7 +8642,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } CallerIdentity caller; - if (isPermissionCheckFlagEnabled()) { + if (isPolicyEngineForFinanceFlagEnabled()) { caller = getCallerIdentity(who, callerPackage); } else { Objects.requireNonNull(who, "ComponentName is null"); @@ -8656,23 +8656,47 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - synchronized (getLockObject()) { - ActiveAdmin ap; - if (isPermissionCheckFlagEnabled()) { - int callerUserId = Binder.getCallingUserHandle().getIdentifier(); - int targetUserId = parent ? getProfileParentId(callerUserId) : callerUserId; - ap = enforcePermissionAndGetEnforcingAdmin( - who, MANAGE_DEVICE_POLICY_SCREEN_CAPTURE, caller.getPackageName(), - targetUserId).getActiveAdmin(); + if (isPolicyEngineForFinanceFlagEnabled()) { + int callerUserId = Binder.getCallingUserHandle().getIdentifier(); + int targetUserId = parent ? getProfileParentId(callerUserId) : callerUserId; + EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin( + who, MANAGE_DEVICE_POLICY_SCREEN_CAPTURE, caller.getPackageName(), + targetUserId); + if ((parent && isProfileOwnerOfOrganizationOwnedDevice(caller)) + || isDefaultDeviceOwner(caller)) { + if (disabled) { + mDevicePolicyEngine.setGlobalPolicy( + PolicyDefinition.SCREEN_CAPTURE_DISABLED, + admin, + new BooleanPolicyValue(disabled)); + } else { + mDevicePolicyEngine.removeGlobalPolicy( + PolicyDefinition.SCREEN_CAPTURE_DISABLED, + admin); + } } else { - ap = getParentOfAdminIfRequired( - getProfileOwnerOrDefaultDeviceOwnerLocked(caller.getUserId()), parent); + if (disabled) { + mDevicePolicyEngine.setLocalPolicy( + PolicyDefinition.SCREEN_CAPTURE_DISABLED, + admin, + new BooleanPolicyValue(disabled), + callerUserId); + } else { + mDevicePolicyEngine.removeLocalPolicy( + PolicyDefinition.SCREEN_CAPTURE_DISABLED, + admin, + callerUserId); + } } - - if (ap.disableScreenCapture != disabled) { - ap.disableScreenCapture = disabled; - saveSettingsLocked(caller.getUserId()); - pushScreenCapturePolicy(caller.getUserId()); + } else { + synchronized (getLockObject()) { + ActiveAdmin ap = getParentOfAdminIfRequired( + getProfileOwnerOrDefaultDeviceOwnerLocked(caller.getUserId()), parent); + if (ap.disableScreenCapture != disabled) { + ap.disableScreenCapture = disabled; + saveSettingsLocked(caller.getUserId()); + pushScreenCapturePolicy(caller.getUserId()); + } } } DevicePolicyEventLogger @@ -8686,6 +8710,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // DO or COPE PO on the parent profile, then this takes precedence as screen capture will // be disabled device-wide. private void pushScreenCapturePolicy(int adminUserId) { + if (isPolicyEngineForFinanceFlagEnabled()) { + return; + } // Update screen capture device-wide if disabled by the DO or COPE PO on the parent profile. // TODO(b/261999445): remove ActiveAdmin admin; @@ -8727,8 +8754,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } /** - * Returns whether or not screen capture is disabled for a given admin, or disabled for any - * active admin (if given admin is null). + * Returns whether or not screen capture is disabled for any active admin. */ @Override public boolean getScreenCaptureDisabled(ComponentName who, int userHandle, boolean parent) { @@ -8742,7 +8768,14 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Preconditions.checkCallAuthorization( isProfileOwnerOfOrganizationOwnedDevice(getCallerIdentity().getUserId())); } - return !mPolicyCache.isScreenCaptureAllowed(userHandle); + if (isPolicyEngineForFinanceFlagEnabled()) { + Boolean disallowed = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.SCREEN_CAPTURE_DISABLED, + userHandle); + return disallowed != null && disallowed; + } else { + return !mPolicyCache.isScreenCaptureAllowed(userHandle); + } } private void updateScreenCaptureDisabled() { @@ -16306,17 +16339,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return result; } } else if (DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) { - synchronized (getLockObject()) { - final DevicePolicyData policy = getUserData(userId); - final int N = policy.mAdminList.size(); - for (int i = 0; i < N; i++) { - final ActiveAdmin admin = policy.mAdminList.get(i); - if (admin.disableScreenCapture) { - result = new Bundle(); - result.putInt(Intent.EXTRA_USER_ID, userId); - result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN, - admin.info.getComponent()); - return result; + if (isPolicyEngineForFinanceFlagEnabled()) { + Boolean value = mDevicePolicyEngine.getResolvedPolicy( + PolicyDefinition.SCREEN_CAPTURE_DISABLED, userId); + if (value != null && value) { + result = new Bundle(); + result.putInt(Intent.EXTRA_USER_ID, userId); + return result; + } + } else { + synchronized (getLockObject()) { + final DevicePolicyData policy = getUserData(userId); + final int N = policy.mAdminList.size(); + for (int i = 0; i < N; i++) { + final ActiveAdmin admin = policy.mAdminList.get(i); + if (admin.disableScreenCapture) { + result = new Bundle(); + result.putInt(Intent.EXTRA_USER_ID, userId); + result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN, + admin.info.getComponent()); + return result; + } } } } @@ -23646,7 +23689,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DEFAULT_VALUE_PERMISSION_BASED_ACCESS_FLAG); } - private boolean isPolicyEngineForFinanceFlagEnabled() { + static boolean isPolicyEngineForFinanceFlagEnabled() { return DeviceConfig.getBoolean( NAMESPACE_DEVICE_POLICY_MANAGER, ENABLE_DEVICE_POLICY_ENGINE_FOR_FINANCE_FLAG, diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java index 43a2c9bbf5c5..17c84f9916dd 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java @@ -315,6 +315,14 @@ final class PolicyDefinition<V> { (Set<String> value, Context context, Integer userId, PolicyKey policyKey) -> true, new StringSetPolicySerializer()); + + static PolicyDefinition<Boolean> SCREEN_CAPTURE_DISABLED = new PolicyDefinition<>( + new NoArgsPolicyKey(DevicePolicyIdentifiers.SCREEN_CAPTURE_DISABLED_POLICY), + TRUE_MORE_RESTRICTIVE, + /* flags= */ 0, + PolicyEnforcerCallbacks::setScreenCaptureDisabled, + new BooleanPolicySerializer()); + private static final Map<String, PolicyDefinition<?>> POLICY_DEFINITIONS = new HashMap<>(); private static Map<String, Integer> USER_RESTRICTION_FLAGS = new HashMap<>(); @@ -342,6 +350,8 @@ final class PolicyDefinition<V> { GENERIC_ACCOUNT_MANAGEMENT_DISABLED); POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.PERMITTED_INPUT_METHODS_POLICY, PERMITTED_INPUT_METHODS); + POLICY_DEFINITIONS.put(DevicePolicyIdentifiers.SCREEN_CAPTURE_DISABLED_POLICY, + SCREEN_CAPTURE_DISABLED); // User Restriction Policies USER_RESTRICTION_FLAGS.put(UserManager.DISALLOW_MODIFY_ACCOUNTS, /* flags= */ 0); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java index 289098e87550..816a9848d5b3 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java @@ -19,6 +19,7 @@ package com.android.server.devicepolicy; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppGlobals; +import android.app.admin.DevicePolicyCache; import android.app.admin.DevicePolicyManager; import android.app.admin.IntentFilterPolicyKey; import android.app.admin.LockTaskPolicy; @@ -35,13 +36,16 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.os.Binder; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.UserHandle; import android.permission.AdminPermissionControlParams; import android.permission.PermissionControllerManager; import android.provider.Settings; import android.util.ArraySet; import android.util.Slog; +import android.view.IWindowManager; +import com.android.internal.os.BackgroundThread; import com.android.server.LocalServices; import com.android.server.pm.UserManagerInternal; import com.android.server.utils.Slogf; @@ -244,4 +248,31 @@ final class PolicyEnforcerCallbacks { packageName, hide != null && hide, userId); })); } + + static boolean setScreenCaptureDisabled( + @Nullable Boolean disabled, @NonNull Context context, int userId, + @NonNull PolicyKey policyKey) { + Binder.withCleanCallingIdentity(() -> { + DevicePolicyCache cache = DevicePolicyCache.getInstance(); + if (cache instanceof DevicePolicyCacheImpl) { + DevicePolicyCacheImpl parsedCache = (DevicePolicyCacheImpl) cache; + parsedCache.setScreenCaptureDisallowedUser( + userId, disabled != null && disabled); + updateScreenCaptureDisabled(); + } + }); + return true; + } + + private static void updateScreenCaptureDisabled() { + BackgroundThread.getHandler().post(() -> { + try { + IWindowManager.Stub + .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE)) + .refreshScreenCaptureDisabled(); + } catch (RemoteException e) { + Slogf.w(LOG_TAG, "Unable to notify WindowManager.", e); + } + }); + } } diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index 475966ea00b8..8f23ae4ff33b 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -72,7 +72,7 @@ public final class ProfcollectForwardingService extends SystemService { private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (intent.getAction() == INTENT_UPLOAD_PROFILES) { + if (INTENT_UPLOAD_PROFILES.equals(intent.getAction())) { Log.d(LOG_TAG, "Received broadcast to pack and upload reports"); packAndUploadReport(); } @@ -89,7 +89,7 @@ public final class ProfcollectForwardingService extends SystemService { final IntentFilter filter = new IntentFilter(); filter.addAction(INTENT_UPLOAD_PROFILES); - context.registerReceiver(mBroadcastReceiver, filter); + context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED); } /** diff --git a/telecomm/java/android/telecom/CallControlCallback.java b/telecomm/java/android/telecom/CallControlCallback.java index 35e2fd43efdd..eac2e64aa2ab 100644 --- a/telecomm/java/android/telecom/CallControlCallback.java +++ b/telecomm/java/android/telecom/CallControlCallback.java @@ -44,9 +44,11 @@ public interface CallControlCallback { * * @param wasCompleted The {@link Consumer} to be completed. If the client can set the call * active on their end, the {@link Consumer#accept(Object)} should be - * called with {@link Boolean#TRUE}. Otherwise, - * {@link Consumer#accept(Object)} should be called with - * {@link Boolean#FALSE}. + * called with {@link Boolean#TRUE}. + * + * Otherwise, {@link Consumer#accept(Object)} should be called with + * {@link Boolean#FALSE}. Telecom will effectively ignore the remote + * setActive request and the call will remain in whatever state it is in. */ void onSetActive(@NonNull Consumer<Boolean> wasCompleted); @@ -56,9 +58,11 @@ public interface CallControlCallback { * * @param wasCompleted The {@link Consumer} to be completed. If the client can set the call * inactive on their end, the {@link Consumer#accept(Object)} should be - * called with {@link Boolean#TRUE}. Otherwise, - * {@link Consumer#accept(Object)} should be called with - * {@link Boolean#FALSE}. + * called with {@link Boolean#TRUE}. + * + * Otherwise, {@link Consumer#accept(Object)} should be called with + * {@link Boolean#FALSE}. Telecom will effectively ignore the remote + * setInactive request and the call will remain in whatever state it is in. */ void onSetInactive(@NonNull Consumer<Boolean> wasCompleted); @@ -68,8 +72,11 @@ public interface CallControlCallback { * @param videoState see {@link android.telecom.CallAttributes.CallType} for valid states * @param wasCompleted The {@link Consumer} to be completed. If the client can answer the call * on their end, {@link Consumer#accept(Object)} should be called with - * {@link Boolean#TRUE}. Otherwise, {@link Consumer#accept(Object)} should - * be called with {@link Boolean#FALSE}. + * {@link Boolean#TRUE}. + * + * Otherwise,{@link Consumer#accept(Object)} should be called with + * {@link Boolean#FALSE}. However, Telecom will still disconnect + * the call and remove it from tracking. */ void onAnswer(@android.telecom.CallAttributes.CallType int videoState, @NonNull Consumer<Boolean> wasCompleted); @@ -80,9 +87,11 @@ public interface CallControlCallback { * @param disconnectCause represents the cause for disconnecting the call. * @param wasCompleted The {@link Consumer} to be completed. If the client can disconnect * the call on their end, {@link Consumer#accept(Object)} should be - * called with {@link Boolean#TRUE}. Otherwise, - * {@link Consumer#accept(Object)} should be called with - * {@link Boolean#FALSE}. + * called with {@link Boolean#TRUE}. + * + * Otherwise,{@link Consumer#accept(Object)} should be called with + * {@link Boolean#FALSE}. However, Telecom will still disconnect + * the call and remove it from tracking. */ void onDisconnect(@NonNull DisconnectCause disconnectCause, @NonNull Consumer<Boolean> wasCompleted); |