diff options
131 files changed, 2980 insertions, 2820 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 2c0b6e9ee89d..36e1c941cbc6 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -314,7 +314,6 @@ package android.app { public final class NotificationChannel implements android.os.Parcelable { method public int getOriginalImportance(); method public boolean isImportanceLockedByCriticalDeviceFunction(); - method public boolean isImportanceLockedByOEM(); method public void lockFields(int); method public void setDeleted(boolean); method public void setDeletedTimeMs(long); diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java index 436eac37b4fb..7c83d5850f76 100644 --- a/core/java/android/app/LocaleConfig.java +++ b/core/java/android/app/LocaleConfig.java @@ -45,7 +45,9 @@ import java.util.Set; * referenced in the manifest via {@code android:localeConfig} on * {@code <application>}. * - * For more information, see TODO(b/214154050): add link to guide + * <p>For more information, see + * <a href="https://developer.android.com/about/versions/13/features/app-languages#use-localeconfig"> + * the section on per-app language preferences</a>. * * @attr ref android.R.styleable#LocaleConfig_Locale_name * @attr ref android.R.styleable#AndroidManifestApplication_localeConfig diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index 91ab19b9e44a..6f0b03aeb6f3 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -867,14 +867,6 @@ public final class NotificationChannel implements Parcelable { * @hide */ @TestApi - public boolean isImportanceLockedByOEM() { - return mImportanceLockedByOEM; - } - - /** - * @hide - */ - @TestApi public boolean isImportanceLockedByCriticalDeviceFunction() { return mImportanceLockedDefaultApp; } diff --git a/core/java/android/app/admin/ProvisioningIntentHelper.java b/core/java/android/app/admin/ProvisioningIntentHelper.java index fbad90c30d7e..1c38559a4083 100644 --- a/core/java/android/app/admin/ProvisioningIntentHelper.java +++ b/core/java/android/app/admin/ProvisioningIntentHelper.java @@ -17,8 +17,10 @@ package android.app.admin; import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE; +import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE; import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME; import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME; +import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ROLE_HOLDER_EXTRAS_BUNDLE; import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_TRIGGER; import static android.app.admin.DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC; import static android.app.admin.DevicePolicyManager.PROVISIONING_TRIGGER_NFC; @@ -36,12 +38,14 @@ import android.nfc.NdefRecord; import android.nfc.NfcAdapter; import android.os.Bundle; import android.os.Parcelable; +import android.os.PersistableBundle; import android.util.Log; import java.io.IOException; import java.io.StringReader; import java.util.Enumeration; import java.util.Properties; +import java.util.Set; /** * Utility class that provides functionality to create provisioning intents from nfc intents. @@ -124,12 +128,46 @@ final class ProvisioningIntentHelper { ComponentName componentName = ComponentName.unflattenFromString( properties.getProperty(propertyName)); bundle.putParcelable(propertyName, componentName); + } else if (EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE.equals(propertyName) + || EXTRA_PROVISIONING_ROLE_HOLDER_EXTRAS_BUNDLE.equals(propertyName)) { + try { + bundle.putParcelable(propertyName, + deserializeExtrasBundle(properties, propertyName)); + } catch (IOException e) { + Log.e(TAG, + "Failed to parse " + propertyName + ".", e); + } } else { bundle.putString(propertyName, properties.getProperty(propertyName)); } } + /** + * Get a {@link PersistableBundle} from a {@code String} property in a {@link Properties} + * object. + * @param properties the source of the extra + * @param extraName key into the {@link Properties} object + * @return the {@link PersistableBundle} or {@code null} if there was no property with the + * given name + * @throws IOException if there was an error parsing the property + */ + private static PersistableBundle deserializeExtrasBundle( + Properties properties, String extraName) throws IOException { + String serializedExtras = properties.getProperty(extraName); + if (serializedExtras == null) { + return null; + } + Properties bundleProperties = new Properties(); + bundleProperties.load(new StringReader(serializedExtras)); + PersistableBundle extrasBundle = new PersistableBundle(bundleProperties.size()); + Set<String> propertyNames = bundleProperties.stringPropertyNames(); + for (String propertyName : propertyNames) { + extrasBundle.putString(propertyName, bundleProperties.getProperty(propertyName)); + } + return extrasBundle; + } + private static Intent createProvisioningIntentFromBundle(Bundle bundle) { requireNonNull(bundle); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 44dc28d2b0fa..52e64e80e6d4 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2618,6 +2618,15 @@ public class PackageParser { return Build.VERSION_CODES.CUR_DEVELOPMENT; } + // STOPSHIP: hack for the pre-release SDK + if (platformSdkCodenames.length == 0 + && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( + targetCode)) { + Slog.w(TAG, "Package requires development platform " + targetCode + + ", returning current version " + Build.VERSION.SDK_INT); + return Build.VERSION.SDK_INT; + } + // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { outError[0] = "Requires development platform " + targetCode @@ -2689,6 +2698,15 @@ public class PackageParser { return Build.VERSION_CODES.CUR_DEVELOPMENT; } + // STOPSHIP: hack for the pre-release SDK + if (platformSdkCodenames.length == 0 + && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( + minCode)) { + Slog.w(TAG, "Package requires min development platform " + minCode + + ", returning current version " + Build.VERSION.SDK_INT); + return Build.VERSION.SDK_INT; + } + // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { outError[0] = "Requires development platform " + minCode diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java index cb55e303e778..20a4fdf658c6 100644 --- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java +++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java @@ -567,9 +567,14 @@ public class ApkLiteParseUtils { targetCode = minCode; } + boolean allowUnknownCodenames = false; + if ((flags & FrameworkParsingPackageUtils.PARSE_APK_IN_APEX) != 0) { + allowUnknownCodenames = true; + } + ParseResult<Integer> targetResult = FrameworkParsingPackageUtils.computeTargetSdkVersion( targetVer, targetCode, SDK_CODENAMES, input, - /* allowUnknownCodenames= */ false); + allowUnknownCodenames); if (targetResult.isError()) { return input.error(targetResult); } diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java index 6d74b819301d..8cc4cdb955ca 100644 --- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java +++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java @@ -58,6 +58,7 @@ public class FrameworkParsingPackageUtils { private static final int MAX_FILE_NAME_SIZE = 223; public static final int PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY = 1 << 7; + public static final int PARSE_APK_IN_APEX = 1 << 9; /** * Check if the given name is valid. @@ -315,6 +316,15 @@ public class FrameworkParsingPackageUtils { return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); } + // STOPSHIP: hack for the pre-release SDK + if (platformSdkCodenames.length == 0 + && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( + minCode)) { + Slog.w(TAG, "Parsed package requires min development platform " + minCode + + ", returning current version " + Build.VERSION.SDK_INT); + return input.success(Build.VERSION.SDK_INT); + } + // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, @@ -367,16 +377,29 @@ public class FrameworkParsingPackageUtils { return input.success(targetVers); } - if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) { - return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); - } - // If it's a pre-release SDK and the codename matches this platform, it // definitely targets this SDK. if (matchTargetCode(platformSdkCodenames, targetCode)) { return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); } + // STOPSHIP: hack for the pre-release SDK + if (platformSdkCodenames.length == 0 + && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals( + targetCode)) { + Slog.w(TAG, "Parsed package requires development platform " + targetCode + + ", returning current version " + Build.VERSION.SDK_INT); + return input.success(Build.VERSION.SDK_INT); + } + + try { + if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) { + return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT); + } + } catch (IllegalArgumentException e) { + return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, "Bad package SDK"); + } + // Otherwise, we're looking at an incompatible pre-release SDK. if (platformSdkCodenames.length > 0) { return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 37f44e98c165..9a2f7baa7265 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -738,6 +738,14 @@ public final class DeviceConfig { */ public static final String NAMESPACE_VENDOR_SYSTEM_NATIVE = "vendor_system_native"; + /** + * Namespace for DevicePolicyManager related features. + * + * @hide + */ + public static final String NAMESPACE_DEVICE_POLICY_MANAGER = + "device_policy_manager"; + private static final Object sLock = new Object(); @GuardedBy("sLock") private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners = diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index dac54cf6146e..d2a86eb31870 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9695,6 +9695,40 @@ public final class Settings { "active_unlock_on_biometric_fail"; /** + * If active unlock triggers on biometric failures, include the following error codes + * as a biometric failure. See {@link android.hardware.biometrics.BiometricFaceConstants}. + * Error codes should be separated by a pipe. For example: "1|4|5". If active unlock + * should never trigger on any face errors, this should be set to an empty string. + * A null value will use the system default value (TIMEOUT). + * @hide + */ + public static final String ACTIVE_UNLOCK_ON_FACE_ERRORS = + "active_unlock_on_face_errors"; + + /** + * If active unlock triggers on biometric failures, include the following acquired info + * as a "biometric failure". See {@link android.hardware.biometrics.BiometricFaceConstants}. + * Acquired codes should be separated by a pipe. For example: "1|4|5". If active unlock + * should never on trigger on any acquired info messages, this should be + * set to an empty string. A null value will use the system default value (none). + * @hide + */ + public static final String ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO = + "active_unlock_on_face_acquire_info"; + + /** + * If active unlock triggers on biometric failures, then also request active unlock on + * unlock intent when each setting (BiometricType) is the only biometric type enrolled. + * Biometric types should be separated by a pipe. For example: "0|3" or "0". If this + * setting should be disabled, then this should be set to an empty string. A null value + * will use the system default value (0 / None). + * 0 = None, 1 = Any face, 2 = Any fingerprint, 3 = Under display fingerprint + * @hide + */ + public static final String ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED = + "active_unlock_on_unlock_intent_when_biometric_enrolled"; + + /** * Whether the assist gesture should be enabled. * * @hide @@ -10121,15 +10155,6 @@ public final class Settings { public static final String NOTIFICATION_DISMISS_RTL = "notification_dismiss_rtl"; /** - * Whether the app-level notification setting is represented by a manifest permission. - * - * @hide - */ - @Readable - public static final String NOTIFICATION_PERMISSION_ENABLED = - "notification_permission_enabled"; - - /** * Comma separated list of QS tiles that have been auto-added already. * @hide */ diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 5bc340b76f56..00052f6015d5 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -3108,10 +3108,14 @@ public interface WindowManager extends ViewManager { /** * The preferred refresh rate for the window. - * + * <p> * This must be one of the supported refresh rates obtained for the display(s) the window * is on. The selected refresh rate will be applied to the display's default mode. - * + * <p> + * This should be used in favor of {@link LayoutParams#preferredDisplayModeId} for + * applications that want to specify the refresh rate, but do not want to specify a + * preference for any other displayMode properties (e.g., resolution). + * <p> * This value is ignored if {@link #preferredDisplayModeId} is set. * * @see Display#getSupportedRefreshRates() diff --git a/core/java/android/window/WindowTokenClient.java b/core/java/android/window/WindowTokenClient.java index 7db4243d3a83..0976f45c02b0 100644 --- a/core/java/android/window/WindowTokenClient.java +++ b/core/java/android/window/WindowTokenClient.java @@ -21,8 +21,10 @@ import static android.window.ConfigurationHelper.shouldUpdateResources; import android.annotation.AnyThread; import android.annotation.BinderThread; +import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityThread; import android.app.IWindowToken; import android.app.ResourcesManager; import android.content.Context; @@ -33,7 +35,6 @@ import android.os.Bundle; import android.os.Debug; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.RemoteException; import android.util.Log; import android.view.IWindowManager; @@ -42,6 +43,7 @@ import android.view.WindowManagerGlobal; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.function.pooled.PooledLambda; import java.lang.ref.WeakReference; @@ -76,7 +78,7 @@ public class WindowTokenClient extends IWindowToken.Stub { private boolean mAttachToWindowContainer; - private final Handler mHandler = new Handler(Looper.getMainLooper()); + private final Handler mHandler = ActivityThread.currentActivityThread().getHandler(); /** * Attaches {@code context} to this {@link WindowTokenClient}. Each {@link WindowTokenClient} @@ -188,8 +190,8 @@ public class WindowTokenClient extends IWindowToken.Stub { @BinderThread @Override public void onConfigurationChanged(Configuration newConfig, int newDisplayId) { - mHandler.post(() -> onConfigurationChanged(newConfig, newDisplayId, - true /* shouldReportConfigChange */)); + mHandler.post(PooledLambda.obtainRunnable(this::onConfigurationChanged, newConfig, + newDisplayId, true /* shouldReportConfigChange */).recycleOnUse()); } // TODO(b/192048581): rewrite this method based on WindowContext and WindowProviderService @@ -279,12 +281,16 @@ public class WindowTokenClient extends IWindowToken.Stub { @BinderThread @Override public void onWindowTokenRemoved() { - mHandler.post(() -> { - final Context context = mContextRef.get(); - if (context != null) { - context.destroy(); - mContextRef.clear(); - } - }); + mHandler.post(PooledLambda.obtainRunnable( + WindowTokenClient::onWindowTokenRemovedInner, this).recycleOnUse()); + } + + @MainThread + private void onWindowTokenRemovedInner() { + final Context context = mContextRef.get(); + if (context != null) { + context.destroy(); + mContextRef.clear(); + } } } diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java index 957a6365739d..e56d92b48528 100644 --- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java +++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java @@ -91,7 +91,12 @@ public class UnlaunchableAppActivity extends Activity } else { builder.setPositiveButton(R.string.ok, null); } - builder.show(); + final AlertDialog dialog = builder.create(); + dialog.create(); + // Prevents screen overlay attack. + getWindow().setHideOverlayWindows(true); + dialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true); + dialog.show(); } private String getDialogTitle() { diff --git a/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java b/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java index 3eb980465214..5adaf4fbd2cc 100644 --- a/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java +++ b/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java @@ -28,18 +28,15 @@ import com.android.internal.R; public final class NotificationAccessConfirmationActivityContract { public static final String EXTRA_USER_ID = "user_id"; public static final String EXTRA_COMPONENT_NAME = "component_name"; - public static final String EXTRA_PACKAGE_TITLE = "package_title"; /** * Creates a launcher intent for NotificationAccessConfirmationActivity. */ - public static Intent launcherIntent(Context context, int userId, ComponentName component, - String packageTitle) { + public static Intent launcherIntent(Context context, int userId, ComponentName component) { return new Intent() .setComponent(ComponentName.unflattenFromString(context.getString( R.string.config_notificationAccessConfirmationActivity))) .putExtra(EXTRA_USER_ID, userId) - .putExtra(EXTRA_COMPONENT_NAME, component) - .putExtra(EXTRA_PACKAGE_TITLE, packageTitle); + .putExtra(EXTRA_COMPONENT_NAME, component); } } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 3f87de2e0f8a..b03a8cbeb79c 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -167,7 +167,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - static final int VERSION = 207; + static final int VERSION = 208; // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks @@ -3981,8 +3981,7 @@ public class BatteryStatsImpl extends BatteryStats { if (idxObj != null) { idx = idxObj; if ((idx & TAG_FIRST_OCCURRENCE_FLAG) != 0) { - idx &= ~TAG_FIRST_OCCURRENCE_FLAG; - mHistoryTagPool.put(tag, idx); + mHistoryTagPool.put(tag, idx & ~TAG_FIRST_OCCURRENCE_FLAG); } return idx; } else if (mNextHistoryTagIdx < HISTORY_TAG_INDEX_LIMIT) { diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 1db4bbba9ad5..ea5797d752d7 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -1262,7 +1262,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind } } - if (forceConsumingNavBar && !mDrawLegacyNavigationBarBackgroundHandled) { + if (forceConsumingNavBar && !hideNavigation && !mDrawLegacyNavigationBarBackgroundHandled) { mBackgroundInsets = Insets.of(mLastLeftInset, 0, mLastRightInset, mLastBottomInset); } else { mBackgroundInsets = Insets.NONE; diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index b6fbe206a262..f24c66695052 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -1264,6 +1264,12 @@ static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile, size_t numPositionMasks = 0; size_t numIndexMasks = 0; + int audioFormat = audioFormatFromNative(nAudioProfile->format); + if (audioFormat == ENCODING_INVALID) { + ALOGW("Unknown native audio format for JAVA API: %u", nAudioProfile->format); + return AUDIO_JAVA_BAD_VALUE; + } + // count up how many masks are positional and indexed for (size_t index = 0; index < nAudioProfile->num_channel_masks; index++) { const audio_channel_mask_t mask = nAudioProfile->channel_masks[index]; @@ -1306,10 +1312,9 @@ static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile, ALOGW("Unknown encapsulation type for JAVA API: %u", nAudioProfile->encapsulation_type); } - *jAudioProfile = - env->NewObject(gAudioProfileClass, gAudioProfileCstor, - audioFormatFromNative(nAudioProfile->format), jSamplingRates.get(), - jChannelMasks.get(), jChannelIndexMasks.get(), encapsulationType); + *jAudioProfile = env->NewObject(gAudioProfileClass, gAudioProfileCstor, audioFormat, + jSamplingRates.get(), jChannelMasks.get(), + jChannelIndexMasks.get(), encapsulationType); if (*jAudioProfile == nullptr) { return AUDIO_JAVA_ERROR; @@ -1368,6 +1373,10 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, jobject jAudioProfile = nullptr; jStatus = convertAudioProfileFromNative(env, &jAudioProfile, &nAudioPort->audio_profiles[i], useInMask); + if (jStatus == AUDIO_JAVA_BAD_VALUE) { + // skipping Java layer unsupported audio formats + continue; + } if (jStatus != NO_ERROR) { jStatus = (jint)AUDIO_JAVA_ERROR; goto exit; @@ -2406,8 +2415,13 @@ static jint android_media_AudioSystem_getSurroundFormats(JNIEnv *env, jobject th goto exit; } for (size_t i = 0; i < numSurroundFormats; i++) { - jobject surroundFormat = env->NewObject(gIntegerClass, gIntegerCstor, - audioFormatFromNative(surroundFormats[i])); + int audioFormat = audioFormatFromNative(surroundFormats[i]); + if (audioFormat == ENCODING_INVALID) { + // skipping Java layer unsupported audio formats + ALOGW("Unknown surround native audio format for JAVA API: %u", surroundFormats[i]); + continue; + } + jobject surroundFormat = env->NewObject(gIntegerClass, gIntegerCstor, audioFormat); jobject enabled = env->NewObject(gBooleanClass, gBooleanCstor, surroundFormatsEnabled[i]); env->CallObjectMethod(jSurroundFormats, gMapPut, surroundFormat, enabled); env->DeleteLocalRef(surroundFormat); @@ -2453,8 +2467,13 @@ static jint android_media_AudioSystem_getReportedSurroundFormats(JNIEnv *env, jo goto exit; } for (size_t i = 0; i < numSurroundFormats; i++) { - jobject surroundFormat = env->NewObject(gIntegerClass, gIntegerCstor, - audioFormatFromNative(surroundFormats[i])); + int audioFormat = audioFormatFromNative(surroundFormats[i]); + if (audioFormat == ENCODING_INVALID) { + // skipping Java layer unsupported audio formats + ALOGW("Unknown surround native audio format for JAVA API: %u", surroundFormats[i]); + continue; + } + jobject surroundFormat = env->NewObject(gIntegerClass, gIntegerCstor, audioFormat); env->CallObjectMethod(jSurroundFormats, gArrayListMethods.add, surroundFormat); env->DeleteLocalRef(surroundFormat); } @@ -2919,6 +2938,10 @@ static jint android_media_AudioSystem_getDirectProfilesForAttributes(JNIEnv *env for (const auto &audioProfile : audioProfiles) { jobject jAudioProfile; jStatus = convertAudioProfileFromNative(env, &jAudioProfile, &audioProfile, false); + if (jStatus == AUDIO_JAVA_BAD_VALUE) { + // skipping Java layer unsupported audio formats + continue; + } if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java index 2262c057d842..385879210d4a 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHistoryIteratorTest.java @@ -35,6 +35,7 @@ import java.io.File; @RunWith(AndroidJUnit4.class) @SmallTest +@SuppressWarnings("GuardedBy") public class BatteryStatsHistoryIteratorTest { private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; @@ -124,7 +125,10 @@ public class BatteryStatsHistoryIteratorTest { // More than 32k strings final int eventCount = 0x7FFF + 100; for (int i = 0; i < eventCount; i++) { - mBatteryStats.noteAlarmStartLocked("a" + i, null, APP_UID, 3_000_000, 2_000_000); + // Names repeat in order to verify de-duping of identical history tags. + String name = "a" + (i % 10); + mBatteryStats.noteAlarmStartLocked(name, null, APP_UID, 3_000_000, 2_000_000); + mBatteryStats.noteAlarmFinishLocked(name, null, APP_UID, 3_500_000, 2_500_000); } final BatteryStatsHistoryIterator iterator = @@ -149,10 +153,23 @@ public class BatteryStatsHistoryIteratorTest { assertThat(item.time).isEqualTo(2_000_000); for (int i = 0; i < eventCount; i++) { + String name = "a" + (i % 10); assertThat(iterator.next(item)).isTrue(); + // Skip a blank event inserted at the start of every buffer + if (item.eventCode == BatteryStats.HistoryItem.EVENT_NONE) { + assertThat(iterator.next(item)).isTrue(); + } assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_ALARM | BatteryStats.HistoryItem.EVENT_FLAG_START); - assertThat(item.eventTag.string).isEqualTo("a" + i); + assertThat(item.eventTag.string).isEqualTo(name); + + assertThat(iterator.next(item)).isTrue(); + if (item.eventCode == BatteryStats.HistoryItem.EVENT_NONE) { + assertThat(iterator.next(item)).isTrue(); + } + assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_ALARM + | BatteryStats.HistoryItem.EVENT_FLAG_FINISH); + assertThat(item.eventTag.string).isEqualTo(name); } assertThat(iterator.next(item)).isFalse(); diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 12d3d642a862..60da2e8cba27 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -2485,12 +2485,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "323235828": { - "message": "Delaying app transition for recents animation to finish", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, "327461496": { "message": "Complete pause: %s", "level": "VERBOSE", diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPrivateKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPrivateKey.java new file mode 100644 index 000000000000..4855ad0f7293 --- /dev/null +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPrivateKey.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 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 android.security.keystore2; + +import android.annotation.NonNull; +import android.security.KeyStoreSecurityLevel; +import android.system.keystore2.Authorization; +import android.system.keystore2.KeyDescriptor; + +import java.security.PrivateKey; +import java.security.interfaces.EdECKey; +import java.security.spec.NamedParameterSpec; + +/** + * EdEC private key (instance of {@link PrivateKey} and {@link EdECKey}) backed by keystore. + * + * @hide + */ +public class AndroidKeyStoreEdECPrivateKey extends AndroidKeyStorePrivateKey implements EdECKey { + public AndroidKeyStoreEdECPrivateKey( + @NonNull KeyDescriptor descriptor, long keyId, + @NonNull Authorization[] authorizations, + @NonNull String algorithm, + @NonNull KeyStoreSecurityLevel securityLevel) { + super(descriptor, keyId, authorizations, algorithm, securityLevel); + } + + @Override + public NamedParameterSpec getParams() { + return NamedParameterSpec.ED25519; + } +} diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPublicKey.java new file mode 100644 index 000000000000..642e08813291 --- /dev/null +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPublicKey.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2022 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 android.security.keystore2; + +import android.annotation.NonNull; +import android.security.KeyStoreSecurityLevel; +import android.system.keystore2.KeyDescriptor; +import android.system.keystore2.KeyMetadata; + +import java.math.BigInteger; +import java.security.interfaces.EdECPublicKey; +import java.security.spec.EdECPoint; +import java.security.spec.NamedParameterSpec; +import java.util.Arrays; +import java.util.Objects; + +/** + * {@link EdECPublicKey} backed by keystore. + * + * @hide + */ +public class AndroidKeyStoreEdECPublicKey extends AndroidKeyStorePublicKey + implements EdECPublicKey { + /** + * DER sequence, as defined in https://datatracker.ietf.org/doc/html/rfc8410#section-4 and + * https://datatracker.ietf.org/doc/html/rfc5280#section-4.1. + * SEQUENCE (2 elem) + * SEQUENCE (1 elem) + * OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm) + * as defined in https://datatracker.ietf.org/doc/html/rfc8410#section-3 + * BIT STRING (256 bit) as defined in + * https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.2 + */ + private static final byte[] DER_KEY_PREFIX = new byte[] { + 0x30, + 0x2a, + 0x30, + 0x05, + 0x06, + 0x03, + 0x2b, + 0x65, + 0x70, + 0x03, + 0x21, + 0x00, + }; + private static final int ED25519_KEY_SIZE_BYTES = 32; + + private byte[] mEncodedKey; + private EdECPoint mPoint; + + public AndroidKeyStoreEdECPublicKey( + @NonNull KeyDescriptor descriptor, + @NonNull KeyMetadata metadata, + @NonNull String algorithm, + @NonNull KeyStoreSecurityLevel iSecurityLevel, + @NonNull byte[] encodedKey) { + super(descriptor, metadata, encodedKey, algorithm, iSecurityLevel); + mEncodedKey = encodedKey; + + int preambleLength = matchesPreamble(DER_KEY_PREFIX, encodedKey); + if (preambleLength == 0) { + throw new IllegalArgumentException("Key size is not correct size"); + } + + mPoint = pointFromKeyByteArray( + Arrays.copyOfRange(encodedKey, preambleLength, encodedKey.length)); + } + + @Override + AndroidKeyStorePrivateKey getPrivateKey() { + return new AndroidKeyStoreEdECPrivateKey( + getUserKeyDescriptor(), + getKeyIdDescriptor().nspace, + getAuthorizations(), + "EdDSA", + getSecurityLevel()); + } + + @Override + public NamedParameterSpec getParams() { + return NamedParameterSpec.ED25519; + } + + @Override + public EdECPoint getPoint() { + return mPoint; + } + + private static int matchesPreamble(byte[] preamble, byte[] encoded) { + if (encoded.length != (preamble.length + ED25519_KEY_SIZE_BYTES)) { + return 0; + } + if (Arrays.compare(preamble, Arrays.copyOf(encoded, preamble.length)) != 0) { + return 0; + } + return preamble.length; + } + + private static EdECPoint pointFromKeyByteArray(byte[] coordinates) { + Objects.requireNonNull(coordinates); + + // Oddity of the key is the most-significant bit of the last byte. + boolean isOdd = (0x80 & coordinates[coordinates.length - 1]) != 0; + // Zero out the oddity bit. + coordinates[coordinates.length - 1] &= (byte) 0x7f; + // Representation of Y is in little-endian, according to rfc8032 section-3.1. + reverse(coordinates); + // The integer representing Y starts from the first bit in the coordinates array. + BigInteger y = new BigInteger(1, coordinates); + return new EdECPoint(isOdd, y); + } + + private static void reverse(byte[] coordinateArray) { + int start = 0; + int end = coordinateArray.length - 1; + while (start < end) { + byte tmp = coordinateArray[start]; + coordinateArray[start] = coordinateArray[end]; + coordinateArray[end] = tmp; + start++; + end--; + } + } + + @Override + public byte[] getEncoded() { + return mEncodedKey.clone(); + } +} diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index d31499e8b36d..0355628b8135 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -224,7 +224,6 @@ public class AndroidKeyStoreProvider extends Provider { String jcaKeyAlgorithm = publicKey.getAlgorithm(); - KeyStoreSecurityLevel securityLevel = iSecurityLevel; if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(jcaKeyAlgorithm)) { return new AndroidKeyStoreECPublicKey(descriptor, metadata, iSecurityLevel, (ECPublicKey) publicKey); @@ -232,8 +231,9 @@ public class AndroidKeyStoreProvider extends Provider { return new AndroidKeyStoreRSAPublicKey(descriptor, metadata, iSecurityLevel, (RSAPublicKey) publicKey); } else if (ED25519_OID.equalsIgnoreCase(jcaKeyAlgorithm)) { - //TODO(b/214203951) missing classes in conscrypt - throw new ProviderException("Curve " + ED25519_OID + " not supported yet"); + final byte[] publicKeyEncoded = publicKey.getEncoded(); + return new AndroidKeyStoreEdECPublicKey(descriptor, metadata, ED25519_OID, + iSecurityLevel, publicKeyEncoded); } else if (X25519_ALIAS.equalsIgnoreCase(jcaKeyAlgorithm)) { //TODO(b/214203951) missing classes in conscrypt throw new ProviderException("Curve " + X25519_ALIAS + " not supported yet"); diff --git a/keystore/tests/src/android/security/keystore2/AndroidKeyStoreEdECPublicKeyTest.java b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreEdECPublicKeyTest.java new file mode 100644 index 000000000000..5bd5797859c9 --- /dev/null +++ b/keystore/tests/src/android/security/keystore2/AndroidKeyStoreEdECPublicKeyTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2022 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 android.security.keystore2; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import android.security.KeyStoreSecurityLevel; +import android.system.keystore2.Authorization; +import android.system.keystore2.Domain; +import android.system.keystore2.KeyDescriptor; +import android.system.keystore2.KeyMetadata; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +import java.math.BigInteger; +import java.util.Base64; + +@RunWith(AndroidJUnit4.class) +public class AndroidKeyStoreEdECPublicKeyTest { + private static KeyDescriptor descriptor() { + final KeyDescriptor keyDescriptor = new KeyDescriptor(); + keyDescriptor.alias = "key"; + keyDescriptor.blob = null; + keyDescriptor.domain = Domain.APP; + keyDescriptor.nspace = -1; + return keyDescriptor; + } + + private static KeyMetadata metadata(byte[] cert, byte[] certChain) { + KeyMetadata metadata = new KeyMetadata(); + metadata.authorizations = new Authorization[0]; + metadata.certificate = cert; + metadata.certificateChain = certChain; + metadata.key = descriptor(); + metadata.modificationTimeMs = 0; + metadata.keySecurityLevel = 1; + return metadata; + } + + @Mock + private KeyStoreSecurityLevel mKeystoreSecurityLevel; + + private static class EdECTestVector { + public final byte[] encodedKeyBytes; + public final boolean isOdd; + public final BigInteger yValue; + + EdECTestVector(String b64KeyBytes, boolean isOdd, String yValue) { + this.encodedKeyBytes = Base64.getDecoder().decode(b64KeyBytes); + this.isOdd = isOdd; + this.yValue = new BigInteger(yValue); + } + } + + private static final EdECTestVector[] ED_EC_TEST_VECTORS = new EdECTestVector[]{ + new EdECTestVector("MCowBQYDK2VwAyEADE+wvQqNHxaERPhAZ0rCFlgFbfWLs/YonPXdSTw0VSo=", + false, + "19147682157189290216699341180089409126316261024914226007941553249095116672780" + ), + new EdECTestVector("MCowBQYDK2VwAyEA/0E1IRNzGj85Ot/TPeXqifkqTkdk4voleH0hIq59D9w=", + true, + "41640152188550647350742178040529506688513911269563908889464821205156322689535" + ), + new EdECTestVector("MCowBQYDK2VwAyEAunOvGuenetl9GQSXGVo5L3RIr4OOIpFIv/Zre8qTc/8=", + true, + "57647939198144376128225770417635248407428273266444593100194116168980378907578" + ), + new EdECTestVector("MCowBQYDK2VwAyEA2hHqaZ5IolswN1Yd58Y4hzhmUMCCqc4PW5A/SFLmTX8=", + false, + "57581368614046789120409806291852629847774713088410311752049592044694364885466" + ), + }; + + @Test + public void testParsingOfValidKeys() { + for (EdECTestVector testVector : ED_EC_TEST_VECTORS) { + AndroidKeyStoreEdECPublicKey pkey = new AndroidKeyStoreEdECPublicKey(descriptor(), + metadata(null, null), "EdDSA", mKeystoreSecurityLevel, + testVector.encodedKeyBytes); + + assertEquals(pkey.getPoint().isXOdd(), testVector.isOdd); + assertEquals(pkey.getPoint().getY(), testVector.yValue); + } + } + + @Test + public void testFailedParsingOfKeysWithDifferentOid() { + final byte[] testVectorWithIncorrectOid = Base64.getDecoder().decode( + "MCowBQYDLGVwAyEADE+wvQqNHxaERPhAZ0rCFlgFbfWLs/YonPXdSTw0VSo="); + assertThrows("OID should be unrecognized", IllegalArgumentException.class, + () -> new AndroidKeyStoreEdECPublicKey(descriptor(), metadata(null, null), "EdDSA", + mKeystoreSecurityLevel, testVectorWithIncorrectOid)); + } + + @Test + public void testFailedParsingOfKeysWithWrongSize() { + final byte[] testVectorWithIncorrectKeySize = Base64.getDecoder().decode( + "MCwwBQYDK2VwAyMADE+wvQqNHxaERPhAZ0rCFlgFbfWLs/YonPXdSTw0VSrOzg=="); + assertThrows("Key length should be invalid", IllegalArgumentException.class, + () -> new AndroidKeyStoreEdECPublicKey(descriptor(), metadata(null, null), "EdDSA", + mKeystoreSecurityLevel, testVectorWithIncorrectKeySize)); + } +} + diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java index 64017e176fc3..d04c34916256 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java @@ -40,6 +40,8 @@ public enum ShellProtoLogGroup implements IProtoLogGroup { Consts.TAG_WM_SHELL), WM_SHELL_PICTURE_IN_PICTURE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM_SHELL), + WM_SHELL_SPLIT_SCREEN(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + Consts.TAG_WM_SHELL), TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest"); private final boolean mEnabled; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 91f9d2522397..d543aa742377 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -54,6 +54,9 @@ import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_P import static com.android.wm.shell.transition.Transitions.isClosingType; import static com.android.wm.shell.transition.Transitions.isOpeningType; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; @@ -68,6 +71,7 @@ import android.content.res.Configuration; import android.graphics.Rect; import android.hardware.devicestate.DeviceStateManager; import android.os.Bundle; +import android.os.Debug; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -147,7 +151,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, private final int mDisplayId; private SplitLayout mSplitLayout; + private ValueAnimator mDividerFadeInAnimator; private boolean mDividerVisible; + private boolean mKeyguardShowing; private final SyncTransactionQueue mSyncQueue; private final ShellTaskOrganizer mTaskOrganizer; private final Context mContext; @@ -404,6 +410,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.init(); // Set false to avoid record new bounds with old task still on top; mShouldUpdateRecents = false; + mIsDividerRemoteAnimating = true; final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction evictWct = new WindowContainerTransaction(); prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct); @@ -417,7 +424,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, final IRemoteAnimationFinishedCallback finishedCallback) { - mIsDividerRemoteAnimating = true; RemoteAnimationTarget[] augmentedNonApps = new RemoteAnimationTarget[nonApps.length + 1]; for (int i = 0; i < nonApps.length; ++i) { @@ -494,8 +500,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } // Using legacy transitions, so we can't use blast sync since it conflicts. mTaskOrganizer.applyTransaction(wct); - mSyncQueue.runInSync(t -> - updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */)); + mSyncQueue.runInSync(t -> { + setDividerVisibility(true, t); + updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); + }); } private void onRemoteAnimationFinishedOrCancelled(WindowContainerTransaction evictWct) { @@ -510,10 +518,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN)); } else { mSyncQueue.queue(evictWct); - mSyncQueue.runInSync(t -> { - setDividerVisibility(true, t); - updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); - }); } } @@ -623,16 +627,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } void onKeyguardVisibilityChanged(boolean showing) { + mKeyguardShowing = showing; if (!mMainStage.isActive()) { return; } - if (ENABLE_SHELL_TRANSITIONS) { - // Update divider visibility so it won't float on top of keyguard. - setDividerVisibility(!showing, null /* transaction */); - } - - if (!showing && mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) { + if (!mKeyguardShowing && mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) { if (ENABLE_SHELL_TRANSITIONS) { final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct); @@ -643,7 +643,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage, EXIT_REASON_DEVICE_FOLDED); } + return; } + + setDividerVisibility(!mKeyguardShowing, null); } void onFinishedWakingUp() { @@ -727,6 +730,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, setResizingSplits(false /* resizing */); t.setWindowCrop(mMainStage.mRootLeash, null) .setWindowCrop(mSideStage.mRootLeash, null); + setDividerVisibility(false, t); }); // Hide divider and reset its position. @@ -1055,8 +1059,31 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } private void setDividerVisibility(boolean visible, @Nullable SurfaceControl.Transaction t) { + if (visible == mDividerVisible) { + return; + } + + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "%s: Request to %s divider bar from %s.", TAG, + (visible ? "show" : "hide"), Debug.getCaller()); + + // Defer showing divider bar after keyguard dismissed, so it won't interfere with keyguard + // dismissing animation. + if (visible && mKeyguardShowing) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "%s: Defer showing divider bar due to keyguard showing.", TAG); + return; + } + mDividerVisible = visible; sendSplitVisibilityChanged(); + + if (mIsDividerRemoteAnimating) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "%s: Skip animating divider bar due to it's remote animating.", TAG); + return; + } + if (t != null) { applyDividerVisibility(t); } else { @@ -1066,15 +1093,56 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void applyDividerVisibility(SurfaceControl.Transaction t) { final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); - if (mIsDividerRemoteAnimating || dividerLeash == null) return; + if (dividerLeash == null) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "%s: Skip animating divider bar due to divider leash not ready.", TAG); + return; + } + if (mIsDividerRemoteAnimating) { + ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "%s: Skip animating divider bar due to it's remote animating.", TAG); + return; + } + + if (mDividerFadeInAnimator != null && mDividerFadeInAnimator.isRunning()) { + mDividerFadeInAnimator.cancel(); + } if (mDividerVisible) { - t.show(dividerLeash); - t.setAlpha(dividerLeash, 1); - t.setLayer(dividerLeash, Integer.MAX_VALUE); - t.setPosition(dividerLeash, - mSplitLayout.getRefDividerBounds().left, - mSplitLayout.getRefDividerBounds().top); + final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); + mDividerFadeInAnimator = ValueAnimator.ofFloat(0f, 1f); + mDividerFadeInAnimator.addUpdateListener(animation -> { + if (dividerLeash == null) { + mDividerFadeInAnimator.cancel(); + return; + } + transaction.setAlpha(dividerLeash, (float) animation.getAnimatedValue()); + transaction.apply(); + }); + mDividerFadeInAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + if (dividerLeash == null) { + mDividerFadeInAnimator.cancel(); + return; + } + transaction.show(dividerLeash); + transaction.setAlpha(dividerLeash, 0); + transaction.setLayer(dividerLeash, Integer.MAX_VALUE); + transaction.setPosition(dividerLeash, + mSplitLayout.getRefDividerBounds().left, + mSplitLayout.getRefDividerBounds().top); + transaction.apply(); + } + + @Override + public void onAnimationEnd(Animator animation) { + mTransactionPool.release(transaction); + mDividerFadeInAnimator = null; + } + }); + + mDividerFadeInAnimator.start(); } else { t.hide(dividerLeash); } @@ -1096,10 +1164,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.init(); prepareEnterSplitScreen(wct); mSyncQueue.queue(wct); - mSyncQueue.runInSync(t -> { - updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */); - setDividerVisibility(true, t); - }); + mSyncQueue.runInSync(t -> + updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */)); } if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) { mShouldUpdateRecents = true; diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt index cf4ea467a29b..41cd31aabf05 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt @@ -37,7 +37,7 @@ class AppPairsHelper( val displayBounds = WindowUtils.displayBounds val secondaryAppBounds = Region.from(0, dividerBounds.bounds.bottom - WindowUtils.dockedStackDividerInset, - displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarHeight) + displayBounds.right, displayBounds.bottom - WindowUtils.navigationBarFrameHeight) return secondaryAppBounds } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt index a510d699387e..e2da1a4565c0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt @@ -171,7 +171,7 @@ class ResizeLegacySplitScreen( val bottomAppBounds = Region.from(0, dividerBounds.bottom - WindowUtils.dockedStackDividerInset, displayBounds.right, - displayBounds.bottom - WindowUtils.navigationBarHeight) + displayBounds.bottom - WindowUtils.navigationBarFrameHeight) visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent()) .coversExactly(topAppBounds) visibleRegion(Components.ImeActivity.COMPONENT.toFlickerComponent()) @@ -192,7 +192,7 @@ class ResizeLegacySplitScreen( val bottomAppBounds = Region.from(0, dividerBounds.bottom - WindowUtils.dockedStackDividerInset, displayBounds.right, - displayBounds.bottom - WindowUtils.navigationBarHeight) + displayBounds.bottom - WindowUtils.navigationBarFrameHeight) visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent()) .coversExactly(topAppBounds) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index a55f737f2f25..ffaab652aa99 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -139,6 +139,7 @@ public class SplitTransitionTests extends ShellTestCase { } @Test + @UiThreadTest public void testLaunchToSide() { ActivityManager.RunningTaskInfo newTask = new TestRunningTaskInfoBuilder() .setParentTaskId(mSideStage.mRootTaskInfo.taskId).build(); @@ -173,6 +174,7 @@ public class SplitTransitionTests extends ShellTestCase { } @Test + @UiThreadTest public void testLaunchPair() { TransitionInfo info = createEnterPairInfo(); @@ -195,6 +197,7 @@ public class SplitTransitionTests extends ShellTestCase { } @Test + @UiThreadTest public void testMonitorInSplit() { enterSplit(); @@ -251,6 +254,7 @@ public class SplitTransitionTests extends ShellTestCase { } @Test + @UiThreadTest public void testEnterRecents() { enterSplit(); @@ -288,6 +292,7 @@ public class SplitTransitionTests extends ShellTestCase { } @Test + @UiThreadTest public void testDismissFromBeingOccluded() { enterSplit(); @@ -325,6 +330,7 @@ public class SplitTransitionTests extends ShellTestCase { } @Test + @UiThreadTest public void testDismissFromMultiWindowSupport() { enterSplit(); @@ -346,6 +352,7 @@ public class SplitTransitionTests extends ShellTestCase { } @Test + @UiThreadTest public void testDismissSnap() { enterSplit(); @@ -370,6 +377,7 @@ public class SplitTransitionTests extends ShellTestCase { } @Test + @UiThreadTest public void testDismissFromAppFinish() { enterSplit(); diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index ef0270b5414c..e5673a613b59 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -1728,7 +1728,9 @@ public class Tuner implements AutoCloseable { } int res = nativeSetMaxNumberOfFrontends(frontendType, maxNumber); if (res == RESULT_SUCCESS) { - // TODO: b/211778848 Update Tuner Resource Manager. + if (!mTunerResourceManager.setMaxNumberOfFrontends(frontendType, maxNumber)) { + res = RESULT_INVALID_ARGUMENT; + } } return res; } @@ -1749,7 +1751,13 @@ public class Tuner implements AutoCloseable { TunerVersionChecker.TUNER_VERSION_2_0, "Set maximum Frontends")) { return -1; } - return nativeGetMaxNumberOfFrontends(frontendType); + int maxNumFromHAL = nativeGetMaxNumberOfFrontends(frontendType); + int maxNumFromTRM = mTunerResourceManager.getMaxNumberOfFrontends(frontendType); + if (maxNumFromHAL != maxNumFromTRM) { + Log.w(TAG, "max num of usable frontend is out-of-sync b/w " + maxNumFromHAL + + " != " + maxNumFromTRM); + } + return maxNumFromHAL; } /** @hide */ diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java index 5ada89e9dea7..15175a783924 100644 --- a/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java +++ b/media/java/android/media/tv/tunerresourcemanager/TunerResourceManager.java @@ -401,6 +401,43 @@ public class TunerResourceManager { } /** + * Sets the maximum usable frontends number of a given frontend type. It is used to enable or + * disable frontends when cable connection status is changed by user. + * + * @param frontendType the frontendType which the maximum usable number will be set for. + * @param maxNum the new maximum usable number. + * + * @return true if successful and false otherwise. + */ + public boolean setMaxNumberOfFrontends(int frontendType, int maxNum) { + boolean result = false; + try { + result = mService.setMaxNumberOfFrontends(frontendType, maxNum); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return result; + } + + /** + * Get the maximum usable frontends number of a given frontend type. + * + * @param frontendType the frontendType which the maximum usable number will be queried for. + * + * @return the maximum usable number of the queried frontend type. Returns -1 when the + * frontendType is invalid + */ + public int getMaxNumberOfFrontends(int frontendType) { + int result = -1; + try { + result = mService.getMaxNumberOfFrontends(frontendType); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return result; + } + + /** * Requests from the client to share frontend with an existing client. * * <p><strong>Note:</strong> {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called diff --git a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl index d16fc6ca1dc7..144b98c36655 100644 --- a/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl +++ b/media/java/android/media/tv/tunerresourcemanager/aidl/android/media/tv/tunerresourcemanager/ITunerResourceManager.aidl @@ -166,6 +166,27 @@ interface ITunerResourceManager { boolean requestFrontend(in TunerFrontendRequest request, out int[] frontendHandle); /* + * Sets the maximum usable frontends number of a given frontend type. It is used to enable or + * disable frontends when cable connection status is changed by user. + * + * @param frontendType the frontendType which the maximum usable number will be set for. + * @param maxNumber the new maximum usable number. + * + * @return true if successful and false otherwise. + */ + boolean setMaxNumberOfFrontends(in int frontendType, in int maxNum); + + /* + * Get the maximum usable frontends number of a given frontend type. + * + * @param frontendType the frontendType which the maximum usable number will be queried for. + * + * @return the maximum usable number of the queried frontend type. Returns -1 when the + * frontendType is invalid + */ + int getMaxNumberOfFrontends(in int frontendType); + + /* * Requests to share frontend with an existing client. * * <p><strong>Note:</strong> {@link #setFrontendInfoList(TunerFrontendInfo[])} must be called diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java index 05a143e7e38a..9e9ec04fbd93 100644 --- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java +++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java @@ -487,6 +487,14 @@ public class CompanionDeviceActivity extends FragmentActivity implements if (deviceFilterPairs.isEmpty()) return; mSelectedDevice = requireNonNull(deviceFilterPairs.get(0)); + // No need to show user consent dialog if it is a singleDevice + // and isSkipPrompt(true) AssociationRequest. + // See AssociationRequestsProcessor#mayAssociateWithoutPrompt. + if (mRequest.isSkipPrompt()) { + mSingleDeviceSpinner.setVisibility(View.GONE); + onUserSelectedDevice(mSelectedDevice); + return; + } final String deviceName = mSelectedDevice.getDisplayName(); final Spanned title = getHtmlFromResources( diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml index 25f0771b2170..72b569f22d6c 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v31/collapsing_toolbar_content_layout.xml @@ -25,7 +25,7 @@ android:fitsSystemWindows="true" android:outlineAmbientShadowColor="@android:color/transparent" android:outlineSpotShadowColor="@android:color/transparent" - android:background="?android:attr/colorPrimary" + android:background="@android:color/transparent" android:theme="@style/Theme.CollapsingToolbar.Settings"> <com.google.android.material.appbar.CollapsingToolbarLayout diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java index 72383fe59e7e..dbb4b5017e6b 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java @@ -20,6 +20,7 @@ import android.app.ActionBar; import android.app.Activity; import android.content.Context; import android.content.res.TypedArray; +import android.os.Build; import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; @@ -29,6 +30,7 @@ import android.widget.Toolbar; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import androidx.coordinatorlayout.widget.CoordinatorLayout; @@ -41,6 +43,7 @@ import com.google.android.material.appbar.CollapsingToolbarLayout; * This widget is wrapping the collapsing toolbar and can be directly used by the * {@link AppCompatActivity}. */ +@RequiresApi(Build.VERSION_CODES.S) public class CollapsingCoordinatorLayout extends CoordinatorLayout { private static final String TAG = "CollapsingCoordinatorLayout"; private static final float TOOLBAR_LINE_SPACING_MULTIPLIER = 1.1f; diff --git a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java index ac306361386e..6766cdd0beb6 100644 --- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java +++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java @@ -40,6 +40,8 @@ public class FooterPreference extends Preference { static final int ORDER_FOOTER = Integer.MAX_VALUE - 1; @VisibleForTesting View.OnClickListener mLearnMoreListener; + @VisibleForTesting + int mIconVisibility = View.VISIBLE; private CharSequence mContentDescription; private CharSequence mLearnMoreText; private CharSequence mLearnMoreContentDescription; @@ -84,6 +86,9 @@ public class FooterPreference extends Preference { } else { learnMore.setVisibility(View.GONE); } + + View icon = holder.itemView.findViewById(R.id.icon_frame); + icon.setVisibility(mIconVisibility); } @Override @@ -165,6 +170,17 @@ public class FooterPreference extends Preference { } } + /** + * Set visibility of footer icon. + */ + public void setIconVisibility(int iconVisibility) { + if (mIconVisibility == iconVisibility) { + return; + } + mIconVisibility = iconVisibility; + notifyChanged(); + } + private void init() { setLayoutResource(R.layout.preference_footer); if (getIcon() == null) { diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java index fdb06072bbd1..6b9daa35f9ca 100644 --- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java +++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java @@ -863,6 +863,30 @@ public class ApplicationsState { } } + /** + * Activate session to enable a class that implements Callbacks to receive the callback. + */ + public void activateSession() { + synchronized (mEntriesMap) { + if (!mResumed) { + mResumed = true; + mSessionsChanged = true; + } + } + } + + /** + * Deactivate session to disable a class that implements Callbacks to get the callback. + */ + public void deactivateSession() { + synchronized (mEntriesMap) { + if (mResumed) { + mResumed = false; + mSessionsChanged = true; + } + } + } + public ArrayList<AppEntry> getAllApps() { synchronized (mEntriesMap) { return new ArrayList<>(mAppEntries); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 4ee21229e364..6919cf237853 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -1376,7 +1376,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> /** * Store the member devices that are in the same coordinated set. */ - public void setMemberDevice(CachedBluetoothDevice memberDevice) { + public void addMemberDevice(CachedBluetoothDevice memberDevice) { mMemberDevices.add(memberDevice); } @@ -1393,24 +1393,24 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> * device and member devices. * * @param prevMainDevice the previous Main device, it will be added into the member device set. - * @param newMainDevie the new Main device, it will be removed from the member device set. + * @param newMainDevice the new Main device, it will be removed from the member device set. */ public void switchMemberDeviceContent(CachedBluetoothDevice prevMainDevice, - CachedBluetoothDevice newMainDevie) { + CachedBluetoothDevice newMainDevice) { // Backup from main device final BluetoothDevice tmpDevice = mDevice; final short tmpRssi = mRssi; final boolean tmpJustDiscovered = mJustDiscovered; // Set main device from sub device - mDevice = newMainDevie.mDevice; - mRssi = newMainDevie.mRssi; - mJustDiscovered = newMainDevie.mJustDiscovered; - setMemberDevice(prevMainDevice); - mMemberDevices.remove(newMainDevie); + mDevice = newMainDevice.mDevice; + mRssi = newMainDevice.mRssi; + mJustDiscovered = newMainDevice.mJustDiscovered; + addMemberDevice(prevMainDevice); + mMemberDevices.remove(newMainDevice); // Set sub device from backup - newMainDevie.mDevice = tmpDevice; - newMainDevie.mRssi = tmpRssi; - newMainDevie.mJustDiscovered = tmpJustDiscovered; + newMainDevice.mDevice = tmpDevice; + newMainDevice.mRssi = tmpRssi; + newMainDevice.mJustDiscovered = tmpJustDiscovered; fetchActiveDevices(); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java index cc56a212aea1..89e10c4b5e11 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java @@ -85,7 +85,7 @@ public class CsipDeviceManager { // Once there is other devices with the same groupId, to add new device as member // devices. if (CsipDevice != null) { - CsipDevice.setMemberDevice(newDevice); + CsipDevice.addMemberDevice(newDevice); newDevice.setName(CsipDevice.getName()); return true; } @@ -148,7 +148,7 @@ public class CsipDeviceManager { log("onGroupIdChanged: removed from UI device =" + cachedDevice + ", with groupId=" + groupId + " firstMatchedIndex=" + firstMatchedIndex); - mainDevice.setMemberDevice(cachedDevice); + mainDevice.addMemberDevice(cachedDevice); mCachedDevices.remove(i); mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice); break; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java index 298ee90d311d..bef1d9cbcde1 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java @@ -40,7 +40,6 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import java.util.Collection; -import java.util.HashMap; import java.util.Map; @RunWith(RobolectricTestRunner.class) @@ -503,8 +502,8 @@ public class CachedBluetoothDeviceManagerTest { CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2); CachedBluetoothDevice cachedDevice3 = mCachedDeviceManager.addDevice(mDevice3); - cachedDevice1.setMemberDevice(cachedDevice2); - cachedDevice1.setMemberDevice(cachedDevice3); + cachedDevice1.addMemberDevice(cachedDevice2); + cachedDevice1.addMemberDevice(cachedDevice3); assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice2); assertThat(cachedDevice1.getMemberDevice()).contains(cachedDevice3); @@ -524,7 +523,7 @@ public class CachedBluetoothDeviceManagerTest { CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2); cachedDevice1.setGroupId(1); cachedDevice2.setGroupId(1); - cachedDevice1.setMemberDevice(cachedDevice2); + cachedDevice1.addMemberDevice(cachedDevice2); // Call onDeviceUnpaired for the one in mCachedDevices. mCachedDeviceManager.onDeviceUnpaired(cachedDevice1); @@ -541,7 +540,7 @@ public class CachedBluetoothDeviceManagerTest { CachedBluetoothDevice cachedDevice2 = mCachedDeviceManager.addDevice(mDevice2); cachedDevice1.setGroupId(1); cachedDevice2.setGroupId(1); - cachedDevice1.setMemberDevice(cachedDevice2); + cachedDevice1.addMemberDevice(cachedDevice2); // Call onDeviceUnpaired for the one in mCachedDevices. mCachedDeviceManager.onDeviceUnpaired(cachedDevice2); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java index 61a28aab061f..9abb27e68398 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import android.content.Context; import android.view.LayoutInflater; +import android.view.View; import android.widget.TextView; import androidx.preference.PreferenceViewHolder; @@ -61,7 +62,7 @@ public class FooterPreferenceTest { mFooterPreference.onBindViewHolder(holder); assertThat(((TextView) holder.findViewById( - R.id.settingslib_learn_more)).getText().toString()) + R.id.settingslib_learn_more)).getText().toString()) .isEqualTo("Custom learn more"); } @@ -86,4 +87,11 @@ public class FooterPreferenceTest { assertThat(mFooterPreference.mLearnMoreListener).isNotNull(); } + + @Test + public void setIconVisibility_shouldReturnSameVisibilityType() { + mFooterPreference.setIconVisibility(View.GONE); + + assertThat(mFooterPreference.mIconVisibility).isEqualTo(View.GONE); + } } diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 3029781f3e99..5eaf553a2047 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -120,6 +120,9 @@ public class SecureSettings { Settings.Secure.ACTIVE_UNLOCK_ON_WAKE, Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT, Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, + Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS, + Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO, + Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, Settings.Secure.VR_DISPLAY_MODE, Settings.Secure.NOTIFICATION_BADGING, Settings.Secure.NOTIFICATION_DISMISS_RTL, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index a4da49713f87..9ee7b654046f 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -18,6 +18,7 @@ package android.provider.settings.validators; import static android.provider.settings.validators.SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR; +import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_COMPONENT_LIST_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_PACKAGE_LIST_VALIDATOR; @@ -176,6 +177,10 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_WAKE, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS, ANY_STRING_VALIDATOR); + VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO, ANY_STRING_VALIDATOR); + VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, + ANY_STRING_VALIDATOR); VALIDATORS.put(Secure.ASSIST_GESTURE_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ASSIST_GESTURE_WAKE_ENABLED, BOOLEAN_VALIDATOR); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index aadfcea150f7..a6edb0f0e2e3 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -5509,16 +5509,7 @@ public class SettingsProvider extends ContentProvider { currentVersion = 209; } if (currentVersion == 209) { - // Version 209: Enable enforcement of - // android.Manifest.permission#POST_NOTIFICATIONS in order for applications - // to post notifications. - final SettingsState secureSettings = getSecureSettingsLocked(userId); - secureSettings.insertSettingLocked( - Secure.NOTIFICATION_PERMISSION_ENABLED, - /* enabled= */ "1", - /* tag= */ null, - /* makeDefault= */ false, - SettingsState.SYSTEM_PACKAGE_NAME); + // removed now that feature is enabled for everyone currentVersion = 210; } diff --git a/packages/SystemUI/res/drawable/media_output_icon_volume.xml b/packages/SystemUI/res/drawable/media_output_icon_volume.xml new file mode 100644 index 000000000000..fce4e0022c7a --- /dev/null +++ b/packages/SystemUI/res/drawable/media_output_icon_volume.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@color/media_dialog_item_main_content" + android:pathData="M14,20.725V18.675Q16.25,18.025 17.625,16.175Q19,14.325 19,11.975Q19,9.625 17.625,7.775Q16.25,5.925 14,5.275V3.225Q17.1,3.925 19.05,6.362Q21,8.8 21,11.975Q21,15.15 19.05,17.587Q17.1,20.025 14,20.725ZM3,15V9H7L12,4V20L7,15ZM14,16V7.95Q15.125,8.475 15.812,9.575Q16.5,10.675 16.5,12Q16.5,13.325 15.812,14.4Q15.125,15.475 14,16ZM10,8.85 L7.85,11H5V13H7.85L10,15.15ZM7.5,12Z"/> +</vector> diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml index 10bb6cbb95aa..085a5810608f 100644 --- a/packages/SystemUI/res/layout/clipboard_overlay.xml +++ b/packages/SystemUI/res/layout/clipboard_overlay.xml @@ -67,7 +67,7 @@ android:layout_width="0dp" android:layout_height="0dp" android:layout_marginStart="@dimen/overlay_offset_x" - android:layout_marginBottom="@dimen/overlay_offset_y" + android:layout_marginBottom="8dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toBottomOf="@id/actions_container_background" android:elevation="7dp" diff --git a/packages/SystemUI/res/layout/screenshot.xml b/packages/SystemUI/res/layout/screenshot.xml index 890dbe592fc7..c29e11bff624 100644 --- a/packages/SystemUI/res/layout/screenshot.xml +++ b/packages/SystemUI/res/layout/screenshot.xml @@ -29,18 +29,11 @@ android:clickable="true" android:importantForAccessibility="no"/> <ImageView - android:id="@+id/screenshot_actions_background" - android:layout_height="@dimen/overlay_bg_protection_height" - android:layout_width="match_parent" - android:layout_gravity="bottom" - android:alpha="0.0" - android:src="@drawable/overlay_actions_background_protection"/> - <ImageView android:id="@+id/screenshot_flash" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone" - android:elevation="@dimen/overlay_preview_elevation" + android:elevation="7dp" android:src="@android:color/white"/> <com.android.systemui.screenshot.ScreenshotSelectorView android:id="@+id/screenshot_selector" diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml index c60609b06d38..9c027495aa1e 100644 --- a/packages/SystemUI/res/layout/screenshot_static.xml +++ b/packages/SystemUI/res/layout/screenshot_static.xml @@ -24,7 +24,7 @@ android:visibility="gone" android:layout_height="0dp" android:layout_width="0dp" - android:elevation="1dp" + android:elevation="4dp" android:background="@drawable/action_chip_container_background" android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal" app:layout_constraintBottom_toBottomOf="@+id/actions_container" @@ -36,9 +36,10 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal" + android:layout_marginBottom="4dp" android:paddingEnd="@dimen/overlay_action_container_padding_right" android:paddingVertical="@dimen/overlay_action_container_padding_vertical" - android:elevation="1dp" + android:elevation="4dp" android:scrollbars="none" app:layout_constraintHorizontal_bias="0" app:layout_constraintWidth_percent="1.0" @@ -64,8 +65,8 @@ android:layout_width="0dp" android:layout_height="0dp" android:layout_marginStart="@dimen/overlay_offset_x" - android:layout_marginBottom="@dimen/overlay_offset_y" - android:elevation="@dimen/overlay_preview_elevation" + android:layout_marginBottom="12dp" + android:elevation="7dp" android:alpha="0" android:background="@drawable/overlay_border" app:layout_constraintStart_toStartOf="parent" @@ -93,7 +94,7 @@ android:layout_margin="@dimen/overlay_border_width" android:layout_height="wrap_content" android:layout_gravity="center" - android:elevation="@dimen/overlay_preview_elevation" + android:elevation="7dp" android:contentDescription="@string/screenshot_edit_description" android:scaleType="fitEnd" android:background="@drawable/overlay_preview_background" @@ -108,7 +109,7 @@ android:id="@+id/screenshot_dismiss_button" android:layout_width="@dimen/overlay_dismiss_button_tappable_size" android:layout_height="@dimen/overlay_dismiss_button_tappable_size" - android:elevation="@dimen/overlay_dismiss_button_elevation" + android:elevation="10dp" android:visibility="gone" app:layout_constraintStart_toEndOf="@id/screenshot_preview" app:layout_constraintEnd_toEndOf="@id/screenshot_preview" @@ -130,5 +131,5 @@ android:visibility="gone" app:layout_constraintStart_toStartOf="@id/screenshot_preview" app:layout_constraintTop_toTopOf="@id/screenshot_preview" - android:elevation="@dimen/overlay_preview_elevation"/> + android:elevation="7dp"/> </com.android.systemui.screenshot.DraggableConstraintLayout> diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml index 1d6f279afc66..e6af6f46ae69 100644 --- a/packages/SystemUI/res/values-h800dp/dimens.xml +++ b/packages/SystemUI/res/values-h800dp/dimens.xml @@ -16,7 +16,7 @@ <resources> <!-- Minimum margin between clock and top of screen or ambient indication --> - <dimen name="keyguard_clock_top_margin">38dp</dimen> + <dimen name="keyguard_clock_top_margin">26dp</dimen> <!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) --> <dimen name="large_clock_text_size">200dp</dimen> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index a014efb7d176..0bc3594ef183 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -282,15 +282,12 @@ <!-- Spacing between chip icon and chip text --> <dimen name="overlay_action_chip_spacing">8dp</dimen> <dimen name="overlay_action_chip_text_size">14sp</dimen> - <dimen name="overlay_offset_y">8dp</dimen> <dimen name="overlay_offset_x">16dp</dimen> - <dimen name="overlay_preview_elevation">4dp</dimen> <dimen name="overlay_action_container_margin_horizontal">8dp</dimen> <dimen name="overlay_bg_protection_height">242dp</dimen> <dimen name="overlay_action_container_corner_radius">18dp</dimen> <dimen name="overlay_action_container_padding_vertical">4dp</dimen> <dimen name="overlay_action_container_padding_right">8dp</dimen> - <dimen name="overlay_dismiss_button_elevation">7dp</dimen> <dimen name="overlay_dismiss_button_tappable_size">48dp</dimen> <dimen name="overlay_dismiss_button_margin">8dp</dimen> <dimen name="overlay_border_width">4dp</dimen> @@ -1134,7 +1131,7 @@ <!-- Output switcher panel related dimensions --> <dimen name="media_output_dialog_list_margin">12dp</dimen> - <dimen name="media_output_dialog_list_max_height">364dp</dimen> + <dimen name="media_output_dialog_list_max_height">355dp</dimen> <dimen name="media_output_dialog_header_album_icon_size">72dp</dimen> <dimen name="media_output_dialog_header_back_icon_size">32dp</dimen> <dimen name="media_output_dialog_header_icon_padding">16dp</dimen> diff --git a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt index f195d2094d6a..38fa35453418 100644 --- a/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt +++ b/packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt @@ -16,14 +16,20 @@ package com.android.keyguard +import android.annotation.IntDef import android.content.ContentResolver import android.database.ContentObserver +import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT import android.net.Uri import android.os.Handler import android.os.UserHandle import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL +import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO +import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT +import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE +import android.util.Log import com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton @@ -44,6 +50,20 @@ class ActiveUnlockConfig @Inject constructor( dumpManager: DumpManager ) : Dumpable { + companion object { + const val TAG = "ActiveUnlockConfig" + + const val BIOMETRIC_TYPE_NONE = 0 + const val BIOMETRIC_TYPE_ANY_FACE = 1 + const val BIOMETRIC_TYPE_ANY_FINGERPRINT = 2 + const val BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT = 3 + } + + @Retention(AnnotationRetention.SOURCE) + @IntDef(BIOMETRIC_TYPE_NONE, BIOMETRIC_TYPE_ANY_FACE, BIOMETRIC_TYPE_ANY_FINGERPRINT, + BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT) + annotation class BiometricType + /** * Indicates the origin for an active unlock request. */ @@ -51,35 +71,50 @@ class ActiveUnlockConfig @Inject constructor( WAKE, UNLOCK_INTENT, BIOMETRIC_FAIL, ASSISTANT } + var keyguardUpdateMonitor: KeyguardUpdateMonitor? = null private var requestActiveUnlockOnWakeup = false private var requestActiveUnlockOnUnlockIntent = false private var requestActiveUnlockOnBioFail = false + private var faceErrorsToTriggerBiometricFailOn = mutableSetOf(FACE_ERROR_TIMEOUT) + private var faceAcquireInfoToTriggerBiometricFailOn = mutableSetOf<Int>() + private var onUnlockIntentWhenBiometricEnrolled = mutableSetOf<Int>(BIOMETRIC_TYPE_NONE) + private val settingsObserver = object : ContentObserver(handler) { - private val wakeUri: Uri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE) - private val unlockIntentUri: Uri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT) - private val bioFailUri: Uri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL) + private val wakeUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE) + private val unlockIntentUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT) + private val bioFailUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL) + private val faceErrorsUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS) + private val faceAcquireInfoUri = + secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO) + private val unlockIntentWhenBiometricEnrolledUri = + secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED) fun register() { - contentResolver.registerContentObserver( - wakeUri, - false, - this, - UserHandle.USER_ALL) - contentResolver.registerContentObserver( - unlockIntentUri, - false, - this, - UserHandle.USER_ALL) - contentResolver.registerContentObserver( - bioFailUri, - false, - this, - UserHandle.USER_ALL) + registerUri( + listOf( + wakeUri, + unlockIntentUri, + bioFailUri, + faceErrorsUri, + faceAcquireInfoUri, + unlockIntentWhenBiometricEnrolledUri + ) + ) onChange(true, ArrayList(), 0, getCurrentUser()) } + private fun registerUri(uris: Collection<Uri>) { + for (uri in uris) { + contentResolver.registerContentObserver( + uri, + false, + this, + UserHandle.USER_ALL) + } + } + override fun onChange( selfChange: Boolean, uris: Collection<Uri>, @@ -104,6 +139,55 @@ class ActiveUnlockConfig @Inject constructor( requestActiveUnlockOnBioFail = secureSettings.getIntForUser( ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 0, getCurrentUser()) == 1 } + + if (selfChange || uris.contains(faceErrorsUri)) { + processStringArray( + secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ERRORS, + getCurrentUser()), + faceErrorsToTriggerBiometricFailOn, + setOf(FACE_ERROR_TIMEOUT)) + } + + if (selfChange || uris.contains(faceAcquireInfoUri)) { + processStringArray( + secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO, + getCurrentUser()), + faceAcquireInfoToTriggerBiometricFailOn, + setOf<Int>()) + } + + if (selfChange || uris.contains(unlockIntentWhenBiometricEnrolledUri)) { + processStringArray( + secureSettings.getStringForUser( + ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, + getCurrentUser()), + onUnlockIntentWhenBiometricEnrolled, + setOf(BIOMETRIC_TYPE_NONE)) + } + } + + /** + * Convert a pipe-separated set of integers into a set of ints. + * @param stringSetting expected input are integers delineated by a pipe. For example, + * it may look something like this: "1|5|3". + * @param out updates the "out" Set will the integers between the pipes. + * @param default If stringSetting is null, "out" will be populated with values in "default" + */ + private fun processStringArray( + stringSetting: String?, + out: MutableSet<Int>, + default: Set<Int> + ) { + out.clear() + stringSetting?.let { + for (code: String in stringSetting.split("|")) { + try { + out.add(code.toInt()) + } catch (e: NumberFormatException) { + Log.e(TAG, "Passed an invalid setting=$code") + } + } + } ?: out.addAll(default) } } @@ -113,6 +197,30 @@ class ActiveUnlockConfig @Inject constructor( } /** + * If any active unlock triggers are enabled. + */ + fun isActiveUnlockEnabled(): Boolean { + return requestActiveUnlockOnWakeup || requestActiveUnlockOnUnlockIntent || + requestActiveUnlockOnBioFail + } + + /** + * Whether the face error code from {@link BiometricFaceConstants} should trigger + * active unlock on biometric failure. + */ + fun shouldRequestActiveUnlockOnFaceError(errorCode: Int): Boolean { + return faceErrorsToTriggerBiometricFailOn.contains(errorCode) + } + + /** + * Whether the face acquireInfo from {@link BiometricFaceConstants} should trigger + * active unlock on biometric failure. + */ + fun shouldRequestActiveUnlockOnFaceAcquireInfo(acquiredInfo: Int): Boolean { + return faceAcquireInfoToTriggerBiometricFailOn.contains(acquiredInfo) + } + + /** * Whether to trigger active unlock based on where the request is coming from and * the current settings. */ @@ -121,7 +229,8 @@ class ActiveUnlockConfig @Inject constructor( ACTIVE_UNLOCK_REQUEST_ORIGIN.WAKE -> requestActiveUnlockOnWakeup ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT -> - requestActiveUnlockOnUnlockIntent || requestActiveUnlockOnWakeup + requestActiveUnlockOnUnlockIntent || requestActiveUnlockOnWakeup || + (shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()) ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL -> requestActiveUnlockOnBioFail || requestActiveUnlockOnUnlockIntent || @@ -131,17 +240,55 @@ class ActiveUnlockConfig @Inject constructor( } } - /** - * If any active unlock triggers are enabled. - */ - fun isActiveUnlockEnabled(): Boolean { - return requestActiveUnlockOnWakeup || requestActiveUnlockOnUnlockIntent || - requestActiveUnlockOnBioFail + private fun shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment(): Boolean { + if (!requestActiveUnlockOnBioFail) { + return false + } + + keyguardUpdateMonitor?.let { + val anyFaceEnrolled = it.isFaceEnrolled + val anyFingerprintEnrolled = + it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser()) + val udfpsEnrolled = it.isUdfpsEnrolled + + if (!anyFaceEnrolled && !anyFingerprintEnrolled) { + return onUnlockIntentWhenBiometricEnrolled.contains(BIOMETRIC_TYPE_NONE) + } + + if (!anyFaceEnrolled && anyFingerprintEnrolled) { + return onUnlockIntentWhenBiometricEnrolled.contains( + BIOMETRIC_TYPE_ANY_FINGERPRINT) || + (udfpsEnrolled && onUnlockIntentWhenBiometricEnrolled.contains( + BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT)) + } + + if (!anyFingerprintEnrolled && anyFaceEnrolled) { + return onUnlockIntentWhenBiometricEnrolled.contains(BIOMETRIC_TYPE_ANY_FACE) + } + } + + return false } override fun dump(pw: PrintWriter, args: Array<out String>) { + pw.println("Settings:") pw.println(" requestActiveUnlockOnWakeup=$requestActiveUnlockOnWakeup") pw.println(" requestActiveUnlockOnUnlockIntent=$requestActiveUnlockOnUnlockIntent") pw.println(" requestActiveUnlockOnBioFail=$requestActiveUnlockOnBioFail") + pw.println(" requestActiveUnlockOnUnlockIntentWhenBiometricEnrolled=" + + "$onUnlockIntentWhenBiometricEnrolled") + pw.println(" requestActiveUnlockOnFaceError=$faceErrorsToTriggerBiometricFailOn") + pw.println(" requestActiveUnlockOnFaceAcquireInfo=" + + "$faceAcquireInfoToTriggerBiometricFailOn") + + pw.println("Current state:") + keyguardUpdateMonitor?.let { + pw.println(" shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment=" + + "${shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()}") + pw.println(" faceEnrolled=${it.isFaceEnrolled}") + pw.println(" fpEnrolled=${ + it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser())}") + pw.println(" udfpsEnrolled=${it.isUdfpsEnrolled}") + } ?: pw.println(" keyguardUpdateMonitor is uninitialized") } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index bbe9a362b1fa..121ac299ec5b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -56,7 +56,6 @@ import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.hardware.SensorPrivacyManager; -import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricSourceType; @@ -1615,7 +1614,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mKeyguardBypassController.setUserHasDeviceEntryIntent(false); } - if (errMsgId == BiometricFaceConstants.FACE_ERROR_TIMEOUT) { + if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceError(errMsgId)) { requestActiveUnlock( ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL, "faceError-" + errMsgId); @@ -1625,6 +1624,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab @Override public void onAuthenticationAcquired(int acquireInfo) { handleFaceAcquired(acquireInfo); + + if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo( + acquireInfo)) { + requestActiveUnlock( + ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL, + "faceAcquireInfo-" + acquireInfo); + } } }; @@ -1639,6 +1645,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private boolean mFingerprintLockedOut; private boolean mFingerprintLockedOutPermanent; private boolean mFaceLockedOutPermanent; + private HashMap<Integer, Boolean> mIsUnlockWithFingerprintPossible = new HashMap<>(); private TelephonyManager mTelephonyManager; /** @@ -1889,6 +1896,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab dumpManager.registerDumpable(getClass().getName(), this); mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class); mActiveUnlockConfig = activeUnlockConfiguration; + mActiveUnlockConfig.setKeyguardUpdateMonitor(this); mHandler = new Handler(mainLooper) { @Override @@ -2329,7 +2337,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } if (shouldTriggerActiveUnlock()) { - if (DEBUG) { + if (DEBUG_ACTIVE_UNLOCK) { Log.d("ActiveUnlock", "initiate active unlock triggerReason=" + reason); } mTrustManager.reportUserMayRequestUnlock(KeyguardUpdateMonitor.getCurrentUser()); @@ -2359,7 +2367,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } if (allowRequest && shouldTriggerActiveUnlock()) { - if (DEBUG) { + if (DEBUG_ACTIVE_UNLOCK) { Log.d("ActiveUnlock", "reportUserRequestedUnlock" + " origin=" + requestOrigin.name() + " reason=" + reason @@ -2777,8 +2785,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } private boolean isUnlockWithFingerprintPossible(int userId) { - return mFpm != null && mFpm.isHardwareDetected() && !isFingerprintDisabled(userId) - && mFpm.hasEnrolledTemplates(userId); + mIsUnlockWithFingerprintPossible.put(userId, mFpm != null && mFpm.isHardwareDetected() + && !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId)); + return mIsUnlockWithFingerprintPossible.get(userId); + } + + /** + * Cached value for whether fingerprint is enrolled and possible to use for authentication. + * Note: checking fingerprint enrollment directly with the AuthController requires an IPC. + */ + public boolean getCachedIsUnlockWithFingerprintPossible(int userId) { + return mIsUnlockWithFingerprintPossible.get(userId); } private boolean isUnlockWithFacePossible(int userId) { diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java index e1913657b7cc..fdde40296511 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java @@ -55,7 +55,7 @@ public class LockIconView extends FrameLayout implements Dumpable { @NonNull private final RectF mSensorRect; @NonNull private PointF mLockIconCenter = new PointF(0f, 0f); - private int mRadius; + private float mRadius; private int mLockIconPadding; private ImageView mLockIcon; @@ -126,7 +126,7 @@ public class LockIconView extends FrameLayout implements Dumpable { * Set the location of the lock icon. */ @VisibleForTesting - public void setCenterLocation(@NonNull PointF center, int radius, int drawablePadding) { + public void setCenterLocation(@NonNull PointF center, float radius, int drawablePadding) { mLockIconCenter = center; mRadius = radius; mLockIconPadding = drawablePadding; diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index 2b217f02e834..d79b1454514e 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -188,6 +188,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme protected void onViewAttached() { updateIsUdfpsEnrolled(); updateConfiguration(); + updateLockIconLocation(); updateKeyguardShowing(); mUserUnlockedWithBiometric = false; @@ -340,20 +341,17 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mHeightPixels = bounds.bottom; mBottomPaddingPx = getResources().getDimensionPixelSize(R.dimen.lock_icon_margin_bottom); - final int defaultPaddingPx = - getResources().getDimensionPixelSize(R.dimen.lock_icon_padding); - mScaledPaddingPx = (int) (defaultPaddingPx * mAuthController.getScaleFactor()); - mUnlockedLabel = mView.getContext().getResources().getString( R.string.accessibility_unlock_button); mLockedLabel = mView.getContext() .getResources().getString(R.string.accessibility_lock_icon); - - updateLockIconLocation(); } private void updateLockIconLocation() { if (mUdfpsSupported) { + final int defaultPaddingPx = + getResources().getDimensionPixelSize(R.dimen.lock_icon_padding); + mScaledPaddingPx = (int) (defaultPaddingPx * mAuthController.getScaleFactor()); mView.setCenterLocation(mAuthController.getUdfpsLocation(), mAuthController.getUdfpsRadius(), mScaledPaddingPx); } else { @@ -362,8 +360,6 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mHeightPixels - mBottomPaddingPx - sLockIconRadiusPx), sLockIconRadiusPx, mScaledPaddingPx); } - - mView.getHitRect(mSensorTouchLocation); } @Override @@ -386,6 +382,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen); pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState)); pw.println(" mInterpolatedDarkAmount: " + mInterpolatedDarkAmount); + pw.println(" mSensorTouchLocation: " + mSensorTouchLocation); if (mView != null) { mView.dump(pw, args); @@ -672,6 +669,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme } private boolean inLockIconArea(MotionEvent event) { + mView.getHitRect(mSensorTouchLocation); return mSensorTouchLocation.contains((int) event.getX(), (int) event.getY()) && mView.getVisibility() == View.VISIBLE; } @@ -692,6 +690,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mExecutor.execute(() -> { updateIsUdfpsEnrolled(); updateConfiguration(); + updateLockIconLocation(); }); } @@ -705,6 +704,11 @@ public class LockIconViewController extends ViewController<LockIconView> impleme public void onEnrollmentsChanged() { updateUdfpsConfig(); } + + @Override + public void onUdfpsLocationChanged() { + updateLockIconLocation(); + } }; private final View.OnClickListener mA11yClickListener = v -> onLongPress(); diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index cbce854e4a71..dd312186afee 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -47,6 +47,7 @@ import android.hardware.graphics.common.AlphaInterpretation; import android.hardware.graphics.common.DisplayDecorationSupport; import android.os.Handler; import android.os.SystemProperties; +import android.os.Trace; import android.os.UserHandle; import android.provider.Settings.Secure; import android.util.DisplayUtils; @@ -1067,15 +1068,22 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } private void updateLayoutParams() { - if (mOverlays == null) { - return; + //ToDo: We should skip unnecessary call to update view layout. + Trace.beginSection("ScreenDecorations#updateLayoutParams"); + if (mScreenDecorHwcWindow != null) { + mWindowManager.updateViewLayout(mScreenDecorHwcWindow, getHwcWindowLayoutParams()); } - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] == null) { - continue; + + if (mOverlays != null) { + for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { + if (mOverlays[i] == null) { + continue; + } + mWindowManager.updateViewLayout( + mOverlays[i].getRootView(), getWindowLayoutParams(i)); } - mWindowManager.updateViewLayout(mOverlays[i].getRootView(), getWindowLayoutParams(i)); } + Trace.endSection(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 932489372872..75339aaa843d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -79,6 +79,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Objects; import java.util.Set; import javax.inject.Inject; @@ -446,11 +447,11 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba /** * @return the radius of UDFPS on the screen in pixels */ - public int getUdfpsRadius() { + public float getUdfpsRadius() { if (mUdfpsController == null || mUdfpsBounds == null) { return -1; } - return mUdfpsBounds.height() / 2; + return mUdfpsBounds.height() / 2f; } /** @@ -634,11 +635,17 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba displayInfo.getNaturalHeight()); final FingerprintSensorPropertiesInternal udfpsProp = mUdfpsProps.get(0); + final Rect previousUdfpsBounds = mUdfpsBounds; mUdfpsBounds = udfpsProp.getLocation().getRect(); mUdfpsBounds.scale(scaleFactor); mUdfpsController.updateOverlayParams(udfpsProp.sensorId, new UdfpsOverlayParams(mUdfpsBounds, displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight(), scaleFactor, displayInfo.rotation)); + if (!Objects.equals(previousUdfpsBounds, mUdfpsBounds)) { + for (Callback cb : mCallbacks) { + cb.onUdfpsLocationChanged(); + } + } } } @@ -1054,5 +1061,10 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba * Called when the biometric prompt is no longer showing. */ default void onBiometricPromptDismissed() {} + + /** + * The location in pixels can change due to resolution changes. + */ + default void onUdfpsLocationChanged() {} } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index d9aa1bae3c69..86e501670440 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -335,15 +335,17 @@ class AuthRippleController @Inject constructor( updateSensorLocation() } - override fun onEnrollmentsChanged() { + override fun onUdfpsLocationChanged() { + updateUdfpsDependentParams() + updateSensorLocation() } } private fun updateUdfpsDependentParams() { authController.udfpsProps?.let { if (it.size > 0) { - udfpsRadius = it[0].location.sensorRadius.toFloat() udfpsController = udfpsControllerProvider.get() + udfpsRadius = authController.udfpsRadius if (mView.isAttachedToWindow) { udfpsController?.addCallback(udfpsControllerCallback) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java index 9139699af26a..f28fedb9155b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java @@ -32,6 +32,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; +import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.asynclayoutinflater.view.AsyncLayoutInflater; @@ -44,6 +45,8 @@ import com.airbnb.lottie.LottieProperty; import com.airbnb.lottie.model.KeyPath; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * View corresponding with udfps_keyguard_view.xml @@ -52,7 +55,6 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { private UdfpsDrawable mFingerprintDrawable; // placeholder private LottieAnimationView mAodFp; private LottieAnimationView mLockScreenFp; - private int mStatusBarState; // used when highlighting fp icon: private int mTextColorPrimary; @@ -70,7 +72,7 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { private float mBurnInOffsetY; private float mBurnInProgress; private float mInterpolatedDarkAmount; - private boolean mAnimatingBetweenAodAndLockscreen; // As opposed to Unlocked => AOD + private int mAnimationType = ANIMATION_NONE; private boolean mFullyInflated; public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) { @@ -117,8 +119,10 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { return; } - final float darkAmountForAnimation = mAnimatingBetweenAodAndLockscreen - ? mInterpolatedDarkAmount : 1f /* animating from unlocked to AOD */; + // if we're animating from screen off, we can immediately place the icon in the + // AoD-burn in location, else we need to translate the icon from LS => AoD. + final float darkAmountForAnimation = mAnimationType == ANIMATION_UNLOCKED_SCREEN_OFF + ? 1f : mInterpolatedDarkAmount; mBurnInOffsetX = MathUtils.lerp(0f, getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */) - mMaxBurnInOffsetX, darkAmountForAnimation); @@ -127,12 +131,12 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { - mMaxBurnInOffsetY, darkAmountForAnimation); mBurnInProgress = MathUtils.lerp(0f, getBurnInProgressOffset(), darkAmountForAnimation); - if (mAnimatingBetweenAodAndLockscreen && !mPauseAuth) { + if (mAnimationType == ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN && !mPauseAuth) { mLockScreenFp.setTranslationX(mBurnInOffsetX); mLockScreenFp.setTranslationY(mBurnInOffsetY); mBgProtection.setAlpha(1f - mInterpolatedDarkAmount); mLockScreenFp.setAlpha(1f - mInterpolatedDarkAmount); - } else if (mInterpolatedDarkAmount == 0f) { + } else if (darkAmountForAnimation == 0f) { mLockScreenFp.setTranslationX(0); mLockScreenFp.setTranslationY(0); mBgProtection.setAlpha(mAlpha / 255f); @@ -148,9 +152,15 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { mAodFp.setProgress(mBurnInProgress); mAodFp.setAlpha(mInterpolatedDarkAmount); - // done animating between AoD & LS - if (mInterpolatedDarkAmount == 1f || mInterpolatedDarkAmount == 0f) { - mAnimatingBetweenAodAndLockscreen = false; + // done animating + final boolean doneAnimatingBetweenAodAndLS = + mAnimationType == ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN + && (mInterpolatedDarkAmount == 0f || mInterpolatedDarkAmount == 1f); + final boolean doneAnimatingUnlockedScreenOff = + mAnimationType == ANIMATION_UNLOCKED_SCREEN_OFF + && (mInterpolatedDarkAmount == 1f); + if (doneAnimatingBetweenAodAndLS || doneAnimatingUnlockedScreenOff) { + mAnimationType = ANIMATION_NONE; } } @@ -158,10 +168,6 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { mUdfpsRequested = request; } - void setStatusBarState(int statusBarState) { - mStatusBarState = statusBarState; - } - void updateColor() { if (!mFullyInflated) { return; @@ -219,8 +225,16 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { return mAlpha; } - void onDozeAmountChanged(float linear, float eased, boolean animatingBetweenAodAndLockscreen) { - mAnimatingBetweenAodAndLockscreen = animatingBetweenAodAndLockscreen; + static final int ANIMATION_NONE = 0; + static final int ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN = 1; + static final int ANIMATION_UNLOCKED_SCREEN_OFF = 2; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({ANIMATION_NONE, ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN, ANIMATION_UNLOCKED_SCREEN_OFF}) + private @interface AnimationType {} + + void onDozeAmountChanged(float linear, float eased, @AnimationType int animationType) { + mAnimationType = animationType; mInterpolatedDarkAmount = eased; updateAlpha(); } @@ -262,7 +276,7 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { pw.println(" mUnpausedAlpha=" + getUnpausedAlpha()); pw.println(" mUdfpsRequested=" + mUdfpsRequested); pw.println(" mInterpolatedDarkAmount=" + mInterpolatedDarkAmount); - pw.println(" mAnimatingBetweenAodAndLockscreen=" + mAnimatingBetweenAodAndLockscreen); + pw.println(" mAnimationType=" + mAnimationType); } private final AsyncLayoutInflater.OnInflateFinishedListener mLayoutInflaterFinishListener = diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java index 8b0f36fe1245..ec4cf2fd8bd4 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -121,7 +121,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud mView.onDozeAmountChanged( animation.getAnimatedFraction(), (float) animation.getAnimatedValue(), - /* animatingBetweenAodAndLockScreen */ false); + UdfpsKeyguardView.ANIMATION_UNLOCKED_SCREEN_OFF); } }); } @@ -394,7 +394,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud mUnlockedScreenOffDozeAnimator.start(); } else { mView.onDozeAmountChanged(linear, eased, - /* animatingBetweenAodAndLockScreen */ true); + UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN); } mLastDozeAmount = linear; @@ -404,7 +404,6 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @Override public void onStateChanged(int statusBarState) { mStatusBarState = statusBarState; - mView.setStatusBarState(statusBarState); updateAlpha(); updatePauseAuth(); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 74044e2c2eb8..1cc5df5d04cf 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -39,7 +39,6 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.doze.DozeMachine.State; import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.statusbar.phone.DozeParameters; -import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DevicePostureController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.Assert; @@ -94,7 +93,6 @@ public class DozeTriggers implements DozeMachine.Part { private final AuthController mAuthController; private final DelayableExecutor mMainExecutor; private final KeyguardStateController mKeyguardStateController; - private final BatteryController mBatteryController; private final UiEventLogger mUiEventLogger; private final DevicePostureController mDevicePostureController; @@ -186,8 +184,7 @@ public class DozeTriggers implements DozeMachine.Part { @Main DelayableExecutor mainExecutor, UiEventLogger uiEventLogger, KeyguardStateController keyguardStateController, - DevicePostureController devicePostureController, - BatteryController batteryController) { + DevicePostureController devicePostureController) { mContext = context; mDozeHost = dozeHost; mConfig = config; @@ -208,7 +205,6 @@ public class DozeTriggers implements DozeMachine.Part { mMainExecutor = mainExecutor; mUiEventLogger = uiEventLogger; mKeyguardStateController = keyguardStateController; - mBatteryController = batteryController; } private final DevicePostureController.Callback mDevicePostureCallback = posture -> { @@ -320,12 +316,7 @@ public class DozeTriggers implements DozeMachine.Part { gentleWakeUp(pulseReason); } else if (isPickup) { if (shouldDropPickupEvent()) { - mDozeLog.traceSensorEventDropped( - pulseReason, - "keyguardOccluded=" - + mKeyguardStateController.isOccluded() - + " pluggedInWireless=" - + mBatteryController.isPluggedInWireless()); + mDozeLog.traceSensorEventDropped(pulseReason, "keyguard occluded"); return; } gentleWakeUp(pulseReason); @@ -356,7 +347,7 @@ public class DozeTriggers implements DozeMachine.Part { } private boolean shouldDropPickupEvent() { - return mKeyguardStateController.isOccluded() || mBatteryController.isPluggedInWireless(); + return mKeyguardStateController.isOccluded(); } private void gentleWakeUp(int reason) { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 2fec49955c2d..8c2cb6ded678 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -97,7 +97,7 @@ import kotlin.Unit; * A view controller used for Media Playback. */ public class MediaControlPanel { - private static final String TAG = "MediaControlPanel"; + protected static final String TAG = "MediaControlPanel"; private static final float DISABLED_ALPHA = 0.38f; private static final String EXPORTED_SMARTSPACE_TRAMPOLINE_ACTIVITY_NAME = "com.google" @@ -106,7 +106,7 @@ public class MediaControlPanel { "com.google.android.apps.gsa.smartspace.extra.SMARTSPACE_INTENT"; private static final String KEY_SMARTSPACE_ARTIST_NAME = "artist_name"; private static final String KEY_SMARTSPACE_OPEN_IN_FOREGROUND = "KEY_OPEN_IN_FOREGROUND"; - private static final String KEY_SMARTSPACE_APP_NAME = "KEY_SMARTSPACE_APP_NAME"; + protected static final String KEY_SMARTSPACE_APP_NAME = "KEY_SMARTSPACE_APP_NAME"; // Event types logged by smartspace private static final int SMARTSPACE_CARD_CLICK_EVENT = 760; @@ -149,6 +149,7 @@ public class MediaControlPanel { private RecommendationViewHolder mRecommendationViewHolder; private String mKey; private MediaData mMediaData; + private SmartspaceMediaData mRecommendationData; private MediaViewController mMediaViewController; private MediaSession.Token mToken; private MediaController mController; @@ -448,6 +449,7 @@ public class MediaControlPanel { bindOutputSwitcherChip(data); bindGutsMenuForPlayer(data); + bindPlayerContentDescription(data); bindScrubbingTime(data); bindActionButtons(data); @@ -541,12 +543,6 @@ public class MediaControlPanel { } private boolean bindSongMetadata(MediaData data) { - // Accessibility label - mMediaViewHolder.getPlayer().setContentDescription( - mContext.getString( - R.string.controls_media_playing_item_description, - data.getSong(), data.getArtist(), data.getApp())); - TextView titleText = mMediaViewHolder.getTitleText(); TextView artistText = mMediaViewHolder.getArtistText(); return mMetadataAnimationHandler.setNext( @@ -568,6 +564,48 @@ public class MediaControlPanel { }); } + // We may want to look into unifying this with bindRecommendationContentDescription if/when we + // do a refactor of this class. + private void bindPlayerContentDescription(MediaData data) { + if (mMediaViewHolder == null) { + return; + } + + CharSequence contentDescription; + if (mMediaViewController.isGutsVisible()) { + contentDescription = mMediaViewHolder.getGutsViewHolder().getGutsText().getText(); + } else if (data != null) { + contentDescription = mContext.getString( + R.string.controls_media_playing_item_description, + data.getSong(), + data.getArtist(), + data.getApp()); + } else { + contentDescription = null; + } + mMediaViewHolder.getPlayer().setContentDescription(contentDescription); + } + + private void bindRecommendationContentDescription(SmartspaceMediaData data) { + if (mRecommendationViewHolder == null) { + return; + } + + CharSequence contentDescription; + if (mMediaViewController.isGutsVisible()) { + contentDescription = + mRecommendationViewHolder.getGutsViewHolder().getGutsText().getText(); + } else if (data != null) { + contentDescription = mContext.getString( + R.string.controls_media_smartspace_rec_description, + data.getAppName(mContext)); + } else { + contentDescription = null; + } + + mRecommendationViewHolder.getRecommendations().setContentDescription(contentDescription); + } + private void bindArtworkAndColors(MediaData data, boolean updateBackground) { final int reqId = mArtworkNextBindRequestId++; if (updateBackground) { @@ -636,6 +674,10 @@ public class MediaControlPanel { mIsArtworkBound = isArtworkBound; } + // Scrim bounds are set manually so it scales as expected + albumView.getForeground().setBounds(0, 0, + Math.max(width, height), Math.max(width, height)); + // Transition Colors to current color scheme mColorSchemeTransition.updateColorScheme(colorScheme, mIsArtworkBound); @@ -950,6 +992,7 @@ public class MediaControlPanel { return; } + mRecommendationData = data; mSmartspaceId = SmallHash.hash(data.getTargetId()); mPackageName = data.getPackageName(); mInstanceId = data.getInstanceId(); @@ -965,6 +1008,12 @@ public class MediaControlPanel { return; } + CharSequence appName = data.getAppName(mContext); + if (appName == null) { + Log.w(TAG, "Fail to get media recommendation's app name"); + return; + } + PackageManager packageManager = mContext.getPackageManager(); // Set up media source app's logo. Drawable icon = packageManager.getApplicationIcon(applicationInfo); @@ -972,28 +1021,11 @@ public class MediaControlPanel { headerLogoImageView.setImageDrawable(icon); fetchAndUpdateRecommendationColors(icon); - // Set up media source app's label text. - CharSequence appName = getAppName(data.getCardAction()); - if (TextUtils.isEmpty(appName)) { - Intent launchIntent = - packageManager.getLaunchIntentForPackage(data.getPackageName()); - if (launchIntent != null) { - ActivityInfo launchActivity = launchIntent.resolveActivityInfo(packageManager, 0); - appName = launchActivity.loadLabel(packageManager); - } else { - Log.w(TAG, "Package " + data.getPackageName() - + " does not have a main launcher activity. Fallback to full app name"); - appName = packageManager.getApplicationLabel(applicationInfo); - } - } - // Set up media rec card's tap action if applicable. TransitionLayout recommendationCard = mRecommendationViewHolder.getRecommendations(); setSmartspaceRecItemOnClickListener(recommendationCard, data.getCardAction(), /* interactedSubcardRank */ -1); - // Set up media rec card's accessibility label. - recommendationCard.setContentDescription( - mContext.getString(R.string.controls_media_smartspace_rec_description, appName)); + bindRecommendationContentDescription(data); List<ImageView> mediaCoverItems = mRecommendationViewHolder.getMediaCoverItems(); List<ViewGroup> mediaCoverContainers = mRecommendationViewHolder.getMediaCoverContainers(); @@ -1175,6 +1207,11 @@ public class MediaControlPanel { mRecommendationViewHolder.marquee(false, mMediaViewController.GUTS_ANIMATION_DURATION); } mMediaViewController.closeGuts(immediate); + if (mMediaViewHolder != null) { + bindPlayerContentDescription(mMediaData); + } else if (mRecommendationViewHolder != null) { + bindRecommendationContentDescription(mRecommendationData); + } } private void closeGuts() { @@ -1188,6 +1225,11 @@ public class MediaControlPanel { mRecommendationViewHolder.marquee(true, mMediaViewController.GUTS_ANIMATION_DURATION); } mMediaViewController.openGuts(); + if (mMediaViewHolder != null) { + bindPlayerContentDescription(mMediaData); + } else if (mRecommendationViewHolder != null) { + bindRecommendationContentDescription(mRecommendationData); + } mLogger.logLongPressOpen(mUid, mPackageName, mInstanceId); } @@ -1302,17 +1344,6 @@ public class MediaControlPanel { }); } - /** Returns the upstream app name if available. */ - @Nullable - private String getAppName(SmartspaceAction action) { - if (action == null || action.getIntent() == null - || action.getIntent().getExtras() == null) { - return null; - } - - return action.getIntent().getExtras().getString(KEY_SMARTSPACE_APP_NAME); - } - /** Returns if the Smartspace action will open the activity in foreground. */ private boolean shouldSmartspaceRecItemOpenInForeground(SmartspaceAction action) { if (action == null || action.getIntent() == null diff --git a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt index 50a96f601443..c8f17d93bcc8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt +++ b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaData.kt @@ -17,8 +17,13 @@ package com.android.systemui.media import android.app.smartspace.SmartspaceAction +import android.content.Context import android.content.Intent +import android.content.pm.PackageManager +import android.text.TextUtils +import android.util.Log import com.android.internal.logging.InstanceId +import com.android.systemui.media.MediaControlPanel.KEY_SMARTSPACE_APP_NAME /** State of a Smartspace media recommendations view. */ data class SmartspaceMediaData( @@ -67,6 +72,32 @@ data class SmartspaceMediaData( * Returns the list of [recommendations] that have valid data. */ fun getValidRecommendations() = recommendations.filter { it.icon != null } + + /** Returns the upstream app name if available. */ + fun getAppName(context: Context): CharSequence? { + val nameFromAction = cardAction?.intent?.extras?.getString(KEY_SMARTSPACE_APP_NAME) + if (!TextUtils.isEmpty(nameFromAction)) { + return nameFromAction + } + + val packageManager = context.packageManager + packageManager.getLaunchIntentForPackage(packageName)?.let { + val launchActivity = it.resolveActivityInfo(packageManager, 0) + return launchActivity.loadLabel(packageManager) + } + + Log.w( + TAG, + "Package $packageName does not have a main launcher activity. " + + "Fallback to full app name") + return try { + val applicationInfo = packageManager.getApplicationInfo(packageName, /* flags= */ 0) + packageManager.getApplicationLabel(applicationInfo) + } catch (e: PackageManager.NameNotFoundException) { + null + } + } } const val NUM_REQUIRED_RECOMMENDATIONS = 3 +private val TAG = SmartspaceMediaData::class.simpleName!! diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java index a397f32dcbbf..58a35ff7e350 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java @@ -143,6 +143,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { if (mController.isTransferring()) { if (device.getState() == MediaDeviceState.STATE_CONNECTING && !mController.hasAdjustVolumeUserRestriction()) { + setUpDeviceIcon(device); mProgressBar.getIndeterminateDrawable().setColorFilter( new PorterDuffColorFilter( mController.getColorItemContent(), @@ -151,11 +152,13 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { false /* showSeekBar*/, true /* showProgressBar */, false /* showStatus */); } else { + setUpDeviceIcon(device); setSingleLineLayout(getItemTitle(device), false /* bFocused */); } } else { // Set different layout for each device if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) { + setUpDeviceIcon(device); mTitleText.setAlpha(DEVICE_CONNECTED_ALPHA); mTitleIcon.setAlpha(DEVICE_CONNECTED_ALPHA); mStatusIcon.setImageDrawable( @@ -167,6 +170,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mSubTitleText.setText(R.string.media_output_dialog_connect_failed); mContainerLayout.setOnClickListener(v -> onItemClick(v, device)); } else if (device.getState() == MediaDeviceState.STATE_GROUPING) { + setUpDeviceIcon(device); mProgressBar.getIndeterminateDrawable().setColorFilter( new PorterDuffColorFilter( mController.getColorItemContent(), @@ -176,7 +180,12 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { true /* showProgressBar */, false /* showStatus */); } else if (mController.getSelectedMediaDevice().size() > 1 && isDeviceIncluded(mController.getSelectedMediaDevice(), device)) { + boolean isDeviceDeselectable = isDeviceIncluded( + mController.getDeselectableMediaDevice(), device); mTitleText.setTextColor(mController.getColorItemContent()); + mTitleIcon.setImageDrawable( + mContext.getDrawable(R.drawable.media_output_icon_volume)); + mTitleIcon.setColorFilter(mController.getColorItemContent()); setSingleLineLayout(getItemTitle(device), true /* bFocused */, true /* showSeekBar */, false /* showProgressBar */, false /* showStatus */); @@ -184,13 +193,20 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mCheckBox.setOnCheckedChangeListener(null); mCheckBox.setVisibility(View.VISIBLE); mCheckBox.setChecked(true); - mCheckBox.setOnCheckedChangeListener( - (buttonView, isChecked) -> onGroupActionTriggered(false, device)); + mCheckBox.setOnCheckedChangeListener(isDeviceDeselectable + ? (buttonView, isChecked) -> onGroupActionTriggered(false, device) + : null); + mCheckBox.setEnabled(isDeviceDeselectable); + mCheckBox.setAlpha( + isDeviceDeselectable ? DEVICE_CONNECTED_ALPHA + : DEVICE_DISCONNECTED_ALPHA + ); setCheckBoxColor(mCheckBox, mController.getColorItemContent()); initSeekbar(device, isCurrentSeekbarInvisible); mEndTouchArea.setVisibility(View.VISIBLE); mEndTouchArea.setOnClickListener(null); - mEndTouchArea.setOnClickListener((v) -> mCheckBox.performClick()); + mEndTouchArea.setOnClickListener( + isDeviceDeselectable ? (v) -> mCheckBox.performClick() : null); mEndTouchArea.setImportantForAccessibility( View.IMPORTANT_FOR_ACCESSIBILITY_YES); setUpContentDescriptionForView(mEndTouchArea, true, device); @@ -198,6 +214,9 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { mStatusIcon.setImageDrawable( mContext.getDrawable(R.drawable.media_output_status_check)); mStatusIcon.setColorFilter(mController.getColorItemContent()); + mTitleIcon.setImageDrawable( + mContext.getDrawable(R.drawable.media_output_icon_volume)); + mTitleIcon.setColorFilter(mController.getColorItemContent()); mTitleText.setTextColor(mController.getColorItemContent()); setSingleLineLayout(getItemTitle(device), true /* bFocused */, true /* showSeekBar */, @@ -206,6 +225,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { setUpContentDescriptionForView(mContainerLayout, false, device); mCurrentActivePosition = position; } else if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) { + setUpDeviceIcon(device); mCheckBox.setOnCheckedChangeListener(null); mCheckBox.setVisibility(View.VISIBLE); mCheckBox.setChecked(false); @@ -218,6 +238,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { false /* showSeekBar */, false /* showProgressBar */, false /* showStatus */); } else { + setUpDeviceIcon(device); setSingleLineLayout(getItemTitle(device), false /* bFocused */); mContainerLayout.setOnClickListener(v -> onItemClick(v, device)); } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java index 5c2cc0b6af35..b407e76f1b11 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java @@ -166,15 +166,6 @@ public abstract class MediaOutputBaseAdapter extends void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) { mDeviceId = device.getId(); - ThreadUtils.postOnBackgroundThread(() -> { - Icon icon = mController.getDeviceIconCompat(device).toIcon(mContext); - ThreadUtils.postOnMainThread(() -> { - if (!TextUtils.equals(mDeviceId, device.getId())) { - return; - } - mTitleIcon.setImageIcon(icon); - }); - }); } abstract void onBind(int customizedItem, boolean topMargin, boolean bottomMargin); @@ -414,5 +405,17 @@ public abstract class MediaOutputBaseAdapter extends mSeekBar.setEnabled(false); mSeekBar.setOnTouchListener((v, event) -> true); } + + protected void setUpDeviceIcon(MediaDevice device) { + ThreadUtils.postOnBackgroundThread(() -> { + Icon icon = mController.getDeviceIconCompat(device).toIcon(mContext); + ThreadUtils.postOnMainThread(() -> { + if (!TextUtils.equals(mDeviceId, device.getId())) { + return; + } + mTitleIcon.setImageIcon(icon); + }); + }); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index 6af6e36a75f7..ccfcaa692e22 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -149,7 +149,6 @@ public class ScreenshotView extends FrameLayout implements private ImageView mActionsContainerBackground; private HorizontalScrollView mActionsContainer; private LinearLayout mActionsView; - private ImageView mBackgroundProtection; private FrameLayout mDismissButton; private OverlayActionChip mShareChip; private OverlayActionChip mEditChip; @@ -345,8 +344,6 @@ public class ScreenshotView extends FrameLayout implements R.id.actions_container_background)); mActionsContainer = requireNonNull(findViewById(R.id.actions_container)); mActionsView = requireNonNull(findViewById(R.id.screenshot_actions)); - mBackgroundProtection = requireNonNull( - findViewById(R.id.screenshot_actions_background)); mDismissButton = requireNonNull(findViewById(R.id.screenshot_dismiss_button)); mScrollablePreview = requireNonNull(findViewById(R.id.screenshot_scrollable_preview)); mScreenshotFlash = requireNonNull(findViewById(R.id.screenshot_flash)); @@ -394,14 +391,6 @@ public class ScreenshotView extends FrameLayout implements } mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED, 0, mPackageName); - animator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - super.onAnimationStart(animation); - mBackgroundProtection.animate() - .alpha(0).setDuration(animation.getDuration()).start(); - } - }); } @Override @@ -704,7 +693,6 @@ public class ScreenshotView extends FrameLayout implements animator.addUpdateListener(animation -> { float t = animation.getAnimatedFraction(); - mBackgroundProtection.setAlpha(t); float containerAlpha = t < alphaFraction ? t / alphaFraction : 1; mActionsContainer.setAlpha(containerAlpha); mActionsContainerBackground.setAlpha(containerAlpha); @@ -910,7 +898,6 @@ public class ScreenshotView extends FrameLayout implements } mDismissButton.setVisibility(View.GONE); mActionsContainer.setVisibility(View.GONE); - mBackgroundProtection.setVisibility(View.GONE); // set these invisible, but not gone, so that the views are laid out correctly mActionsContainerBackground.setVisibility(View.INVISIBLE); mScreenshotPreviewBorder.setVisibility(View.INVISIBLE); @@ -932,7 +919,6 @@ public class ScreenshotView extends FrameLayout implements mDismissButton.setVisibility(View.VISIBLE); } mActionsContainer.setVisibility(View.VISIBLE); - mBackgroundProtection.setVisibility(View.VISIBLE); mActionsContainerBackground.setVisibility(View.VISIBLE); mScreenshotPreviewBorder.setVisibility(View.VISIBLE); mScreenshotPreview.setVisibility(View.VISIBLE); @@ -969,7 +955,6 @@ public class ScreenshotView extends FrameLayout implements mPendingSharedTransition = false; mActionsContainerBackground.setVisibility(View.GONE); mActionsContainer.setVisibility(View.GONE); - mBackgroundProtection.setAlpha(0f); mDismissButton.setVisibility(View.GONE); mScrollingScrim.setVisibility(View.GONE); mScrollablePreview.setVisibility(View.GONE); @@ -1016,7 +1001,6 @@ public class ScreenshotView extends FrameLayout implements mDismissButton.setAlpha(alpha); mActionsContainerBackground.setAlpha(alpha); mActionsContainer.setAlpha(alpha); - mBackgroundProtection.setAlpha(alpha); mScreenshotPreviewBorder.setAlpha(alpha); }); alphaAnim.setDuration(600); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt index 6b79680fdb26..f0b221dd4726 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt @@ -160,8 +160,7 @@ class ChannelEditorDialogController @Inject constructor( val channels = groupList .flatMap { group -> group.channels.asSequence().filterNot { channel -> - channel.isImportanceLockedByOEM || - channel.importance == IMPORTANCE_NONE || + channel.importance == IMPORTANCE_NONE || channel.isImportanceLockedByCriticalDeviceFunction } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 0ae365352df0..dff8c47e36e0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -376,44 +376,24 @@ public class ExpandableNotificationRow extends ActivatableNotificationView * calls. */ private static Boolean isSystemNotification(Context context, StatusBarNotification sbn) { - // TODO (b/194833441): clean up before launch - if (Settings.Secure.getIntForUser(context.getContentResolver(), - Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 0, USER_SYSTEM) == 1) { - INotificationManager iNm = INotificationManager.Stub.asInterface( - ServiceManager.getService(Context.NOTIFICATION_SERVICE)); - - boolean isSystem = false; - try { - isSystem = iNm.isPermissionFixed(sbn.getPackageName(), sbn.getUserId()); - } catch (RemoteException e) { - Log.e(TAG, "cannot reach NMS"); - } - RoleManager rm = context.getSystemService(RoleManager.class); - List<String> fixedRoleHolders = new ArrayList<>(); - fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_DIALER)); - fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_EMERGENCY)); - if (fixedRoleHolders.contains(sbn.getPackageName())) { - isSystem = true; - } - - return isSystem; - } else { - PackageManager packageManager = CentralSurfaces.getPackageManagerForUser( - context, sbn.getUser().getIdentifier()); - Boolean isSystemNotification = null; - - try { - PackageInfo packageInfo = packageManager.getPackageInfo( - sbn.getPackageName(), PackageManager.GET_SIGNATURES); + INotificationManager iNm = INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); - isSystemNotification = - com.android.settingslib.Utils.isSystemPackage( - context.getResources(), packageManager, packageInfo); - } catch (PackageManager.NameNotFoundException e) { - Log.e(TAG, "cacheIsSystemNotification: Could not find package info"); - } - return isSystemNotification; + boolean isSystem = false; + try { + isSystem = iNm.isPermissionFixed(sbn.getPackageName(), sbn.getUserId()); + } catch (RemoteException e) { + Log.e(TAG, "cannot reach NMS"); } + RoleManager rm = context.getSystemService(RoleManager.class); + List<String> fixedRoleHolders = new ArrayList<>(); + fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_DIALER)); + fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_EMERGENCY)); + if (fixedRoleHolders.contains(sbn.getPackageName())) { + isSystem = true; + } + + return isSystem; } public NotificationContentView[] getLayouts() { @@ -567,9 +547,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mEntry.mIsSystemNotification = isSystemNotification(mContext, mEntry.getSbn()); } - // TODO (b/194833441): remove when we've migrated to permission - boolean isNonblockable = mEntry.getChannel().isImportanceLockedByOEM() - || mEntry.getChannel().isImportanceLockedByCriticalDeviceFunction(); + boolean isNonblockable = mEntry.getChannel().isImportanceLockedByCriticalDeviceFunction(); if (!isNonblockable && mEntry != null && mEntry.mIsSystemNotification != null) { if (mEntry.mIsSystemNotification) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 83970dc15e61..629aa0317d24 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -220,7 +220,7 @@ public class KeyguardClockPositionAlgorithm { } } - public float getMinStackScrollerPadding() { + public float getLockscreenMinStackScrollerPadding() { if (mBypassEnabled) { return mUnlockedStackScrollerPadding; } else if (mIsSplitShade) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 7da32384b766..77b9efa3b16b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -326,6 +326,11 @@ public class NotificationPanelViewController extends PanelViewController { private boolean mShouldUseSplitNotificationShade; // The bottom padding reserved for elements of the keyguard measuring notifications private float mKeyguardNotificationBottomPadding; + /** + * The top padding from where notification should start in lockscreen. + * Should be static also during animations and should match the Y of the first notification. + */ + private float mKeyguardNotificationTopPadding; // Current max allowed keyguard notifications determined by measuring the panel private int mMaxAllowedKeyguardNotifications; @@ -1514,7 +1519,10 @@ public class NotificationPanelViewController extends PanelViewController { */ @VisibleForTesting float getSpaceForLockscreenNotifications() { - float topPadding = mNotificationStackScrollLayoutController.getTopPadding(); + float staticTopPadding = mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding() + // getMinStackScrollerPadding is from the top of the screen, + // but we need it from the top of the NSSL. + - mNotificationStackScrollLayoutController.getTop(); // Space between bottom of notifications and top of lock icon or udfps background. float lockIconPadding = mLockIconViewController.getTop(); @@ -1526,11 +1534,15 @@ public class NotificationPanelViewController extends PanelViewController { float bottomPadding = Math.max(lockIconPadding, Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding)); + mKeyguardNotificationBottomPadding = bottomPadding; + mKeyguardNotificationTopPadding = staticTopPadding; + // To debug the available space, enable debug lines in this class. If you change how the + // available space is calculated, please also update those lines. float availableSpace = mNotificationStackScrollLayoutController.getHeight() - - topPadding + - staticTopPadding - bottomPadding; return availableSpace; } @@ -3198,6 +3210,8 @@ public class NotificationPanelViewController extends PanelViewController { updateTrackingHeadsUp(null); mExpandingFromHeadsUp = false; setPanelScrimMinFraction(0.0f); + // Reset status bar alpha so alpha can be calculated upon updating view state. + setKeyguardStatusBarAlpha(-1f); } private void updateTrackingHeadsUp(@Nullable ExpandableNotificationRow pickedChild) { @@ -4924,6 +4938,20 @@ public class NotificationPanelViewController extends PanelViewController { drawDebugInfo(canvas, (int) mLockIconViewController.getTop(), Color.GRAY, "mLockIconViewController.getTop()"); + if (mKeyguardShowing) { + // Notifications have the space between those two lines. + drawDebugInfo(canvas, + mNotificationStackScrollLayoutController.getTop() + + (int) mKeyguardNotificationTopPadding, + Color.RED, + "NSSL.getTop() + mKeyguardNotificationTopPadding"); + + drawDebugInfo(canvas, mNotificationStackScrollLayoutController.getBottom() - + (int) mKeyguardNotificationBottomPadding, + Color.RED, + "NSSL.getBottom() - mKeyguardNotificationBottomPadding"); + } + mDebugPaint.setColor(Color.CYAN); canvas.drawLine(0, mClockPositionResult.stackScrollerPadding, mView.getWidth(), mNotificationStackScrollLayoutController.getTopPadding(), mDebugPaint); diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt index 3329eabc80ad..ad734914170b 100644 --- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt @@ -71,6 +71,11 @@ class UserSwitcherActivity @Inject constructor( private var popupMenu: UserSwitcherPopupMenu? = null private lateinit var addButton: View private var addUserRecords = mutableListOf<UserRecord>() + private val userSwitchedCallback: UserTracker.Callback = object : UserTracker.Callback { + override fun onUserChanged(newUser: Int, userContext: Context) { + finish() + } + } // When the add users options become available, insert another option to manage users private val manageUserRecord = UserRecord( null /* info */, @@ -215,11 +220,7 @@ class UserSwitcherActivity @Inject constructor( initBroadcastReceiver() parent.post { buildUserViews() } - userTracker.addCallback(object : UserTracker.Callback { - override fun onUserChanged(newUser: Int, userContext: Context) { - finish() - } - }, mainExecutor) + userTracker.addCallback(userSwitchedCallback, mainExecutor) } private fun showPopupMenu() { @@ -340,6 +341,7 @@ class UserSwitcherActivity @Inject constructor( super.onDestroy() broadcastDispatcher.unregisterReceiver(broadcastReceiver) + userTracker.removeCallback(userSwitchedCallback) } private fun initBroadcastReceiver() { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt index 747649006b45..39cc34bb7e26 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt @@ -18,6 +18,7 @@ package com.android.keyguard import android.content.ContentResolver import android.database.ContentObserver +import android.hardware.biometrics.BiometricFaceConstants import android.net.Uri import android.os.Handler import android.os.UserHandle @@ -44,18 +45,20 @@ class ActiveUnlockConfigTest : SysuiTestCase() { private val fakeWakeUri = Uri.Builder().appendPath("wake").build() private val fakeUnlockIntentUri = Uri.Builder().appendPath("unlock-intent").build() private val fakeBioFailUri = Uri.Builder().appendPath("bio-fail").build() + private val fakeFaceErrorsUri = Uri.Builder().appendPath("face-errors").build() + private val fakeFaceAcquiredUri = Uri.Builder().appendPath("face-acquired").build() + private val fakeUnlockIntentBioEnroll = Uri.Builder().appendPath("unlock-intent-bio").build() @Mock private lateinit var secureSettings: SecureSettings - @Mock private lateinit var contentResolver: ContentResolver - @Mock private lateinit var handler: Handler - @Mock private lateinit var dumpManager: DumpManager + @Mock + private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Captor private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver> @@ -72,6 +75,13 @@ class ActiveUnlockConfigTest : SysuiTestCase() { .thenReturn(fakeUnlockIntentUri) `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)) .thenReturn(fakeBioFailUri) + `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS)) + .thenReturn(fakeFaceErrorsUri) + `when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO)) + .thenReturn(fakeFaceAcquiredUri) + `when`(secureSettings.getUriFor( + Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)) + .thenReturn(fakeUnlockIntentBioEnroll) activeUnlockConfig = ActiveUnlockConfig( handler, @@ -99,12 +109,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() { // WHEN unlock on wake is allowed `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_WAKE, 0, 0)).thenReturn(1) - settingsObserverCaptor.value.onChange( - false, - listOf(fakeWakeUri), - 0, - 0 - ) + updateSetting(fakeWakeUri) // THEN active unlock triggers allowed on: wake, unlock-intent, and biometric failure assertTrue( @@ -134,12 +139,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() { // WHEN unlock on biometric failed is allowed `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 0, 0)).thenReturn(1) - settingsObserverCaptor.value.onChange( - false, - listOf(fakeUnlockIntentUri), - 0, - 0 - ) + updateSetting(fakeUnlockIntentUri) // THEN active unlock triggers allowed on: biometric failure ONLY assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( @@ -154,19 +154,19 @@ class ActiveUnlockConfigTest : SysuiTestCase() { fun testOnBioFailSettingChanged() { verifyRegisterSettingObserver() - // GIVEN no active unlock settings enabled + // GIVEN no active unlock settings enabled and triggering unlock intent on biometric + // enrollment setting is disabled (empty string is disabled, null would use the default) + `when`(secureSettings.getStringForUser( + Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, + 0)).thenReturn("") + updateSetting(fakeUnlockIntentBioEnroll) assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL)) // WHEN unlock on biometric failed is allowed `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 0, 0)).thenReturn(1) - settingsObserverCaptor.value.onChange( - false, - listOf(fakeBioFailUri), - 0, - 0 - ) + updateSetting(fakeBioFailUri) // THEN active unlock triggers allowed on: biometric failure ONLY assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( @@ -177,21 +177,146 @@ class ActiveUnlockConfigTest : SysuiTestCase() { ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL)) } - private fun verifyRegisterSettingObserver() { - verify(contentResolver).registerContentObserver( - eq(fakeWakeUri), - eq(false), - capture(settingsObserverCaptor), - eq(UserHandle.USER_ALL)) + @Test + fun testFaceErrorSettingsChanged() { + verifyRegisterSettingObserver() - verify(contentResolver).registerContentObserver( - eq(fakeUnlockIntentUri), - eq(false), - capture(settingsObserverCaptor), - eq(UserHandle.USER_ALL)) + // GIVEN unlock on biometric fail + `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, + 0, 0)).thenReturn(1) + updateSetting(fakeBioFailUri) + + // WHEN face error timeout (3), allow trigger active unlock + `when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS, + 0)).thenReturn("3") + updateSetting(fakeFaceAcquiredUri) + + // THEN active unlock triggers allowed on error TIMEOUT + assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError( + BiometricFaceConstants.FACE_ERROR_TIMEOUT)) + + assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError( + BiometricFaceConstants.FACE_ERROR_CANCELED)) + } + + @Test + fun testFaceAcquiredSettingsChanged() { + verifyRegisterSettingObserver() + + // GIVEN unlock on biometric fail + `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, + 0, 0)).thenReturn(1) + updateSetting(fakeBioFailUri) + + // WHEN face acquiredMsg DARK_GLASSESand MOUTH_COVERING are allowed to trigger + `when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO, + 0)).thenReturn( + "${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" + + "|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}") + updateSetting(fakeFaceAcquiredUri) + + // THEN active unlock triggers allowed on acquired messages DARK_GLASSES & MOUTH_COVERING + assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo( + BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED)) + assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo( + BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED)) + + assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo( + BiometricFaceConstants.FACE_ACQUIRED_GOOD)) + assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo( + BiometricFaceConstants.FACE_ACQUIRED_NOT_DETECTED)) + } + + @Test + fun testTriggerOnUnlockIntentWhenBiometricEnrolledNone() { + verifyRegisterSettingObserver() + + // GIVEN unlock on biometric fail + `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, + 0, 0)).thenReturn(1) + updateSetting(fakeBioFailUri) + + // GIVEN fingerprint and face are NOT enrolled + activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor + `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(false) + `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false) + + // WHEN unlock intent is allowed when NO biometrics are enrolled (0) + `when`(secureSettings.getStringForUser( + Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, + 0)).thenReturn("${ActiveUnlockConfig.BIOMETRIC_TYPE_NONE}") + updateSetting(fakeUnlockIntentBioEnroll) + + // THEN active unlock triggers allowed on unlock intent + assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( + ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT)) + } + + @Test + fun testTriggerOnUnlockIntentWhenBiometricEnrolledFingerprintOrFaceOnly() { + verifyRegisterSettingObserver() + + // GIVEN unlock on biometric fail + `when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, + 0, 0)).thenReturn(1) + updateSetting(fakeBioFailUri) + + // GIVEN fingerprint and face are both enrolled + activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor + `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true) + `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true) + + // WHEN unlock intent is allowed when ONLY fingerprint is enrolled or NO biometircs + // are enrolled + `when`(secureSettings.getStringForUser( + Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, + 0)).thenReturn( + "${ActiveUnlockConfig.BIOMETRIC_TYPE_ANY_FACE}" + + "|${ActiveUnlockConfig.BIOMETRIC_TYPE_ANY_FINGERPRINT}") + updateSetting(fakeUnlockIntentBioEnroll) + + // THEN active unlock triggers NOT allowed on unlock intent + assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( + ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT)) + + // WHEN fingerprint ONLY enrolled + `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(false) + `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true) + + // THEN active unlock triggers allowed on unlock intent + assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( + ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT)) + + // WHEN face ONLY enrolled + `when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true) + `when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false) + + // THEN active unlock triggers allowed on unlock intent + assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin( + ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT)) + } + + private fun updateSetting(uri: Uri) { + settingsObserverCaptor.value.onChange( + false, + listOf(uri), + 0, + 0 /* flags */ + ) + } + + private fun verifyRegisterSettingObserver() { + verifyRegisterSettingObserver(fakeWakeUri) + verifyRegisterSettingObserver(fakeUnlockIntentUri) + verifyRegisterSettingObserver(fakeBioFailUri) + verifyRegisterSettingObserver(fakeFaceErrorsUri) + verifyRegisterSettingObserver(fakeFaceAcquiredUri) + verifyRegisterSettingObserver(fakeUnlockIntentBioEnroll) + } + private fun verifyRegisterSettingObserver(uri: Uri) { verify(contentResolver).registerContentObserver( - eq(fakeBioFailUri), + eq(uri), eq(false), capture(settingsObserverCaptor), eq(UserHandle.USER_ALL)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java index b87a7e78f73a..0fdd9054e4bc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java @@ -168,7 +168,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { mController.onViewAttached(); verify(mView, atLeast(1)).setPauseAuth(true); - verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount, true); + verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount, + UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN); } @Test @@ -195,7 +196,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { final float eased = .65f; mStatusBarStateListener.onDozeAmountChanged(linear, eased); - verify(mView).onDozeAmountChanged(linear, eased, true); + verify(mView).onDozeAmountChanged(linear, eased, + UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index ae387e86de9d..4eeb4acf18b8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -45,7 +45,6 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; import com.android.systemui.doze.DozeTriggers.DozingUpdateUiEvent; import com.android.systemui.statusbar.phone.DozeParameters; -import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DevicePostureController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; @@ -91,8 +90,6 @@ public class DozeTriggersTest extends SysuiTestCase { private KeyguardStateController mKeyguardStateController; @Mock private DevicePostureController mDevicePostureController; - @Mock - private BatteryController mBatteryController; private DozeTriggers mTriggers; private FakeSensorManager mSensors; @@ -125,7 +122,7 @@ public class DozeTriggersTest extends SysuiTestCase { asyncSensorManager, wakeLock, mDockManager, mProximitySensor, mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(), mAuthController, mExecutor, mUiEventLogger, mKeyguardStateController, - mDevicePostureController, mBatteryController); + mDevicePostureController); mTriggers.setDozeMachine(mMachine); waitForSensorManager(); } @@ -233,9 +230,7 @@ public class DozeTriggersTest extends SysuiTestCase { when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); // WHEN the pick up gesture is triggered and keyguard isn't occluded - // and device isn't on a wireless charger when(mKeyguardStateController.isOccluded()).thenReturn(false); - when(mBatteryController.isPluggedInWireless()).thenReturn(false); mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null); // THEN wakeup @@ -249,22 +244,6 @@ public class DozeTriggersTest extends SysuiTestCase { // WHEN the pick up gesture is triggered and keyguard IS occluded when(mKeyguardStateController.isOccluded()).thenReturn(true); - when(mBatteryController.isPluggedInWireless()).thenReturn(false); - mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null); - - // THEN never wakeup - verify(mMachine, never()).wakeUp(); - } - - @Test - public void testPickupGestureWirelessCharger() { - // GIVEN device is in doze (screen blank, but running doze sensors) - when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); - - // WHEN the pick up gesture is triggered - // and device IS on a wireless charger - when(mKeyguardStateController.isOccluded()).thenReturn(false); - when(mBatteryController.isPluggedInWireless()).thenReturn(true); mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null); // THEN never wakeup diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java index 5ff316e2efec..c532ed5ab651 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java @@ -34,8 +34,6 @@ import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.AnimatedStateListDrawable; import android.hardware.biometrics.BiometricSourceType; -import android.hardware.biometrics.SensorLocationInternal; -import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.Pair; @@ -77,9 +75,6 @@ import org.mockito.MockitoAnnotations; import org.mockito.MockitoSession; import org.mockito.quality.Strictness; -import java.util.ArrayList; -import java.util.List; - @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -182,7 +177,7 @@ public class LockIconViewControllerTest extends SysuiTestCase { @Test public void testUpdateFingerprintLocationOnInit() { // GIVEN fp sensor location is available pre-attached - Pair<Integer, PointF> udfps = setupUdfps(); // first = radius, second = udfps location + Pair<Float, PointF> udfps = setupUdfps(); // first = radius, second = udfps location // WHEN lock icon view controller is initialized and attached mLockIconViewController.init(); @@ -197,7 +192,7 @@ public class LockIconViewControllerTest extends SysuiTestCase { @Test public void testUpdatePaddingBasedOnResolutionScale() { // GIVEN fp sensor location is available pre-attached & scaled resolution factor is 5 - Pair<Integer, PointF> udfps = setupUdfps(); // first = radius, second = udfps location + Pair<Float, PointF> udfps = setupUdfps(); // first = radius, second = udfps location when(mAuthController.getScaleFactor()).thenReturn(5f); // WHEN lock icon view controller is initialized and attached @@ -215,20 +210,19 @@ public class LockIconViewControllerTest extends SysuiTestCase { // GIVEN fp sensor location is not available pre-init when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false); when(mAuthController.getFingerprintSensorLocation()).thenReturn(null); - when(mAuthController.getUdfpsProps()).thenReturn(null); mLockIconViewController.init(); captureAttachListener(); mAttachListener.onViewAttachedToWindow(mLockIconView); - // GIVEN fp sensor location is available post-atttached + // GIVEN fp sensor location is available post-attached captureAuthControllerCallback(); - Pair<Integer, PointF> udfps = setupUdfps(); + Pair<Float, PointF> udfps = setupUdfps(); // WHEN all authenticators are registered mAuthControllerCallback.onAllAuthenticatorsRegistered(); mDelayableExecutor.runAllReady(); - // THEN lock icon view location is updated with the same coordinates as fpProps + // THEN lock icon view location is updated with the same coordinates as auth controller vals verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), eq(PADDING)); } @@ -402,21 +396,10 @@ public class LockIconViewControllerTest extends SysuiTestCase { verify(mLockIconView).setTranslationX(0); } - private Pair<Integer, PointF> setupUdfps() { + private Pair<Float, PointF> setupUdfps() { when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true); final PointF udfpsLocation = new PointF(50, 75); - final int radius = 33; - final FingerprintSensorPropertiesInternal fpProps = - new FingerprintSensorPropertiesInternal( - /* sensorId */ 0, - /* strength */ 0, - /* max enrollments per user */ 5, - /* component info */ new ArrayList<>(), - /* sensorType */ 3, - /* halControlsIllumination */ true, - /* resetLockoutRequiresHwToken */ false, - List.of(new SensorLocationInternal("" /* displayId */, - (int) udfpsLocation.x, (int) udfpsLocation.y, radius))); + final float radius = 33f; when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocation); when(mAuthController.getUdfpsRadius()).thenReturn(radius); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index f9fb8657d0f6..18aae0edd846 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -21,7 +21,6 @@ import android.animation.AnimatorSet import android.app.PendingIntent import android.app.smartspace.SmartspaceAction import android.content.Context -import org.mockito.Mockito.`when` as whenever import android.content.Intent import android.content.pm.ApplicationInfo import android.content.pm.PackageManager @@ -59,6 +58,7 @@ import com.android.systemui.ActivityIntentHelper import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastSender +import com.android.systemui.media.MediaControlPanel.KEY_SMARTSPACE_APP_NAME import com.android.systemui.media.dialog.MediaOutputDialogFactory import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.FalsingManager @@ -67,8 +67,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.animation.TransitionLayout import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.KotlinArgumentCaptor -import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.nullable import com.android.systemui.util.mockito.withArgCaptor @@ -92,6 +92,7 @@ import org.mockito.Mockito.reset import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit +import org.mockito.Mockito.`when` as whenever private const val KEY = "TEST_KEY" private const val PACKAGE = "PKG" @@ -102,6 +103,7 @@ private const val SESSION_KEY = "SESSION_KEY" private const val SESSION_ARTIST = "SESSION_ARTIST" private const val SESSION_TITLE = "SESSION_TITLE" private const val DISABLED_DEVICE_NAME = "DISABLED_DEVICE_NAME" +private const val REC_APP_NAME = "REC APP NAME" @SmallTest @RunWith(AndroidTestingRunner::class) @@ -262,6 +264,7 @@ public class MediaControlPanelTest : SysuiTestCase() { // Set valid recommendation data val extras = Bundle() + extras.putString(KEY_SMARTSPACE_APP_NAME, REC_APP_NAME) val intent = Intent().apply { putExtras(extras) setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) @@ -339,6 +342,7 @@ public class MediaControlPanelTest : SysuiTestCase() { whenever(viewHolder.player).thenReturn(view) whenever(viewHolder.appIcon).thenReturn(appIcon) whenever(viewHolder.albumView).thenReturn(albumView) + whenever(albumView.foreground).thenReturn(mock(Drawable::class.java)) whenever(viewHolder.titleText).thenReturn(titleText) whenever(viewHolder.artistText).thenReturn(artistText) whenever(seamlessBackground.getDrawable(0)).thenReturn(mock(GradientDrawable::class.java)) @@ -1121,6 +1125,91 @@ public class MediaControlPanelTest : SysuiTestCase() { verify(mediaCarouselController).removePlayer(eq(mediaKey), eq(false), eq(false)) } + @Test + fun player_gutsOpen_contentDescriptionIsForGuts() { + whenever(mediaViewController.isGutsVisible).thenReturn(true) + player.attachPlayer(viewHolder) + + val gutsTextString = "gutsText" + whenever(gutsText.text).thenReturn(gutsTextString) + player.bindPlayer(mediaData, KEY) + + val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java) + verify(viewHolder.player).contentDescription = descriptionCaptor.capture() + val description = descriptionCaptor.value.toString() + + assertThat(description).isEqualTo(gutsTextString) + } + + @Test + fun player_gutsClosed_contentDescriptionIsForPlayer() { + whenever(mediaViewController.isGutsVisible).thenReturn(false) + player.attachPlayer(viewHolder) + + val app = "appName" + player.bindPlayer(mediaData.copy(app = app), KEY) + + val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java) + verify(viewHolder.player).contentDescription = descriptionCaptor.capture() + val description = descriptionCaptor.value.toString() + + assertThat(description).contains(mediaData.song!!) + assertThat(description).contains(mediaData.artist!!) + assertThat(description).contains(app) + } + + @Test + fun player_gutsChangesFromOpenToClosed_contentDescriptionUpdated() { + // Start out open + whenever(mediaViewController.isGutsVisible).thenReturn(true) + whenever(gutsText.text).thenReturn("gutsText") + player.attachPlayer(viewHolder) + val app = "appName" + player.bindPlayer(mediaData.copy(app = app), KEY) + + // Update to closed by long pressing + val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java) + verify(viewHolder.player).onLongClickListener = captor.capture() + reset(viewHolder.player) + + whenever(mediaViewController.isGutsVisible).thenReturn(false) + captor.value.onLongClick(viewHolder.player) + + // Then content description is now the player content description + val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java) + verify(viewHolder.player).contentDescription = descriptionCaptor.capture() + val description = descriptionCaptor.value.toString() + + assertThat(description).contains(mediaData.song!!) + assertThat(description).contains(mediaData.artist!!) + assertThat(description).contains(app) + } + + @Test + fun player_gutsChangesFromClosedToOpen_contentDescriptionUpdated() { + // Start out closed + whenever(mediaViewController.isGutsVisible).thenReturn(false) + val gutsTextString = "gutsText" + whenever(gutsText.text).thenReturn(gutsTextString) + player.attachPlayer(viewHolder) + player.bindPlayer(mediaData.copy(app = "appName"), KEY) + + // Update to open by long pressing + val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java) + verify(viewHolder.player).onLongClickListener = captor.capture() + reset(viewHolder.player) + + whenever(mediaViewController.isGutsVisible).thenReturn(true) + captor.value.onLongClick(viewHolder.player) + + // Then content description is now the guts content description + val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java) + verify(viewHolder.player).contentDescription = descriptionCaptor.capture() + val description = descriptionCaptor.value.toString() + + assertThat(description).isEqualTo(gutsTextString) + } + /* ***** END guts tests for the player ***** */ /* ***** Guts tests for the recommendations ***** */ @@ -1189,6 +1278,85 @@ public class MediaControlPanelTest : SysuiTestCase() { verify(mediaDataManager).dismissSmartspaceRecommendation(eq(mediaKey), anyLong()) } + @Test + fun recommendation_gutsOpen_contentDescriptionIsForGuts() { + whenever(mediaViewController.isGutsVisible).thenReturn(true) + player.attachRecommendation(recommendationViewHolder) + + val gutsTextString = "gutsText" + whenever(gutsText.text).thenReturn(gutsTextString) + player.bindRecommendation(smartspaceData) + + val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java) + verify(viewHolder.player).contentDescription = descriptionCaptor.capture() + val description = descriptionCaptor.value.toString() + + assertThat(description).isEqualTo(gutsTextString) + } + + @Test + fun recommendation_gutsClosed_contentDescriptionIsForPlayer() { + whenever(mediaViewController.isGutsVisible).thenReturn(false) + player.attachRecommendation(recommendationViewHolder) + + player.bindRecommendation(smartspaceData) + + val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java) + verify(viewHolder.player).contentDescription = descriptionCaptor.capture() + val description = descriptionCaptor.value.toString() + + assertThat(description).contains(REC_APP_NAME) + } + + @Test + fun recommendation_gutsChangesFromOpenToClosed_contentDescriptionUpdated() { + // Start out open + whenever(mediaViewController.isGutsVisible).thenReturn(true) + whenever(gutsText.text).thenReturn("gutsText") + player.attachRecommendation(recommendationViewHolder) + player.bindRecommendation(smartspaceData) + + // Update to closed by long pressing + val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java) + verify(viewHolder.player).onLongClickListener = captor.capture() + reset(viewHolder.player) + + whenever(mediaViewController.isGutsVisible).thenReturn(false) + captor.value.onLongClick(viewHolder.player) + + // Then content description is now the player content description + val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java) + verify(viewHolder.player).contentDescription = descriptionCaptor.capture() + val description = descriptionCaptor.value.toString() + + assertThat(description).contains(REC_APP_NAME) + } + + @Test + fun recommendation_gutsChangesFromClosedToOpen_contentDescriptionUpdated() { + // Start out closed + whenever(mediaViewController.isGutsVisible).thenReturn(false) + val gutsTextString = "gutsText" + whenever(gutsText.text).thenReturn(gutsTextString) + player.attachRecommendation(recommendationViewHolder) + player.bindRecommendation(smartspaceData) + + // Update to open by long pressing + val captor = ArgumentCaptor.forClass(View.OnLongClickListener::class.java) + verify(viewHolder.player).onLongClickListener = captor.capture() + reset(viewHolder.player) + + whenever(mediaViewController.isGutsVisible).thenReturn(true) + captor.value.onLongClick(viewHolder.player) + + // Then content description is now the guts content description + val descriptionCaptor = ArgumentCaptor.forClass(CharSequence::class.java) + verify(viewHolder.player).contentDescription = descriptionCaptor.capture() + val description = descriptionCaptor.value.toString() + + assertThat(description).isEqualTo(gutsTextString) + } + /* ***** END guts tests for the recommendations ***** */ @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 4ea932157457..0f2e9bccc215 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -318,15 +318,6 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { } @Test - public void testGetIsNonblockable_oemLocked() throws Exception { - ExpandableNotificationRow row = - mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification()); - row.getEntry().getChannel().setImportanceLockedByOEM(true); - - assertTrue(row.getIsNonblockable()); - } - - @Test public void testGetIsNonblockable_criticalDeviceFunction() throws Exception { ExpandableNotificationRow row = mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java index 6d3a5fecc826..ec20271ea43b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java @@ -336,7 +336,7 @@ public class KeyguardClockPositionAlgorithmTest extends SysuiTestCase { // WHEN the position algorithm is run positionClock(); // THEN the padding DOESN'T adjust for keyguard status height. - assertThat(mClockPositionAlgorithm.getMinStackScrollerPadding()) + assertThat(mClockPositionAlgorithm.getLockscreenMinStackScrollerPadding()) .isEqualTo(mKeyguardStatusBarHeaderHeight); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java index 94e6b9a12a93..11f96cec6369 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java @@ -610,13 +610,13 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { when(mLockIconViewController.getTop()).thenReturn(80f); when(mResources.getDimensionPixelSize(R.dimen.shelf_and_lock_icon_overlap)).thenReturn(5); - // Available space (100 - 10 - 15 = 75) + // Available space (100 - 0 - 15 = 85) when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(100); - when(mNotificationStackScrollLayoutController.getTopPadding()).thenReturn(10); + when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0); mNotificationPanelViewController.updateResources(); assertThat(mNotificationPanelViewController.getSpaceForLockscreenNotifications()) - .isEqualTo(75); + .isEqualTo(85); } @Test diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 649328d1eef3..9b29bae1fc46 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -242,6 +242,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ int getCurrentUserIdLocked(); + Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec( + int windowId); + boolean isAccessibilityButtonShown(); /** @@ -551,8 +554,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int resolvedWindowId; RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); - final MagnificationSpec spec; - final float[] transformMatrix; synchronized (mLock) { mUsesAccessibilityCache = true; if (!hasRightsToCurrentUserLocked()) { @@ -576,11 +577,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ partialInteractiveRegion.recycle(); partialInteractiveRegion = null; } - final Pair<float[], MagnificationSpec> transformMatrixAndSpec = - getTransformMatrixAndSpecLocked(resolvedWindowId); - transformMatrix = transformMatrixAndSpec.first; - spec = transformMatrixAndSpec.second; } + final Pair<float[], MagnificationSpec> transformMatrixAndSpec = + getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId); + final float[] transformMatrix = transformMatrixAndSpec.first; + final MagnificationSpec spec = transformMatrixAndSpec.second; if (!mSecurityPolicy.checkAccessibilityAccess(this)) { return null; } @@ -628,8 +629,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int resolvedWindowId; RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); - final MagnificationSpec spec; - final float [] transformMatrix; synchronized (mLock) { mUsesAccessibilityCache = true; if (!hasRightsToCurrentUserLocked()) { @@ -653,11 +652,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ partialInteractiveRegion.recycle(); partialInteractiveRegion = null; } - final Pair<float[], MagnificationSpec> transformMatrixAndSpec = - getTransformMatrixAndSpecLocked(resolvedWindowId); - transformMatrix = transformMatrixAndSpec.first; - spec = transformMatrixAndSpec.second; } + final Pair<float[], MagnificationSpec> transformMatrixAndSpec = + getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId); + final float[] transformMatrix = transformMatrixAndSpec.first; + final MagnificationSpec spec = transformMatrixAndSpec.second; if (!mSecurityPolicy.checkAccessibilityAccess(this)) { return null; } @@ -706,8 +705,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int resolvedWindowId; RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); - final MagnificationSpec spec; - final float[] transformMatrix; synchronized (mLock) { mUsesAccessibilityCache = true; if (!hasRightsToCurrentUserLocked()) { @@ -731,11 +728,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ partialInteractiveRegion.recycle(); partialInteractiveRegion = null; } - final Pair<float[], MagnificationSpec> transformMatrixAndSpec = - getTransformMatrixAndSpecLocked(resolvedWindowId); - transformMatrix = transformMatrixAndSpec.first; - spec = transformMatrixAndSpec.second; } + final Pair<float[], MagnificationSpec> transformMatrixAndSpec = + getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId); + final float[] transformMatrix = transformMatrixAndSpec.first; + final MagnificationSpec spec = transformMatrixAndSpec.second; if (!mSecurityPolicy.checkAccessibilityAccess(this)) { return null; } @@ -786,8 +783,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int resolvedWindowId; RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); - final MagnificationSpec spec; - final float[] transformMatrix; synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { return null; @@ -811,11 +806,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ partialInteractiveRegion.recycle(); partialInteractiveRegion = null; } - final Pair<float[], MagnificationSpec> transformMatrixAndSpec = - getTransformMatrixAndSpecLocked(resolvedWindowId); - transformMatrix = transformMatrixAndSpec.first; - spec = transformMatrixAndSpec.second; } + final Pair<float[], MagnificationSpec> transformMatrixAndSpec = + getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId); + final float[] transformMatrix = transformMatrixAndSpec.first; + final MagnificationSpec spec = transformMatrixAndSpec.second; if (!mSecurityPolicy.checkAccessibilityAccess(this)) { return null; } @@ -865,8 +860,6 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ final int resolvedWindowId; RemoteAccessibilityConnection connection; Region partialInteractiveRegion = Region.obtain(); - final MagnificationSpec spec; - final float[] transformMatrix; synchronized (mLock) { if (!hasRightsToCurrentUserLocked()) { return null; @@ -889,12 +882,11 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ partialInteractiveRegion.recycle(); partialInteractiveRegion = null; } - - final Pair<float[], MagnificationSpec> transformMatrixAndSpec = - getTransformMatrixAndSpecLocked(resolvedWindowId); - transformMatrix = transformMatrixAndSpec.first; - spec = transformMatrixAndSpec.second; } + final Pair<float[], MagnificationSpec> transformMatrixAndSpec = + getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId); + final float[] transformMatrix = transformMatrixAndSpec.first; + final MagnificationSpec spec = transformMatrixAndSpec.second; if (!mSecurityPolicy.checkAccessibilityAccess(this)) { return null; } @@ -1672,21 +1664,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ mInvocationHandler.startInputLocked(connection, editorInfo, restarting); } - - @Nullable - Pair<float[], MagnificationSpec> getTransformMatrixAndSpecLocked(int resolvedWindowId) { - final WindowInfo windowInfo = - mA11yWindowManager.findWindowInfoByIdLocked(resolvedWindowId); - if (windowInfo == null) { - Slog.w(LOG_TAG, "getTransformMatrixAndSpec, windowInfo is null window id = " - + resolvedWindowId); - return new Pair<>(null, null); - } - - final MagnificationSpec spec = new MagnificationSpec(); - spec.setTo(windowInfo.mMagnificationSpec); - return new Pair<>(windowInfo.mTransformMatrix, spec); + private Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec( + int resolvedWindowId) { + return mSystemSupport.getWindowTransformationMatrixAndMagnificationSpec(resolvedWindowId); } /** diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 4806514a0e13..99c849527034 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -68,6 +68,7 @@ import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.database.ContentObserver; +import android.graphics.Matrix; import android.graphics.Point; import android.graphics.Rect; import android.graphics.Region; @@ -99,6 +100,7 @@ import android.text.TextUtils; import android.text.TextUtils.SimpleStringSplitter; import android.util.ArraySet; import android.util.IntArray; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.view.Display; @@ -458,6 +460,41 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } @Override + public Pair<float[], MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec( + int windowId) { + WindowInfo windowInfo; + synchronized (mLock) { + windowInfo = mA11yWindowManager.findWindowInfoByIdLocked(windowId); + } + if (windowInfo != null) { + final MagnificationSpec spec = new MagnificationSpec(); + spec.setTo(windowInfo.mMagnificationSpec); + return new Pair<>(windowInfo.mTransformMatrix, spec); + } else { + // If the framework doesn't track windows, we fall back to get the pair of + // transformation matrix and MagnificationSpe from the WindowManagerService's + // WindowState. + IBinder token; + synchronized (mLock) { + token = mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(mCurrentUserId, + windowId); + } + Pair<Matrix, MagnificationSpec> pair = + mWindowManagerService.getWindowTransformationMatrixAndMagnificationSpec(token); + final float[] outTransformationMatrix = new float[9]; + final Matrix tmpMatrix = pair.first; + final MagnificationSpec spec = pair.second; + if (!spec.isNop()) { + tmpMatrix.postScale(spec.scale, spec.scale); + tmpMatrix.postTranslate(spec.offsetX, spec.offsetY); + } + tmpMatrix.getValues(outTransformationMatrix); + + return new Pair<>(outTransformationMatrix, pair.second); + } + } + + @Override public void onServiceInfoChangedLocked(AccessibilityUserState userState) { mSecurityPolicy.onBoundServicesChangedLocked(userState.mUserId, userState.mBoundServices); @@ -3750,12 +3787,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub boundsInScreenBeforeMagnification.centerY()); // Invert magnification if needed. - final WindowInfo windowInfo = mA11yWindowManager.findWindowInfoByIdLocked( - focus.getWindowId()); + final Pair<float[], MagnificationSpec> pair = + getWindowTransformationMatrixAndMagnificationSpec(focus.getWindowId()); MagnificationSpec spec = null; - if (windowInfo != null) { + if (pair != null && pair.second != null) { spec = new MagnificationSpec(); - spec.setTo(windowInfo.mMagnificationSpec); + spec.setTo(pair.second); } if (spec != null && !spec.isNop()) { diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 8e32a7a8e255..6d3620fa2ee6 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -3225,10 +3225,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN); mService.logDatasetShown(id, mClientState, UI_TYPE_DIALOG); } + // Just show fill dialog once, so disabled after shown. + // Note: Cannot disable before requestShowFillDialog() because the method + // need to check whether fill dialog enabled. + setFillDialogDisabled(); return; } else { setFillDialogDisabled(); } + } if (response.supportsInlineSuggestions()) { diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 62bb9f155c34..2a22cac75ae0 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -62,7 +62,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageInfo; -import android.content.pm.PackageItemInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.UserInfo; @@ -81,7 +80,6 @@ import android.os.ServiceManager; import android.os.ShellCallback; import android.os.UserHandle; import android.os.UserManager; -import android.text.BidiFormatter; import android.util.ArraySet; import android.util.ExceptionUtils; import android.util.Log; @@ -538,20 +536,12 @@ public class CompanionDeviceManagerService extends SystemService { String callingPackage = component.getPackageName(); checkCanCallNotificationApi(callingPackage); // TODO: check userId. - String packageTitle = BidiFormatter.getInstance().unicodeWrap( - getPackageInfo(getContext(), userId, callingPackage) - .applicationInfo - .loadSafeLabel(getContext().getPackageManager(), - PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX, - PackageItemInfo.SAFE_LABEL_FLAG_TRIM - | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE) - .toString()); final long identity = Binder.clearCallingIdentity(); try { return PendingIntent.getActivityAsUser(getContext(), 0 /* request code */, NotificationAccessConfirmationActivityContract.launcherIntent( - getContext(), userId, component, packageTitle), + getContext(), userId, component), PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT, null /* options */, @@ -732,9 +722,12 @@ public class CompanionDeviceManagerService extends SystemService { String[] args, ShellCallback callback, ResultReceiver resultReceiver) throws RemoteException { enforceCallerCanManageCompanionDevice(getContext(), "onShellCommand"); - new CompanionDeviceShellCommand( - CompanionDeviceManagerService.this, mAssociationStore) - .exec(this, in, out, err, args, callback, resultReceiver); + + final CompanionDeviceShellCommand cmd = new CompanionDeviceShellCommand( + CompanionDeviceManagerService.this, + mAssociationStore, + mDevicePresenceMonitor); + cmd.exec(this, in, out, err, args, callback, resultReceiver); } @Override diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java index fd130852a43a..6a19a42723c5 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java @@ -21,6 +21,8 @@ import android.os.ShellCommand; import android.util.Log; import android.util.Slog; +import com.android.server.companion.presence.CompanionDevicePresenceMonitor; + import java.io.PrintWriter; import java.util.List; @@ -29,20 +31,24 @@ class CompanionDeviceShellCommand extends ShellCommand { private final CompanionDeviceManagerService mService; private final AssociationStore mAssociationStore; + private final CompanionDevicePresenceMonitor mDevicePresenceMonitor; CompanionDeviceShellCommand(CompanionDeviceManagerService service, - AssociationStore associationStore) { + AssociationStore associationStore, + CompanionDevicePresenceMonitor devicePresenceMonitor) { mService = service; mAssociationStore = associationStore; + mDevicePresenceMonitor = devicePresenceMonitor; } @Override public int onCommand(String cmd) { final PrintWriter out = getOutPrintWriter(); + final int associationId; try { switch (cmd) { case "list": { - final int userId = getNextArgInt(); + final int userId = getNextIntArgRequired(); final List<AssociationInfo> associationsForUser = mAssociationStore.getAssociationsForUser(userId); for (AssociationInfo association : associationsForUser) { @@ -55,7 +61,7 @@ class CompanionDeviceShellCommand extends ShellCommand { break; case "associate": { - int userId = getNextArgInt(); + int userId = getNextIntArgRequired(); String packageName = getNextArgRequired(); String address = getNextArgRequired(); mService.legacyCreateAssociation(userId, address, packageName, null); @@ -63,7 +69,7 @@ class CompanionDeviceShellCommand extends ShellCommand { break; case "disassociate": { - final int userId = getNextArgInt(); + final int userId = getNextIntArgRequired(); final String packageName = getNextArgRequired(); final String address = getNextArgRequired(); final AssociationInfo association = @@ -80,6 +86,16 @@ class CompanionDeviceShellCommand extends ShellCommand { } break; + case "simulate-device-appeared": + associationId = getNextIntArgRequired(); + mDevicePresenceMonitor.simulateDeviceAppeared(associationId); + break; + + case "simulate-device-disappeared": + associationId = getNextIntArgRequired(); + mDevicePresenceMonitor.simulateDeviceDisappeared(associationId); + break; + default: return handleDefaultCommands(cmd); } @@ -91,10 +107,6 @@ class CompanionDeviceShellCommand extends ShellCommand { } } - private int getNextArgInt() { - return Integer.parseInt(getNextArgRequired()); - } - @Override public void onHelp() { PrintWriter pw = getOutPrintWriter(); @@ -108,7 +120,31 @@ class CompanionDeviceShellCommand extends ShellCommand { pw.println(" disassociate USER_ID PACKAGE MAC_ADDRESS"); pw.println(" Remove an existing Association."); pw.println(" clear-association-memory-cache"); - pw.println(" Clear the in-memory association cache and reload all association " - + "information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY."); + pw.println(" Clear the in-memory association cache and reload all association "); + pw.println(" information from persistent storage. USE FOR DEBUGGING PURPOSES ONLY."); + pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + + pw.println(" simulate-device-appeared ASSOCIATION_ID"); + pw.println(" Make CDM act as if the given companion device has appeared."); + pw.println(" I.e. bind the associated companion application's"); + pw.println(" CompanionDeviceService(s) and trigger onDeviceAppeared() callback."); + pw.println(" The CDM will consider the devices as present for 60 seconds and then"); + pw.println(" will act as if device disappeared, unless 'simulate-device-disappeared'"); + pw.println(" or 'simulate-device-appeared' is called again before 60 seconds run out" + + "."); + pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + + pw.println(" simulate-device-disappeared ASSOCIATION_ID"); + pw.println(" Make CDM act as if the given companion device has disappeared."); + pw.println(" I.e. unbind the associated companion application's"); + pw.println(" CompanionDeviceService(s) and trigger onDeviceDisappeared() callback."); + pw.println(" NOTE: This will only have effect if 'simulate-device-appeared' was"); + pw.println(" invoked for the same device (same ASSOCIATION_ID) no longer than"); + pw.println(" 60 seconds ago."); + pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + } + + private int getNextIntArgRequired() { + return Integer.parseInt(getNextArgRequired()); } } diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java index 24be1b6fd701..37e83697c787 100644 --- a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java +++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java @@ -16,11 +16,19 @@ package com.android.server.companion.presence; +import static android.os.Process.ROOT_UID; +import static android.os.Process.SHELL_UID; + import android.annotation.NonNull; import android.annotation.SuppressLint; +import android.annotation.TestApi; import android.bluetooth.BluetoothAdapter; import android.companion.AssociationInfo; import android.content.Context; +import android.os.Binder; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.util.Log; import com.android.server.companion.AssociationStore; @@ -72,6 +80,11 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange private final @NonNull Set<Integer> mNearbyBleDevices = new HashSet<>(); private final @NonNull Set<Integer> mReportedSelfManagedDevices = new HashSet<>(); + // Tracking "simulated" presence. Used for debugging and testing only. + private final @NonNull Set<Integer> mSimulated = new HashSet<>(); + private final SimulatedDevicePresenceSchedulerHelper mSchedulerHelper = + new SimulatedDevicePresenceSchedulerHelper(); + public CompanionDevicePresenceMonitor(@NonNull AssociationStore associationStore, @NonNull Callback callback) { mAssociationStore = associationStore; @@ -106,7 +119,8 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange public boolean isDevicePresent(int associationId) { return mReportedSelfManagedDevices.contains(associationId) || mConnectedBtDevices.contains(associationId) - || mNearbyBleDevices.contains(associationId); + || mNearbyBleDevices.contains(associationId) + || mSimulated.contains(associationId); } /** @@ -155,6 +169,45 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange onDeviceGone(mNearbyBleDevices, associationId, /* sourceLoggingTag */ "ble"); } + /** FOR DEBUGGING AND/OR TESTING PURPOSES ONLY. */ + @TestApi + public void simulateDeviceAppeared(int associationId) { + // IMPORTANT: this API should only be invoked via the + // 'companiondevice simulate-device-appeared' Shell command, so the only uid-s allowed to + // make this call are SHELL and ROOT. + // No other caller (including SYSTEM!) should be allowed. + enforceCallerShellOrRoot(); + // Make sure the association exists. + enforceAssociationExists(associationId); + + onDevicePresent(mSimulated, associationId, /* sourceLoggingTag */ "simulated"); + + mSchedulerHelper.scheduleOnDeviceGoneCallForSimulatedDevicePresence(associationId); + } + + /** FOR DEBUGGING AND/OR TESTING PURPOSES ONLY. */ + @TestApi + public void simulateDeviceDisappeared(int associationId) { + // IMPORTANT: this API should only be invoked via the + // 'companiondevice simulate-device-appeared' Shell command, so the only uid-s allowed to + // make this call are SHELL and ROOT. + // No other caller (including SYSTEM!) should be allowed. + enforceCallerShellOrRoot(); + // Make sure the association exists. + enforceAssociationExists(associationId); + + mSchedulerHelper.unscheduleOnDeviceGoneCallForSimulatedDevicePresence(associationId); + + onDeviceGone(mSimulated, associationId, /* sourceLoggingTag */ "simulated"); + } + + private void enforceAssociationExists(int associationId) { + if (mAssociationStore.getAssociationById(associationId) == null) { + throw new IllegalArgumentException( + "Association with id " + associationId + " does not exist."); + } + } + private void onDevicePresent(@NonNull Set<Integer> presentDevicesForSource, int newDeviceAssociationId, @NonNull String sourceLoggingTag) { if (DEBUG) { @@ -212,6 +265,7 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange mConnectedBtDevices.remove(associationId); mNearbyBleDevices.remove(associationId); mReportedSelfManagedDevices.remove(associationId); + mSimulated.remove(associationId); } /** @@ -232,4 +286,36 @@ public class CompanionDevicePresenceMonitor implements AssociationStore.OnChange // CompanionDeviceManagerService will know that the association is removed, and will do // what's needed. } + + private static void enforceCallerShellOrRoot() { + final int callingUid = Binder.getCallingUid(); + if (callingUid == SHELL_UID || callingUid == ROOT_UID) return; + + throw new SecurityException("Caller is neither Shell nor Root"); + } + + private class SimulatedDevicePresenceSchedulerHelper extends Handler { + SimulatedDevicePresenceSchedulerHelper() { + super(Looper.getMainLooper()); + } + + void scheduleOnDeviceGoneCallForSimulatedDevicePresence(int associationId) { + // First, unschedule if it was scheduled previously. + if (hasMessages(/* what */ associationId)) { + removeMessages(/* what */ associationId); + } + + sendEmptyMessageDelayed(/* what */ associationId, 60 * 1000 /* 60 seconds */); + } + + void unscheduleOnDeviceGoneCallForSimulatedDevicePresence(int associationId) { + removeMessages(/* what */ associationId); + } + + @Override + public void handleMessage(@NonNull Message msg) { + final int associationId = msg.what; + onDeviceGone(mSimulated, associationId, /* sourceLoggingTag */ "simulated"); + } + } } diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java index 5a234f5010dd..c09bb2d4dc28 100644 --- a/services/core/java/com/android/server/am/AppBatteryTracker.java +++ b/services/core/java/com/android/server/am/AppBatteryTracker.java @@ -67,6 +67,7 @@ import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; +import android.util.SparseLongArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; @@ -360,6 +361,7 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> mUidBatteryUsageInWindow.removeAt(i); } } + mInjector.getPolicy().onUserRemovedLocked(userId); } } @@ -368,6 +370,7 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> synchronized (mLock) { mUidBatteryUsage.delete(uid); mUidBatteryUsageInWindow.delete(uid); + mInjector.getPolicy().onUidRemovedLocked(uid); } } @@ -1208,6 +1211,14 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_window"; /** + * The grace period after an interaction event with the app, if the background current + * drain goes beyond the threshold within that period, the system won't apply the + * restrictions. + */ + static final String KEY_BG_CURRENT_DRAIN_INTERACTION_GRACE_PERIOD = + DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_interaction_grace_period"; + + /** * Similar to {@link #KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET}, but a higher * value for the legitimate cases with higher background current drain. */ @@ -1310,6 +1321,11 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> final long mDefaultBgCurrentDrainWindowMs; /** + * Default value to {@link #mBgCurrentDrainInteractionGracePeriodMs}. + */ + final long mDefaultBgCurrentDrainInteractionGracePeriodMs; + + /** * Default value to the {@link #INDEX_HIGH_CURRENT_DRAIN_THRESHOLD} of * the {@link #mBgCurrentDrainRestrictedBucketThreshold}. */ @@ -1394,6 +1410,11 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> volatile long mBgCurrentDrainWindowMs; /** + * @see #KEY_BG_CURRENT_DRAIN_INTERACTION_GRACE_PERIOD. + */ + volatile long mBgCurrentDrainInteractionGracePeriodMs; + + /** * @see #KEY_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION. */ volatile long mBgCurrentDrainMediaPlaybackMinDuration; @@ -1455,6 +1476,12 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> private final SparseArray<Pair<long[], ImmutableBatteryUsage[]>> mHighBgBatteryPackages = new SparseArray<>(); + /** + * The timestamp of the last interaction, key is the UID. + */ + @GuardedBy("mLock") + private final SparseLongArray mLastInteractionTime = new SparseLongArray(); + @NonNull private final Object mLock; @@ -1478,6 +1505,7 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> isLowRamDeviceStatic() ? val[1] : val[0]; mDefaultBgCurrentDrainWindowMs = resources.getInteger( R.integer.config_bg_current_drain_window) * 1_000; + mDefaultBgCurrentDrainInteractionGracePeriodMs = mDefaultBgCurrentDrainWindowMs; val = getFloatArray(resources.obtainTypedArray( R.array.config_bg_current_drain_high_threshold_to_restricted_bucket)); mDefaultBgCurrentDrainRestrictedBucketHighThreshold = @@ -1511,6 +1539,8 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> mBgCurrentDrainBgRestrictedThreshold[1] = mDefaultBgCurrentDrainBgRestrictedHighThreshold; mBgCurrentDrainWindowMs = mDefaultBgCurrentDrainWindowMs; + mBgCurrentDrainInteractionGracePeriodMs = + mDefaultBgCurrentDrainInteractionGracePeriodMs; mBgCurrentDrainMediaPlaybackMinDuration = mDefaultBgCurrentDrainMediaPlaybackMinDuration; mBgCurrentDrainLocationMinDuration = mDefaultBgCurrentDrainLocationMinDuration; @@ -1542,6 +1572,9 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> case KEY_BG_CURRENT_DRAIN_WINDOW: updateCurrentDrainWindow(); break; + case KEY_BG_CURRENT_DRAIN_INTERACTION_GRACE_PERIOD: + updateCurrentDrainInteractionGracePeriod(); + break; case KEY_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION: updateCurrentDrainMediaPlaybackMinDuration(); break; @@ -1626,6 +1659,13 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> mDefaultBgCurrentDrainWindowMs); } + private void updateCurrentDrainInteractionGracePeriod() { + mBgCurrentDrainInteractionGracePeriodMs = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_BG_CURRENT_DRAIN_INTERACTION_GRACE_PERIOD, + mDefaultBgCurrentDrainInteractionGracePeriodMs); + } + private void updateCurrentDrainMediaPlaybackMinDuration() { mBgCurrentDrainMediaPlaybackMinDuration = DeviceConfig.getLong( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, @@ -1668,6 +1708,7 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> super.onSystemReady(); updateCurrentDrainThreshold(); updateCurrentDrainWindow(); + updateCurrentDrainInteractionGracePeriod(); updateCurrentDrainMediaPlaybackMinDuration(); updateCurrentDrainLocationMinDuration(); updateCurrentDrainEventDurationBasedThresholdEnabled(); @@ -1685,8 +1726,10 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> synchronized (mLock) { final Pair<long[], ImmutableBatteryUsage[]> pair = mHighBgBatteryPackages.get(uid); if (pair != null) { + final long lastInteractionTime = mLastInteractionTime.get(uid, 0L); final long[] ts = pair.first; - final int restrictedLevel = ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] > 0 + final int restrictedLevel = ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] + > (lastInteractionTime + mBgCurrentDrainInteractionGracePeriodMs) && mTracker.mAppRestrictionController.isAutoRestrictAbusiveAppEnabled() ? RESTRICTION_LEVEL_RESTRICTED_BUCKET : RESTRICTION_LEVEL_ADAPTIVE_BUCKET; @@ -1777,6 +1820,7 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> // We're already in the background restricted level, nothing more we could do. return; } + final long lastInteractionTime = mLastInteractionTime.get(uid, 0L); final long now = SystemClock.elapsedRealtime(); final int thresholdIndex = getCurrentDrainThresholdIndex(uid, now, mBgCurrentDrainWindowMs); @@ -1788,13 +1832,17 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> long[] ts = null; ImmutableBatteryUsage[] usages = null; if (rbPercentage >= rbThreshold) { - // New findings to us, track it and let the controller know. - ts = new long[TIME_STAMP_INDEX_LAST]; - ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = now; - usages = new ImmutableBatteryUsage[TIME_STAMP_INDEX_LAST]; - usages[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = usage; - mHighBgBatteryPackages.put(uid, Pair.create(ts, usages)); - notifyController = excessive = true; + if (now > lastInteractionTime + mBgCurrentDrainInteractionGracePeriodMs) { + // New findings to us, track it and let the controller know. + ts = new long[TIME_STAMP_INDEX_LAST]; + ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = now; + usages = new ImmutableBatteryUsage[TIME_STAMP_INDEX_LAST]; + usages[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = usage; + mHighBgBatteryPackages.put(uid, Pair.create(ts, usages)); + // It's beeen long enough since last interaction with this app. + notifyController = true; + } + excessive = true; } if (decoupleThresholds && brPercentage >= brThreshold) { if (ts == null) { @@ -1812,11 +1860,15 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> final long[] ts = pair.first; final long lastRestrictBucketTs = ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET]; if (rbPercentage >= rbThreshold) { - if (lastRestrictBucketTs == 0) { - ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = now; - pair.second[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = usage; + if (now > lastInteractionTime + mBgCurrentDrainInteractionGracePeriodMs) { + if (lastRestrictBucketTs == 0) { + ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = now; + pair.second[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = usage; + } + // It's been long enough since last interaction with this app. + notifyController = true; } - notifyController = excessive = true; + excessive = true; } else { // It's actually back to normal, but we don't untrack it until // explicit user interactions, because the restriction could be the cause @@ -1833,7 +1885,7 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> && (now > lastRestrictBucketTs + mBgCurrentDrainWindowMs)); if (notifyController) { ts[TIME_STAMP_INDEX_BG_RESTRICTED] = now; - pair.second[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = usage; + pair.second[TIME_STAMP_INDEX_BG_RESTRICTED] = usage; } excessive = true; } else { @@ -1841,7 +1893,7 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> // user consent to unrestrict it; or if it's in restricted bucket level, // resetting this won't lift it from that level. ts[TIME_STAMP_INDEX_BG_RESTRICTED] = 0; - pair.second[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = null; + pair.second[TIME_STAMP_INDEX_BG_RESTRICTED] = null; // Now need to notify the controller. } } @@ -1902,6 +1954,7 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> void onUserInteractionStarted(String packageName, int uid) { boolean changed = false; synchronized (mLock) { + mLastInteractionTime.put(uid, SystemClock.elapsedRealtime()); final int curLevel = mTracker.mAppRestrictionController.getRestrictionLevel( uid, packageName); if (curLevel == RESTRICTION_LEVEL_BACKGROUND_RESTRICTED) { @@ -1940,9 +1993,30 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> @VisibleForTesting void reset() { mHighBgBatteryPackages.clear(); + mLastInteractionTime.clear(); mTracker.reset(); } + @GuardedBy("mLock") + void onUserRemovedLocked(final @UserIdInt int userId) { + for (int i = mHighBgBatteryPackages.size() - 1; i >= 0; i--) { + if (UserHandle.getUserId(mHighBgBatteryPackages.keyAt(i)) == userId) { + mHighBgBatteryPackages.removeAt(i); + } + } + for (int i = mLastInteractionTime.size() - 1; i >= 0; i--) { + if (UserHandle.getUserId(mLastInteractionTime.keyAt(i)) == userId) { + mLastInteractionTime.removeAt(i); + } + } + } + + @GuardedBy("mLock") + void onUidRemovedLocked(final int uid) { + mHighBgBatteryPackages.remove(uid); + mLastInteractionTime.delete(uid); + } + @Override void dump(PrintWriter pw, String prefix) { pw.print(prefix); @@ -1976,6 +2050,10 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> pw.print('='); pw.println(mBgCurrentDrainWindowMs); pw.print(prefix); + pw.print(KEY_BG_CURRENT_DRAIN_INTERACTION_GRACE_PERIOD); + pw.print('='); + pw.println(mBgCurrentDrainInteractionGracePeriodMs); + pw.print(prefix); pw.print(KEY_BG_CURRENT_DRAIN_MEDIA_PLAYBACK_MIN_DURATION); pw.print('='); pw.println(mBgCurrentDrainMediaPlaybackMinDuration); diff --git a/services/core/java/com/android/server/am/DropboxRateLimiter.java b/services/core/java/com/android/server/am/DropboxRateLimiter.java index 18fb6a480f29..08a6719a016a 100644 --- a/services/core/java/com/android/server/am/DropboxRateLimiter.java +++ b/services/core/java/com/android/server/am/DropboxRateLimiter.java @@ -24,9 +24,14 @@ import com.android.internal.annotations.GuardedBy; /** Rate limiter for adding errors into dropbox. */ public class DropboxRateLimiter { - private static final long RATE_LIMIT_BUFFER_EXPIRY = 15 * DateUtils.SECOND_IN_MILLIS; - private static final long RATE_LIMIT_BUFFER_DURATION = 10 * DateUtils.SECOND_IN_MILLIS; - private static final int RATE_LIMIT_ALLOWED_ENTRIES = 5; + // After RATE_LIMIT_ALLOWED_ENTRIES have been collected (for a single breakdown of + // process/eventType) further entries will be rejected until RATE_LIMIT_BUFFER_DURATION has + // elapsed, after which the current count for this breakdown will be reset. + private static final long RATE_LIMIT_BUFFER_DURATION = 10 * DateUtils.MINUTE_IN_MILLIS; + // The time duration after which the rate limit buffer will be cleared. + private static final long RATE_LIMIT_BUFFER_EXPIRY = 3 * RATE_LIMIT_BUFFER_DURATION; + // The number of entries to keep per breakdown of process/eventType. + private static final int RATE_LIMIT_ALLOWED_ENTRIES = 6; @GuardedBy("mErrorClusterRecords") private final ArrayMap<String, ErrorRecord> mErrorClusterRecords = new ArrayMap<>(); diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java index db17c1056e39..8180e66166d9 100644 --- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java +++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java @@ -64,7 +64,9 @@ final class HandwritingEventReceiverSurface { | InputConfig.INTERCEPTS_STYLUS | InputConfig.TRUSTED_OVERLAY; - // The touchable region of this input surface is not initially configured. + // Configure the surface to receive stylus events across the entire display. + mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */); + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); t.setInputWindowInfo(mInputSurface, mWindowHandle); t.setLayer(mInputSurface, HANDWRITING_SURFACE_LAYER); @@ -81,10 +83,6 @@ final class HandwritingEventReceiverSurface { mWindowHandle.ownerUid = imeUid; mWindowHandle.inputConfig &= ~InputConfig.SPY; - // Update the touchable region so that the IME can intercept stylus events - // across the entire display. - mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */); - new SurfaceControl.Transaction() .setInputWindowInfo(mInputSurface, mWindowHandle) .apply(); diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java index a70677222506..f89b6aedf1f5 100644 --- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java +++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java @@ -91,7 +91,7 @@ final class HandwritingModeController { * InputEventReceiver that batches events according to the current thread's Choreographer. */ @UiThread - void initializeHandwritingSpy(int displayId, IBinder focusedWindowToken) { + void initializeHandwritingSpy(int displayId) { // When resetting, reuse resources if we are reinitializing on the same display. reset(displayId == mCurrentDisplayId); mCurrentDisplayId = displayId; @@ -115,12 +115,6 @@ final class HandwritingModeController { mHandwritingSurface = new HandwritingEventReceiverSurface( name, displayId, surface, channel); - // Configure the handwriting window to receive events over the focused window's bounds. - mWindowManagerInternal.replaceInputSurfaceTouchableRegionWithWindowCrop( - mHandwritingSurface.getSurface(), - mHandwritingSurface.getInputWindowHandle(), - focusedWindowToken); - // Use a dup of the input channel so that event processing can be paused by disposing the // event receiver without causing a fd hangup. mHandwritingEventReceiver = new BatchedInputEventReceiver.SimpleBatchedInputEventReceiver( @@ -149,7 +143,8 @@ final class HandwritingModeController { */ @UiThread @Nullable - HandwritingSession startHandwritingSession(int requestId, int imePid, int imeUid) { + HandwritingSession startHandwritingSession( + int requestId, int imePid, int imeUid, IBinder focusedWindowToken) { if (mHandwritingSurface == null) { Slog.e(TAG, "Cannot start handwriting session: Handwriting was not initialized."); return null; @@ -158,12 +153,20 @@ final class HandwritingModeController { Slog.e(TAG, "Cannot start handwriting session: Invalid request id: " + requestId); return null; } - if (!mRecordingGesture) { + if (!mRecordingGesture || mHandwritingBuffer.isEmpty()) { Slog.e(TAG, "Cannot start handwriting session: No stylus gesture is being recorded."); return null; } Objects.requireNonNull(mHandwritingEventReceiver, "Handwriting session was already transferred to IME."); + final MotionEvent downEvent = mHandwritingBuffer.get(0); + assert (downEvent.getActionMasked() == MotionEvent.ACTION_DOWN); + if (!mWindowManagerInternal.isPointInsideWindow( + focusedWindowToken, mCurrentDisplayId, downEvent.getRawX(), downEvent.getRawY())) { + Slog.e(TAG, "Cannot start handwriting session: " + + "Stylus gesture did not start inside the focused window."); + return null; + } if (DEBUG) Slog.d(TAG, "Starting handwriting session in display: " + mCurrentDisplayId); mInputManagerInternal.pilferPointers(mHandwritingSurface.getInputChannel().getToken()); @@ -226,13 +229,17 @@ final class HandwritingModeController { } if (!(ev instanceof MotionEvent)) { - Slog.e("Stylus", "Received non-motion event in stylus monitor."); + Slog.wtf(TAG, "Received non-motion event in stylus monitor."); return false; } final MotionEvent event = (MotionEvent) ev; if (!isStylusEvent(event)) { return false; } + if (event.getDisplayId() != mCurrentDisplayId) { + Slog.wtf(TAG, "Received stylus event associated with the incorrect display."); + return false; + } onStylusEvent(event); return true; diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index c759c645a318..ea2b1571c0ae 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -5118,9 +5118,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub case MSG_RESET_HANDWRITING: { synchronized (ImfLock.class) { if (mBindingController.supportsStylusHandwriting() - && getCurMethodLocked() != null && mCurFocusedWindow != null) { - mHwController.initializeHandwritingSpy( - mCurTokenDisplayId, mCurFocusedWindow); + && getCurMethodLocked() != null) { + mHwController.initializeHandwritingSpy(mCurTokenDisplayId); } else { mHwController.reset(); } @@ -5130,14 +5129,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub case MSG_START_HANDWRITING: synchronized (ImfLock.class) { IInputMethodInvoker curMethod = getCurMethodLocked(); - if (curMethod == null) { + if (curMethod == null || mCurFocusedWindow == null) { return true; } final HandwritingModeController.HandwritingSession session = mHwController.startHandwritingSession( msg.arg1 /*requestId*/, msg.arg2 /*pid*/, - mBindingController.getCurMethodUid()); + mBindingController.getCurMethodUid(), + mCurFocusedWindow); if (session == null) { Slog.e(TAG, "Failed to start handwriting session for requestId: " + msg.arg1); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 83c576e9259d..9042326c5760 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -58,6 +58,7 @@ import static android.content.Context.BIND_AUTO_CREATE; import static android.content.Context.BIND_FOREGROUND_SERVICE; import static android.content.Context.BIND_NOT_PERCEPTIBLE; import static android.content.pm.PackageManager.FEATURE_LEANBACK; +import static android.content.pm.PackageManager.FEATURE_TELECOM; import static android.content.pm.PackageManager.FEATURE_TELEVISION; import static android.content.pm.PackageManager.MATCH_ALL; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; @@ -655,10 +656,10 @@ public class NotificationManagerService extends SystemService { private int mWarnRemoteViewsSizeBytes; private int mStripRemoteViewsSizeBytes; - final boolean mEnableAppSettingMigration; private boolean mForceUserSetOnUpgrade; private MetricsLogger mMetricsLogger; + private NotificationChannelLogger mNotificationChannelLogger; private TriPredicate<String, Integer, String> mAllowedManagedServicePackages; private final SavePolicyFileRunnable mSavePolicyFile = new SavePolicyFileRunnable(); @@ -1998,12 +1999,6 @@ public class NotificationManagerService extends SystemService { mNotificationRecordLogger = notificationRecordLogger; mNotificationInstanceIdSequence = notificationInstanceIdSequence; Notification.processAllowlistToken = ALLOWLIST_TOKEN; - // TODO (b/194833441): remove when OS is ready for migration. This flag is checked once - // rather than having a settings observer because some of the behaviors (e.g. readXml) only - // happen on reboot - mEnableAppSettingMigration = Settings.Secure.getIntForUser( - getContext().getContentResolver(), - Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 0, USER_SYSTEM) == 1; } // TODO - replace these methods with new fields in the VisibleForTesting constructor @@ -2161,6 +2156,11 @@ public class NotificationManagerService extends SystemService { mAccessibilityManager = am; } + @VisibleForTesting + void setTelecomManager(TelecomManager tm) { + mTelecomManager = tm; + } + // TODO: All tests should use this init instead of the one-off setters above. @VisibleForTesting void init(WorkerHandler handler, RankingHandler rankingHandler, @@ -2178,7 +2178,7 @@ public class NotificationManagerService extends SystemService { TelephonyManager telephonyManager, ActivityManagerInternal ami, MultiRateLimiter toastRateLimiter, PermissionHelper permissionHelper, UsageStatsManagerInternal usageStatsManagerInternal, - TelecomManager telecomManager) { + TelecomManager telecomManager, NotificationChannelLogger channelLogger) { mHandler = handler; Resources resources = getContext().getResources(); mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(), @@ -2275,12 +2275,13 @@ public class NotificationManagerService extends SystemService { } }); mPermissionHelper = permissionHelper; + mNotificationChannelLogger = channelLogger; mPreferencesHelper = new PreferencesHelper(getContext(), mPackageManagerClient, mRankingHandler, mZenModeHelper, mPermissionHelper, - new NotificationChannelLoggerImpl(), + mNotificationChannelLogger, mAppOps, new SysUiStatsEvent.BuilderFactory()); mRankingHelper = new RankingHelper(getContext(), @@ -2362,9 +2363,6 @@ public class NotificationManagerService extends SystemService { mNotificationEffectsEnabledForAutomotive = resources.getBoolean(R.bool.config_enableServerNotificationEffectsForAutomotive); - mPreferencesHelper.lockChannelsForOEM(getContext().getResources().getStringArray( - com.android.internal.R.array.config_nonBlockableNotificationPackages)); - mZenModeHelper.setPriorityOnlyDndExemptPackages(getContext().getResources().getStringArray( com.android.internal.R.array.config_priorityOnlyDndExemptPackages)); @@ -2504,10 +2502,11 @@ public class NotificationManagerService extends SystemService { LocalServices.getService(ActivityManagerInternal.class), createToastRateLimiter(), new PermissionHelper(LocalServices.getService( PermissionManagerServiceInternal.class), AppGlobals.getPackageManager(), - AppGlobals.getPermissionManager(), mEnableAppSettingMigration, + AppGlobals.getPermissionManager(), mForceUserSetOnUpgrade), LocalServices.getService(UsageStatsManagerInternal.class), - getContext().getSystemService(TelecomManager.class)); + getContext().getSystemService(TelecomManager.class), + new NotificationChannelLoggerImpl()); publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL); @@ -2799,7 +2798,7 @@ public class NotificationManagerService extends SystemService { } } - private void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel, + void updateNotificationChannelInt(String pkg, int uid, NotificationChannel channel, boolean fromListener) { if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) { // cancel @@ -2821,11 +2820,9 @@ public class NotificationManagerService extends SystemService { mPreferencesHelper.getNotificationChannel(pkg, uid, channel.getId(), true); mPreferencesHelper.updateNotificationChannel(pkg, uid, channel, true); - if (mEnableAppSettingMigration) { - if (mPreferencesHelper.onlyHasDefaultChannel(pkg, uid)) { - mPermissionHelper.setNotificationPermission(pkg, UserHandle.getUserId(uid), - channel.getImportance() != IMPORTANCE_NONE, true); - } + if (mPreferencesHelper.onlyHasDefaultChannel(pkg, uid)) { + mPermissionHelper.setNotificationPermission(pkg, UserHandle.getUserId(uid), + channel.getImportance() != IMPORTANCE_NONE, true); } maybeNotifyChannelOwner(pkg, uid, preUpdate, channel); @@ -3474,36 +3471,19 @@ public class NotificationManagerService extends SystemService { @Override public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) { enforceSystemOrSystemUI("setNotificationsEnabledForPackage"); - if (mEnableAppSettingMigration) { - boolean wasEnabled = mPermissionHelper.hasPermission(uid); - if (wasEnabled == enabled) { - return; - } - mPermissionHelper.setNotificationPermission( - pkg, UserHandle.getUserId(uid), enabled, true); - sendAppBlockStateChangedBroadcast(pkg, uid, !enabled); - } else { - synchronized (mNotificationLock) { - boolean wasEnabled = mPreferencesHelper.getImportance(pkg, uid) - != NotificationManager.IMPORTANCE_NONE; - - if (wasEnabled == enabled) { - return; - } - } - - mPreferencesHelper.setEnabled(pkg, uid, enabled); - // TODO (b/194833441): this is being ignored by app ops now that the permission - // exists, so send the broadcast manually - mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg, - enabled ? MODE_ALLOWED : AppOpsManager.MODE_IGNORED); - - sendAppBlockStateChangedBroadcast(pkg, uid, !enabled); + boolean wasEnabled = mPermissionHelper.hasPermission(uid); + if (wasEnabled == enabled) { + return; } + mPermissionHelper.setNotificationPermission( + pkg, UserHandle.getUserId(uid), enabled, true); + sendAppBlockStateChangedBroadcast(pkg, uid, !enabled); + mMetricsLogger.write(new LogMaker(MetricsEvent.ACTION_BAN_APP_NOTES) .setType(MetricsEvent.TYPE_ACTION) .setPackageName(pkg) .setSubtype(enabled ? 1 : 0)); + mNotificationChannelLogger.logAppNotificationsAllowed(uid, pkg, enabled); // Now, cancel any outstanding notifications that are part of a just-disabled app if (!enabled) { cancelAllNotificationsInt(MY_UID, MY_PID, pkg, null, 0, 0, true, @@ -3529,8 +3509,6 @@ public class NotificationManagerService extends SystemService { public void setNotificationsEnabledWithImportanceLockForPackage( String pkg, int uid, boolean enabled) { setNotificationsEnabledForPackage(pkg, uid, enabled); - - mPreferencesHelper.setAppImportanceLocked(pkg, uid); } /** @@ -3646,14 +3624,10 @@ public class NotificationManagerService extends SystemService { @Override public int getPackageImportance(String pkg) { checkCallerIsSystemOrSameApp(pkg); - if (mEnableAppSettingMigration) { - if (mPermissionHelper.hasPermission(Binder.getCallingUid())) { - return IMPORTANCE_DEFAULT; - } else { - return IMPORTANCE_NONE; - } + if (mPermissionHelper.hasPermission(Binder.getCallingUid())) { + return IMPORTANCE_DEFAULT; } else { - return mPreferencesHelper.getImportance(pkg, Binder.getCallingUid()); + return IMPORTANCE_NONE; } } @@ -5885,8 +5859,7 @@ public class NotificationManagerService extends SystemService { NotificationRecord createAutoGroupSummary(int userId, String pkg, String triggeringKey, boolean needsOngoingFlag) { NotificationRecord summaryRecord = null; - boolean isPermissionFixed = mPermissionHelper.isMigrationEnabled() - ? mPermissionHelper.isPermissionFixed(pkg, userId) : false; + boolean isPermissionFixed = mPermissionHelper.isPermissionFixed(pkg, userId); synchronized (mNotificationLock) { NotificationRecord notificationRecord = mNotificationsByKey.get(triggeringKey); if (notificationRecord == null) { @@ -5895,10 +5868,6 @@ public class NotificationManagerService extends SystemService { return null; } NotificationChannel channel = notificationRecord.getChannel(); - boolean isImportanceFixed = mPermissionHelper.isMigrationEnabled() - ? isPermissionFixed - : (channel.isImportanceLockedByOEM() - || channel.isImportanceLockedByCriticalDeviceFunction()); final StatusBarNotification adjustedSbn = notificationRecord.getSbn(); userId = adjustedSbn.getUser().getIdentifier(); int uid = adjustedSbn.getUid(); @@ -5944,7 +5913,7 @@ public class NotificationManagerService extends SystemService { System.currentTimeMillis()); summaryRecord = new NotificationRecord(getContext(), summarySbn, notificationRecord.getChannel()); - summaryRecord.setImportanceFixed(isImportanceFixed); + summaryRecord.setImportanceFixed(isPermissionFixed); summaryRecord.setIsAppImportanceLocked( notificationRecord.getIsAppImportanceLocked()); summaries.put(pkg, summarySbn.getKey()); @@ -5992,10 +5961,6 @@ public class NotificationManagerService extends SystemService { @VisibleForTesting protected ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> getAllUsersNotificationPermissions() { - // don't bother if migration is not enabled - if (!mEnableAppSettingMigration) { - return null; - } ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> allPermissions = new ArrayMap<>(); final List<UserInfo> allUsers = mUm.getUsers(); // for each of these, get the package notification permissions that are associated @@ -6509,13 +6474,8 @@ public class NotificationManagerService extends SystemService { + ", notificationUid=" + notificationUid + ", notification=" + notification; Slog.e(TAG, noChannelStr); - boolean appNotificationsOff; - if (mEnableAppSettingMigration) { - appNotificationsOff = !mPermissionHelper.hasPermission(notificationUid); - } else { - appNotificationsOff = mPreferencesHelper.getImportance(pkg, notificationUid) - == NotificationManager.IMPORTANCE_NONE; - } + boolean appNotificationsOff = !mPermissionHelper.hasPermission(notificationUid); + if (!appNotificationsOff) { doChannelWarningToast(notificationUid, @@ -6527,14 +6487,11 @@ public class NotificationManagerService extends SystemService { } final NotificationRecord r = new NotificationRecord(getContext(), n, channel); - r.setIsAppImportanceLocked(mPreferencesHelper.getIsAppImportanceLocked(pkg, callingUid)); + r.setIsAppImportanceLocked(mPermissionHelper.isPermissionUserSet(pkg, userId)); r.setPostSilently(postSilently); r.setFlagBubbleRemoved(false); r.setPkgAllowedAsConvo(mMsgPkgsAllowedAsConvos.contains(pkg)); - boolean isImportanceFixed = mPermissionHelper.isMigrationEnabled() - ? mPermissionHelper.isPermissionFixed(pkg, userId) - : (channel.isImportanceLockedByOEM() - || channel.isImportanceLockedByCriticalDeviceFunction()); + boolean isImportanceFixed = mPermissionHelper.isPermissionFixed(pkg, userId); r.setImportanceFixed(isImportanceFixed); if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) { @@ -6978,19 +6935,20 @@ public class NotificationManagerService extends SystemService { private boolean isCallNotification(String pkg, int uid) { final long identity = Binder.clearCallingIdentity(); try { - return mTelecomManager.isInManagedCall() || mTelecomManager.isInSelfManagedCall( - pkg, UserHandle.getUserHandleForUid(uid)); + if (mPackageManagerClient.hasSystemFeature(FEATURE_TELECOM) + && mTelecomManager != null) { + return mTelecomManager.isInManagedCall() + || mTelecomManager.isInSelfManagedCall( + pkg, UserHandle.getUserHandleForUid(uid)); + } + return false; } finally { Binder.restoreCallingIdentity(identity); } } private boolean areNotificationsEnabledForPackageInt(String pkg, int uid) { - if (mEnableAppSettingMigration) { - return mPermissionHelper.hasPermission(uid); - } else { - return mPreferencesHelper.getImportance(pkg, uid) != IMPORTANCE_NONE; - } + return mPermissionHelper.hasPermission(uid); } protected int getNotificationCount(String pkg, int userId, int excludedId, @@ -7405,6 +7363,7 @@ public class NotificationManagerService extends SystemService { @Override public void run() { boolean appBanned = !areNotificationsEnabledForPackageInt(pkg, uid); + boolean isCallNotification = isCallNotification(pkg, uid); synchronized (mNotificationLock) { try { NotificationRecord r = null; @@ -7423,8 +7382,10 @@ public class NotificationManagerService extends SystemService { final StatusBarNotification n = r.getSbn(); final Notification notification = n.getNotification(); + boolean isCallNotificationAndCorrectStyle = isCallNotification + && notification.isStyle(Notification.CallStyle.class); - if (!notification.isMediaNotification() + if (!(notification.isMediaNotification() || isCallNotificationAndCorrectStyle) && (appBanned || isRecordBlockedLocked(r))) { mUsageStats.registerBlocked(r); if (DBG) { diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index f979343248f1..cbaf485c077f 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -1089,7 +1089,7 @@ public final class NotificationRecord { } /** - * @see PreferencesHelper#getIsAppImportanceLocked(String, int) + * @see PermissionHelper#isPermissionUserSet(String, int) */ public boolean getIsAppImportanceLocked() { return mIsAppImportanceLocked; diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java index b4230c11bcab..b2fee1e8b545 100644 --- a/services/core/java/com/android/server/notification/PermissionHelper.java +++ b/services/core/java/com/android/server/notification/PermissionHelper.java @@ -55,30 +55,21 @@ public final class PermissionHelper { private final PermissionManagerServiceInternal mPmi; private final IPackageManager mPackageManager; private final IPermissionManager mPermManager; - // TODO (b/194833441): Remove when the migration is enabled - private final boolean mMigrationEnabled; private final boolean mForceUserSetOnUpgrade; public PermissionHelper(PermissionManagerServiceInternal pmi, IPackageManager packageManager, - IPermissionManager permManager, boolean migrationEnabled, - boolean forceUserSetOnUpgrade) { + IPermissionManager permManager, boolean forceUserSetOnUpgrade) { mPmi = pmi; mPackageManager = packageManager; mPermManager = permManager; - mMigrationEnabled = migrationEnabled; mForceUserSetOnUpgrade = forceUserSetOnUpgrade; } - public boolean isMigrationEnabled() { - return mMigrationEnabled; - } - /** * Returns whether the given uid holds the notification permission. Must not be called * with a lock held. */ public boolean hasPermission(int uid) { - assertFlag(); final long callingId = Binder.clearCallingIdentity(); try { return mPmi.checkPostNotificationsPermissionGrantedOrLegacyAccess(uid) @@ -93,7 +84,6 @@ public final class PermissionHelper { * Must not be called with a lock held. Format: uid, packageName */ Set<Pair<Integer, String>> getAppsRequestingPermission(int userId) { - assertFlag(); Set<Pair<Integer, String>> requested = new HashSet<>(); List<PackageInfo> pkgs = getInstalledPackages(userId); for (PackageInfo pi : pkgs) { @@ -131,7 +121,6 @@ public final class PermissionHelper { * with a lock held. Format: uid, packageName. */ Set<Pair<Integer, String>> getAppsGrantedPermission(int userId) { - assertFlag(); Set<Pair<Integer, String>> granted = new HashSet<>(); ParceledListSlice<PackageInfo> parceledList = null; try { @@ -153,7 +142,6 @@ public final class PermissionHelper { public @NonNull ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> getNotificationPermissionValues(int userId) { - assertFlag(); ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> notifPermissions = new ArrayMap<>(); Set<Pair<Integer, String>> allRequestingUids = getAppsRequestingPermission(userId); Set<Pair<Integer, String>> allApprovedUids = getAppsGrantedPermission(userId); @@ -180,7 +168,6 @@ public final class PermissionHelper { */ public void setNotificationPermission(String packageName, @UserIdInt int userId, boolean grant, boolean userSet, boolean reviewRequired) { - assertFlag(); final long callingId = Binder.clearCallingIdentity(); try { // Do not change the permission if the package doesn't request it, do not change fixed @@ -221,7 +208,6 @@ public final class PermissionHelper { * restoring a pre-T backup on a T+ device */ public void setNotificationPermission(PackagePermission pkgPerm) { - assertFlag(); if (pkgPerm == null || pkgPerm.packageName == null) { return; } @@ -233,7 +219,6 @@ public final class PermissionHelper { } public boolean isPermissionFixed(String packageName, @UserIdInt int userId) { - assertFlag(); final long callingId = Binder.clearCallingIdentity(); try { try { @@ -251,7 +236,6 @@ public final class PermissionHelper { } boolean isPermissionUserSet(String packageName, @UserIdInt int userId) { - assertFlag(); final long callingId = Binder.clearCallingIdentity(); try { try { @@ -269,7 +253,6 @@ public final class PermissionHelper { } boolean isPermissionGrantedByDefaultOrRole(String packageName, @UserIdInt int userId) { - assertFlag(); final long callingId = Binder.clearCallingIdentity(); try { try { @@ -288,7 +271,6 @@ public final class PermissionHelper { private boolean packageRequestsNotificationPermission(String packageName, @UserIdInt int userId) { - assertFlag(); try { String[] permissions = mPackageManager.getPackageInfo(packageName, GET_PERMISSIONS, userId).requestedPermissions; @@ -299,12 +281,6 @@ public final class PermissionHelper { return false; } - private void assertFlag() { - if (!mMigrationEnabled) { - throw new IllegalStateException("Method called without checking flag value"); - } - } - public static class PackagePermission { public final String packageName; public final @UserIdInt int userId; diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index ef3c770f125b..4e3fbaa18870 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -209,11 +209,7 @@ public class PreferencesHelper implements RankingConfig { mAppOps = appOpsManager; mStatsEventBuilderFactory = statsEventBuilderFactory; - if (mPermissionHelper.isMigrationEnabled()) { - XML_VERSION = 4; - } else { - XML_VERSION = 2; - } + XML_VERSION = 4; updateBadgingEnabled(); updateBubblesEnabled(); @@ -230,8 +226,7 @@ public class PreferencesHelper implements RankingConfig { final int xmlVersion = parser.getAttributeInt(null, ATT_VERSION, -1); boolean upgradeForBubbles = xmlVersion == XML_VERSION_BUBBLES_UPGRADE; - boolean migrateToPermission = (xmlVersion < XML_VERSION_NOTIF_PERMISSION) - && mPermissionHelper.isMigrationEnabled(); + boolean migrateToPermission = (xmlVersion < XML_VERSION_NOTIF_PERMISSION); if (xmlVersion < XML_VERSION_REVIEW_PERMISSIONS_NOTIFICATION) { // make a note that we should show the notification at some point. // it shouldn't be possible for the user to already have seen it, as the XML version @@ -393,8 +388,6 @@ public class PreferencesHelper implements RankingConfig { hasUserConfiguredSettings(r)); pkgPerms.add(pkgPerm); } - } else if (!mPermissionHelper.isMigrationEnabled()) { - r.importance = appImportance; } } catch (Exception e) { Slog.w(TAG, "Failed to restore pkg", e); @@ -417,16 +410,8 @@ public class PreferencesHelper implements RankingConfig { } else { channel.populateFromXml(parser); } - if (!mPermissionHelper.isMigrationEnabled()) { - channel.setImportanceLockedByCriticalDeviceFunction( - r.defaultAppLockedImportance); - channel.setImportanceLockedByOEM(r.oemLockedImportance); - if (!channel.isImportanceLockedByOEM()) { - if (r.oemLockedChannels.contains(channel.getId())) { - channel.setImportanceLockedByOEM(true); - } - } - } + channel.setImportanceLockedByCriticalDeviceFunction( + r.defaultAppLockedImportance); if (isShortcutOk(channel) && isDeletionOk(channel)) { r.channels.put(id, channel); @@ -604,7 +589,7 @@ public class PreferencesHelper implements RankingConfig { out.endTag(null, TAG_STATUS_ICONS); } ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> notifPermissions = new ArrayMap<>(); - if (mPermissionHelper.isMigrationEnabled() && forBackup) { + if (forBackup) { notifPermissions = mPermissionHelper.getNotificationPermissionValues(userId); } @@ -736,28 +721,6 @@ public class PreferencesHelper implements RankingConfig { } } - /** - * Gets importance. - */ - @Override - public int getImportance(String packageName, int uid) { - synchronized (mPackagePreferences) { - return getOrCreatePackagePreferencesLocked(packageName, uid).importance; - } - } - - /** - * Returns whether the importance of the corresponding notification is user-locked and shouldn't - * be adjusted by an assistant (via means of a blocking helper, for example). For the channel - * locking field, see {@link NotificationChannel#USER_LOCKED_IMPORTANCE}. - */ - public boolean getIsAppImportanceLocked(String packageName, int uid) { - synchronized (mPackagePreferences) { - int userLockedFields = getOrCreatePackagePreferencesLocked(packageName, uid).lockedAppFields; - return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0; - } - } - @Override public boolean canShowBadge(String packageName, int uid) { synchronized (mPackagePreferences) { @@ -1043,16 +1006,10 @@ public class PreferencesHelper implements RankingConfig { : NotificationChannel.DEFAULT_ALLOW_BUBBLE); } clearLockedFieldsLocked(channel); - if (!mPermissionHelper.isMigrationEnabled()) { - channel.setImportanceLockedByOEM(r.oemLockedImportance); - if (!channel.isImportanceLockedByOEM()) { - if (r.oemLockedChannels.contains(channel.getId())) { - channel.setImportanceLockedByOEM(true); - } - } - channel.setImportanceLockedByCriticalDeviceFunction( - r.defaultAppLockedImportance); - } + + channel.setImportanceLockedByCriticalDeviceFunction( + r.defaultAppLockedImportance); + if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) { channel.setLockscreenVisibility( NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE); @@ -1133,33 +1090,15 @@ public class PreferencesHelper implements RankingConfig { updatedChannel.unlockFields(updatedChannel.getUserLockedFields()); } - if (mPermissionHelper.isMigrationEnabled()) { - if (mPermissionHelper.isPermissionFixed(r.pkg, UserHandle.getUserId(r.uid)) - && !(channel.isBlockable() || channel.getImportance() == IMPORTANCE_NONE)) { - updatedChannel.setImportance(channel.getImportance()); - } - } else { - // no importance updates are allowed if OEM blocked it - updatedChannel.setImportanceLockedByOEM(channel.isImportanceLockedByOEM()); - if (updatedChannel.isImportanceLockedByOEM()) { - updatedChannel.setImportance(channel.getImportance()); - } - updatedChannel.setImportanceLockedByCriticalDeviceFunction( - r.defaultAppLockedImportance); - if (updatedChannel.isImportanceLockedByCriticalDeviceFunction() - && updatedChannel.getImportance() == IMPORTANCE_NONE) { - updatedChannel.setImportance(channel.getImportance()); - } + if ((mPermissionHelper.isPermissionFixed(r.pkg, UserHandle.getUserId(r.uid)) + || channel.isImportanceLockedByCriticalDeviceFunction()) + && !(channel.isBlockable() || channel.getImportance() == IMPORTANCE_NONE)) { + updatedChannel.setImportance(channel.getImportance()); } r.channels.put(updatedChannel.getId(), updatedChannel); if (onlyHasDefaultChannel(pkg, uid)) { - if (!mPermissionHelper.isMigrationEnabled()) { - // copy settings to app level so they are inherited by new channels - // when the app migrates - r.importance = updatedChannel.getImportance(); - } r.priority = updatedChannel.canBypassDnd() ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT; r.visibility = updatedChannel.getLockscreenVisibility(); @@ -1328,61 +1267,8 @@ public class PreferencesHelper implements RankingConfig { mHideSilentStatusBarIcons = hide; } - public void lockChannelsForOEM(String[] appOrChannelList) { - if (mPermissionHelper.isMigrationEnabled()) { - return; - } - if (appOrChannelList == null) { - return; - } - for (String appOrChannel : appOrChannelList) { - if (!TextUtils.isEmpty(appOrChannel)) { - String[] appSplit = appOrChannel.split(NON_BLOCKABLE_CHANNEL_DELIM); - if (appSplit != null && appSplit.length > 0) { - String appName = appSplit[0]; - String channelId = appSplit.length == 2 ? appSplit[1] : null; - - synchronized (mPackagePreferences) { - boolean foundApp = false; - for (PackagePreferences r : mPackagePreferences.values()) { - if (r.pkg.equals(appName)) { - foundApp = true; - if (channelId == null) { - // lock all channels for the app - r.oemLockedImportance = true; - for (NotificationChannel channel : r.channels.values()) { - channel.setImportanceLockedByOEM(true); - } - } else { - NotificationChannel channel = r.channels.get(channelId); - if (channel != null) { - channel.setImportanceLockedByOEM(true); - } - // Also store the locked channels on the record, so they aren't - // temporarily lost when data is cleared on the package - r.oemLockedChannels.add(channelId); - } - } - } - if (!foundApp) { - List<String> channels = - mOemLockedApps.getOrDefault(appName, new ArrayList<>()); - if (channelId != null) { - channels.add(channelId); - } - mOemLockedApps.put(appName, channels); - } - } - } - } - } - } - public void updateDefaultApps(int userId, ArraySet<String> toRemove, ArraySet<Pair<String, Integer>> toAdd) { - if (mPermissionHelper.isMigrationEnabled()) { - return; - } synchronized (mPackagePreferences) { for (PackagePreferences p : mPackagePreferences.values()) { if (userId == UserHandle.getUserId(p.uid)) { @@ -1802,20 +1688,8 @@ public class PreferencesHelper implements RankingConfig { } for (int i = candidatePkgs.size() - 1; i >= 0; i--) { Pair<String, Integer> app = candidatePkgs.valueAt(i); - if (mPermissionHelper.isMigrationEnabled()) { - if (!mPermissionHelper.hasPermission(app.second)) { - candidatePkgs.removeAt(i); - } - } else { - synchronized (mPackagePreferences) { - PackagePreferences r = getPackagePreferencesLocked(app.first, app.second); - if (r == null) { - continue; - } - if (r.importance == IMPORTANCE_NONE) { - candidatePkgs.removeAt(i); - } - } + if (!mPermissionHelper.hasPermission(app.second)) { + candidatePkgs.removeAt(i); } } boolean haveBypassingApps = candidatePkgs.size() > 0; @@ -1861,27 +1735,6 @@ public class PreferencesHelper implements RankingConfig { } /** - * Sets importance. - */ - @Override - public void setImportance(String pkgName, int uid, int importance) { - synchronized (mPackagePreferences) { - getOrCreatePackagePreferencesLocked(pkgName, uid).importance = importance; - } - updateConfig(); - } - - public void setEnabled(String packageName, int uid, boolean enabled) { - boolean wasEnabled = getImportance(packageName, uid) != IMPORTANCE_NONE; - if (wasEnabled == enabled) { - return; - } - setImportance(packageName, uid, - enabled ? DEFAULT_IMPORTANCE : IMPORTANCE_NONE); - mNotificationChannelLogger.logAppNotificationsAllowed(uid, packageName, enabled); - } - - /** * Sets whether any notifications from the app, represented by the given {@code pkgName} and * {@code uid}, have their importance locked by the user. Locked notifications don't get * considered for sentiment adjustments (and thus never show a blocking helper). @@ -2055,23 +1908,15 @@ public class PreferencesHelper implements RankingConfig { pw.print(" ("); pw.print(r.uid == UNKNOWN_UID ? "UNKNOWN_UID" : Integer.toString(r.uid)); pw.print(')'); - if (!mPermissionHelper.isMigrationEnabled()) { - if (r.importance != DEFAULT_IMPORTANCE) { - pw.print(" importance="); - pw.print(NotificationListenerService.Ranking.importanceToString( - r.importance)); - } - } else { - Pair<Integer, String> key = new Pair<>(r.uid, r.pkg); - if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) { - pw.print(" importance="); - pw.print(NotificationListenerService.Ranking.importanceToString( - packagePermissions.get(key).first - ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE)); - pw.print(" userSet="); - pw.print(packagePermissions.get(key).second); - pkgsWithPermissionsToHandle.remove(key); - } + Pair<Integer, String> key = new Pair<>(r.uid, r.pkg); + if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) { + pw.print(" importance="); + pw.print(NotificationListenerService.Ranking.importanceToString( + packagePermissions.get(key).first + ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE)); + pw.print(" userSet="); + pw.print(packagePermissions.get(key).second); + pkgsWithPermissionsToHandle.remove(key); } if (r.priority != DEFAULT_PRIORITY) { pw.print(" priority="); @@ -2111,7 +1956,7 @@ public class PreferencesHelper implements RankingConfig { } } // Handle any remaining packages with permissions - if (mPermissionHelper.isMigrationEnabled() && pkgsWithPermissionsToHandle != null) { + if (pkgsWithPermissionsToHandle != null) { for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) { // p.first is the uid of this package; p.second is the package name if (filter.matches(p.second)) { @@ -2151,16 +1996,12 @@ public class PreferencesHelper implements RankingConfig { proto.write(RankingHelperProto.RecordProto.PACKAGE, r.pkg); proto.write(RankingHelperProto.RecordProto.UID, r.uid); - if (mPermissionHelper.isMigrationEnabled()) { - Pair<Integer, String> key = new Pair<>(r.uid, r.pkg); - if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) { - proto.write(RankingHelperProto.RecordProto.IMPORTANCE, - packagePermissions.get(key).first - ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE); - pkgsWithPermissionsToHandle.remove(key); - } - } else { - proto.write(RankingHelperProto.RecordProto.IMPORTANCE, r.importance); + Pair<Integer, String> key = new Pair<>(r.uid, r.pkg); + if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) { + proto.write(RankingHelperProto.RecordProto.IMPORTANCE, + packagePermissions.get(key).first + ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE); + pkgsWithPermissionsToHandle.remove(key); } proto.write(RankingHelperProto.RecordProto.PRIORITY, r.priority); proto.write(RankingHelperProto.RecordProto.VISIBILITY, r.visibility); @@ -2177,7 +2018,7 @@ public class PreferencesHelper implements RankingConfig { } } - if (mPermissionHelper.isMigrationEnabled() && pkgsWithPermissionsToHandle != null) { + if (pkgsWithPermissionsToHandle != null) { for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) { if (filter.matches(p.second)) { fToken = proto.start(fieldId); @@ -2217,25 +2058,22 @@ public class PreferencesHelper implements RankingConfig { // collect whether this package's importance info was user-set for later, if needed // before the migration is enabled, this will simply default to false in all cases. boolean importanceIsUserSet = false; - if (mPermissionHelper.isMigrationEnabled()) { - // Even if this package's data is not present, we need to write something; - // so default to IMPORTANCE_NONE, since if PM doesn't know about the package - // for some reason, notifications are not allowed. - int importance = IMPORTANCE_NONE; - Pair<Integer, String> key = new Pair<>(r.uid, r.pkg); - if (pkgPermissions != null && pkgsWithPermissionsToHandle.contains(key)) { - Pair<Boolean, Boolean> permissionPair = pkgPermissions.get(key); - importance = permissionPair.first - ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE; - // cache the second value for writing later - importanceIsUserSet = permissionPair.second; - - pkgsWithPermissionsToHandle.remove(key); - } - event.writeInt(importance); - } else { - event.writeInt(r.importance); - } + // Even if this package's data is not present, we need to write something; + // so default to IMPORTANCE_NONE, since if PM doesn't know about the package + // for some reason, notifications are not allowed. + int importance = IMPORTANCE_NONE; + Pair<Integer, String> key = new Pair<>(r.uid, r.pkg); + if (pkgPermissions != null && pkgsWithPermissionsToHandle.contains(key)) { + Pair<Boolean, Boolean> permissionPair = pkgPermissions.get(key); + importance = permissionPair.first + ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE; + // cache the second value for writing later + importanceIsUserSet = permissionPair.second; + + pkgsWithPermissionsToHandle.remove(key); + } + event.writeInt(importance); + event.writeInt(r.visibility); event.writeInt(r.lockedAppFields); event.writeBoolean(importanceIsUserSet); // optional bool user_set_importance = 5; @@ -2244,7 +2082,7 @@ public class PreferencesHelper implements RankingConfig { } // handle remaining packages with PackageManager permissions but not local settings - if (mPermissionHelper.isMigrationEnabled() && pkgPermissions != null) { + if (pkgPermissions != null) { for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) { if (pulledEvents > NOTIFICATION_PREFERENCES_PULL_LIMIT) { break; @@ -2357,22 +2195,14 @@ public class PreferencesHelper implements RankingConfig { try { PackagePreferences.put("userId", UserHandle.getUserId(r.uid)); PackagePreferences.put("packageName", r.pkg); - if (mPermissionHelper.isMigrationEnabled()) { - Pair<Integer, String> key = new Pair<>(r.uid, r.pkg); - if (pkgPermissions != null - && pkgsWithPermissionsToHandle.contains(key)) { - PackagePreferences.put("importance", - NotificationListenerService.Ranking.importanceToString( - pkgPermissions.get(key).first - ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE)); - pkgsWithPermissionsToHandle.remove(key); - } - } else { - if (r.importance != DEFAULT_IMPORTANCE) { - PackagePreferences.put("importance", - NotificationListenerService.Ranking.importanceToString( - r.importance)); - } + Pair<Integer, String> key = new Pair<>(r.uid, r.pkg); + if (pkgPermissions != null + && pkgsWithPermissionsToHandle.contains(key)) { + PackagePreferences.put("importance", + NotificationListenerService.Ranking.importanceToString( + pkgPermissions.get(key).first + ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE)); + pkgsWithPermissionsToHandle.remove(key); } if (r.priority != DEFAULT_PRIORITY) { PackagePreferences.put("priority", @@ -2404,7 +2234,7 @@ public class PreferencesHelper implements RankingConfig { } // handle packages for which there are permissions but no local settings - if (mPermissionHelper.isMigrationEnabled() && pkgsWithPermissionsToHandle != null) { + if (pkgsWithPermissionsToHandle != null) { for (Pair<Integer, String> p : pkgsWithPermissionsToHandle) { if (filter == null || filter.matches(p.second)) { JSONObject PackagePreferences = new JSONObject(); @@ -2443,8 +2273,7 @@ public class PreferencesHelper implements RankingConfig { public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter, ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) { JSONArray bans = new JSONArray(); - Map<Integer, String> packageBans = mPermissionHelper.isMigrationEnabled() - ? getPermissionBasedPackageBans(pkgPermissions) : getPackageBans(); + Map<Integer, String> packageBans = getPermissionBasedPackageBans(pkgPermissions); for (Map.Entry<Integer, String> ban : packageBans.entrySet()) { final int userId = UserHandle.getUserId(ban.getKey()); final String packageName = ban.getValue(); @@ -2597,7 +2426,7 @@ public class PreferencesHelper implements RankingConfig { synchronized (mPackagePreferences) { mPackagePreferences.put(packagePreferencesKey(r.pkg, r.uid), r); } - if (mPermissionHelper.isMigrationEnabled() && r.migrateToPm) { + if (r.migrateToPm) { try { PackagePermission p = new PackagePermission( r.pkg, UserHandle.getUserId(r.uid), diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java index 398259333e16..3e9d90c440b6 100644 --- a/services/core/java/com/android/server/notification/RankingConfig.java +++ b/services/core/java/com/android/server/notification/RankingConfig.java @@ -24,8 +24,6 @@ import java.util.Collection; public interface RankingConfig { - void setImportance(String packageName, int uid, int importance); - int getImportance(String packageName, int uid); void setShowBadge(String packageName, int uid, boolean showBadge); boolean canShowBadge(String packageName, int uid); boolean badgingEnabled(UserHandle userHandle); diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 320b06f6dc3e..32f0f109821d 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -119,7 +119,7 @@ public class Installer extends SystemService { IInstalld.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES; private final boolean mIsolated; - + private volatile boolean mDeferSetFirstBoot; private volatile IInstalld mInstalld; private volatile Object mWarnIfHeld; @@ -171,6 +171,7 @@ public class Installer extends SystemService { mInstalld = IInstalld.Stub.asInterface(binder); try { invalidateMounts(); + executeDeferredActions(); } catch (InstallerException ignored) { } } else { @@ -180,6 +181,15 @@ public class Installer extends SystemService { } /** + * Perform any deferred actions on mInstalld while the connection could not be made. + */ + private void executeDeferredActions() throws InstallerException { + if (mDeferSetFirstBoot) { + setFirstBoot(); + } + } + + /** * Do several pre-flight checks before making a remote call. * * @return if the remote call should continue. @@ -291,8 +301,15 @@ public class Installer extends SystemService { return; } try { - mInstalld.setFirstBoot(); - } catch (RemoteException e) { + // mInstalld might be null if the connection could not be established. + if (mInstalld != null) { + mInstalld.setFirstBoot(); + } else { + // if it is null while trying to set the first boot, set a flag to try and set the + // first boot when the connection is eventually established + mDeferSetFirstBoot = true; + } + } catch (Exception e) { throw InstallerException.from(e); } } diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java index e1ff9ead6740..6ee9c66e328a 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java @@ -392,6 +392,9 @@ public class ParsingPackageUtils { if ((flags & PARSE_FRAMEWORK_RES_SPLITS) != 0) { liteParseFlags = flags; } + if ((flags & PARSE_APK_IN_APEX) != 0) { + liteParseFlags |= PARSE_APK_IN_APEX; + } final ParseResult<PackageLite> liteResult = ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, frameworkSplits, liteParseFlags); diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index 7ba1cadc5c8b..977f79f6175d 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -1453,16 +1453,6 @@ public final class PermissionPolicyService extends SystemService { } } - try { - if (Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, UserHandle.USER_SYSTEM) - == 0) { - return false; - } - } catch (Settings.SettingNotFoundException e) { - return false; - } - if (!pkg.getRequestedPermissions().contains(POST_NOTIFICATIONS) || CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, pkgName, user) || mKeyguardManager.isKeyguardLocked()) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index b14214189833..d8e7fbe8b296 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -27,7 +27,6 @@ import static android.content.pm.PackageManager.FEATURE_LEANBACK; import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS; import static android.os.Build.VERSION_CODES.M; import static android.os.Build.VERSION_CODES.O; import static android.provider.Settings.Secure.VOLUME_HUSH_OFF; @@ -130,7 +129,6 @@ import android.media.AudioManagerInternal; import android.media.AudioSystem; import android.media.IAudioService; import android.media.session.MediaSessionLegacyHelper; -import android.os.BatteryManagerInternal; import android.os.Binder; import android.os.Bundle; import android.os.DeviceIdleManager; @@ -396,7 +394,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { PowerManagerInternal mPowerManagerInternal; IStatusBarService mStatusBarService; StatusBarManagerInternal mStatusBarManagerInternal; - BatteryManagerInternal mBatteryManagerInternal; AudioManagerInternal mAudioManagerInternal; DisplayManager mDisplayManager; DisplayManagerInternal mDisplayManagerInternal; @@ -791,8 +788,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void onWakeUp() { synchronized (mLock) { - if (shouldEnableWakeGestureLp() - && getBatteryManagerInternal().getPlugType() != BATTERY_PLUGGED_WIRELESS) { + if (shouldEnableWakeGestureLp()) { performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false, "Wake Up"); wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture, @@ -849,15 +845,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - BatteryManagerInternal getBatteryManagerInternal() { - synchronized (mServiceAcquireLock) { - if (mBatteryManagerInternal == null) { - mBatteryManagerInternal = - LocalServices.getService(BatteryManagerInternal.class); - } - return mBatteryManagerInternal; - } - } // returns true if the key was handled and should not be passed to the user private boolean backKeyPress() { diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java index ee30fa2ac928..fb9a4d41ee90 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java @@ -15,6 +15,8 @@ */ package com.android.server.tv.tunerresourcemanager; +import android.media.tv.tunerresourcemanager.TunerResourceManager; + import java.util.HashSet; import java.util.Set; @@ -63,6 +65,11 @@ public final class ClientProfile { private int mNiceValue; /** + * The handle of the primary frontend resource + */ + private int mPrimaryUsingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; + + /** * List of the frontend handles that are used by the current client. */ private Set<Integer> mUsingFrontendHandles = new HashSet<>(); @@ -175,6 +182,22 @@ public final class ClientProfile { } /** + * Set the primary frontend used by the client + * + * @param frontendHandle being used. + */ + public void setPrimaryFrontend(int frontendHandle) { + mPrimaryUsingFrontendHandle = frontendHandle; + } + + /** + * Get the primary frontend used by the client + */ + public int getPrimaryFrontend() { + return mPrimaryUsingFrontendHandle; + } + + /** * Update the set of client that share frontend with the current client. * * @param clientId the client to share the fe with the current client. @@ -206,6 +229,7 @@ public final class ClientProfile { public void releaseFrontend() { mUsingFrontendHandles.clear(); mShareFeClientIds.clear(); + mPrimaryUsingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; } /** @@ -276,6 +300,7 @@ public final class ClientProfile { public void reclaimAllResources() { mUsingFrontendHandles.clear(); mShareFeClientIds.clear(); + mPrimaryUsingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE; mUsingLnbHandles.clear(); mUsingCasSystemId = INVALID_RESOURCE_ID; mUsingCiCamId = INVALID_RESOURCE_ID; diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java index af705d597af2..6162d716b85e 100644 --- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java +++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java @@ -42,6 +42,7 @@ import android.os.SystemClock; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; +import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -72,14 +73,28 @@ public class TunerResourceManagerService extends SystemService implements IBinde private static final long INVALID_THREAD_ID = -1; private static final long TRMS_LOCK_TIMEOUT = 500; + private static final int INVALID_FE_COUNT = -1; + // Map of the registered client profiles private Map<Integer, ClientProfile> mClientProfiles = new HashMap<>(); private int mNextUnusedClientId = 0; // Map of the current available frontend resources private Map<Integer, FrontendResource> mFrontendResources = new HashMap<>(); - // Backup Map of the current available frontend resources + // SparseIntArray of the max usable number for each frontend resource type + private SparseIntArray mFrontendMaxUsableNums = new SparseIntArray(); + // SparseIntArray of the currently used number for each frontend resource type + private SparseIntArray mFrontendUsedNums = new SparseIntArray(); + // SparseIntArray of the existing number for each frontend resource type + private SparseIntArray mFrontendExistingNums = new SparseIntArray(); + + // Backups for the frontend resource maps for enabling testing with custom resource maps + // such as TunerTest.testHasUnusedFrontend1() private Map<Integer, FrontendResource> mFrontendResourcesBackup = new HashMap<>(); + private SparseIntArray mFrontendMaxUsableNumsBackup = new SparseIntArray(); + private SparseIntArray mFrontendUsedNumsBackup = new SparseIntArray(); + private SparseIntArray mFrontendExistingNumsBackup = new SparseIntArray(); + // Map of the current available lnb resources private Map<Integer, LnbResource> mLnbResources = new HashMap<>(); // Map of the current available Cas resources @@ -268,6 +283,29 @@ public class TunerResourceManagerService extends SystemService implements IBinde } @Override + public boolean setMaxNumberOfFrontends(int frontendType, int maxUsableNum) { + enforceTunerAccessPermission("requestFrontend"); + enforceTrmAccessPermission("requestFrontend"); + if (maxUsableNum < 0) { + Slog.w(TAG, "setMaxNumberOfFrontends failed with maxUsableNum:" + maxUsableNum + + " frontendType:" + frontendType); + return false; + } + synchronized (mLock) { + return setMaxNumberOfFrontendsInternal(frontendType, maxUsableNum); + } + } + + @Override + public int getMaxNumberOfFrontends(int frontendType) { + enforceTunerAccessPermission("requestFrontend"); + enforceTrmAccessPermission("requestFrontend"); + synchronized (mLock) { + return getMaxNumberOfFrontendsInternal(frontendType); + } + } + + @Override public void shareFrontend(int selfClientId, int targetClientId) throws RemoteException { enforceTunerAccessPermission("shareFrontend"); enforceTrmAccessPermission("shareFrontend"); @@ -572,71 +610,19 @@ public class TunerResourceManagerService extends SystemService implements IBinde } synchronized (mLock) { - if (mClientProfiles != null) { - pw.println("ClientProfiles:"); - pw.increaseIndent(); - for (Map.Entry<Integer, ClientProfile> entry : mClientProfiles.entrySet()) { - pw.println(entry.getKey() + " : " + entry.getValue()); - } - pw.decreaseIndent(); - } - - if (mFrontendResources != null) { - pw.println("FrontendResources:"); - pw.increaseIndent(); - for (Map.Entry<Integer, FrontendResource> entry - : mFrontendResources.entrySet()) { - pw.println(entry.getKey() + " : " + entry.getValue()); - } - pw.decreaseIndent(); - } - - if (mFrontendResourcesBackup != null) { - pw.println("FrontendResourcesBackUp:"); - pw.increaseIndent(); - for (Map.Entry<Integer, FrontendResource> entry - : mFrontendResourcesBackup.entrySet()) { - pw.println(entry.getKey() + " : " + entry.getValue()); - } - pw.decreaseIndent(); - } - - if (mLnbResources != null) { - pw.println("LnbResources:"); - pw.increaseIndent(); - for (Map.Entry<Integer, LnbResource> entry : mLnbResources.entrySet()) { - pw.println(entry.getKey() + " : " + entry.getValue()); - } - pw.decreaseIndent(); - } - - if (mCasResources != null) { - pw.println("CasResources:"); - pw.increaseIndent(); - for (Map.Entry<Integer, CasResource> entry : mCasResources.entrySet()) { - pw.println(entry.getKey() + " : " + entry.getValue()); - } - pw.decreaseIndent(); - } - - if (mCiCamResources != null) { - pw.println("CiCamResources:"); - pw.increaseIndent(); - for (Map.Entry<Integer, CiCamResource> entry : mCiCamResources.entrySet()) { - pw.println(entry.getKey() + " : " + entry.getValue()); - } - pw.decreaseIndent(); - } - - if (mListeners != null) { - pw.println("Listners:"); - pw.increaseIndent(); - for (Map.Entry<Integer, ResourcesReclaimListenerRecord> entry - : mListeners.entrySet()) { - pw.println(entry.getKey() + " : " + entry.getValue()); - } - pw.decreaseIndent(); - } + dumpMap(mClientProfiles, "ClientProfiles:", "\n", pw); + dumpMap(mFrontendResources, "FrontendResources:", "\n", pw); + dumpSIA(mFrontendExistingNums, "FrontendExistingNums:", ", ", pw); + dumpSIA(mFrontendUsedNums, "FrontendUsedNums:", ", ", pw); + dumpSIA(mFrontendMaxUsableNums, "FrontendMaxUsableNums:", ", ", pw); + dumpMap(mFrontendResourcesBackup, "FrontendResourcesBackUp:", "\n", pw); + dumpSIA(mFrontendExistingNumsBackup, "FrontendExistingNumsBackup:", ", ", pw); + dumpSIA(mFrontendUsedNumsBackup, "FrontendUsedNumsBackup:", ", ", pw); + dumpSIA(mFrontendMaxUsableNumsBackup, "FrontendUsedNumsBackup:", ", ", pw); + dumpMap(mLnbResources, "LnbResource:", "\n", pw); + dumpMap(mCasResources, "CasResource:", "\n", pw); + dumpMap(mCiCamResources, "CiCamResource:", "\n", pw); + dumpMap(mListeners, "Listners:", "\n", pw); } } @@ -786,10 +772,10 @@ public class TunerResourceManagerService extends SystemService implements IBinde protected void storeResourceMapInternal(int resourceType) { switch (resourceType) { case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: - if (mFrontendResources != null && mFrontendResources.size() > 0) { - mFrontendResourcesBackup.putAll(mFrontendResources); - mFrontendResources.clear(); - } + replaceFeResourceMap(mFrontendResources, mFrontendResourcesBackup); + replaceFeCounts(mFrontendExistingNums, mFrontendExistingNumsBackup); + replaceFeCounts(mFrontendUsedNums, mFrontendUsedNumsBackup); + replaceFeCounts(mFrontendMaxUsableNums, mFrontendMaxUsableNumsBackup); break; // TODO: implement for other resource type when needed default: @@ -800,9 +786,10 @@ public class TunerResourceManagerService extends SystemService implements IBinde protected void clearResourceMapInternal(int resourceType) { switch (resourceType) { case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: - if (mFrontendResources != null) { - mFrontendResources.clear(); - } + replaceFeResourceMap(null, mFrontendResources); + replaceFeCounts(null, mFrontendExistingNums); + replaceFeCounts(null, mFrontendUsedNums); + replaceFeCounts(null, mFrontendMaxUsableNums); break; // TODO: implement for other resource type when needed default: @@ -813,12 +800,10 @@ public class TunerResourceManagerService extends SystemService implements IBinde protected void restoreResourceMapInternal(int resourceType) { switch (resourceType) { case TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND: - if (mFrontendResourcesBackup != null - && mFrontendResourcesBackup.size() > 0) { - mFrontendResources.clear(); - mFrontendResources.putAll(mFrontendResourcesBackup); - mFrontendResourcesBackup.clear(); - } + replaceFeResourceMap(mFrontendResourcesBackup, mFrontendResources); + replaceFeCounts(mFrontendExistingNumsBackup, mFrontendExistingNums); + replaceFeCounts(mFrontendUsedNumsBackup, mFrontendUsedNums); + replaceFeCounts(mFrontendMaxUsableNumsBackup, mFrontendMaxUsableNums); break; // TODO: implement for other resource type when needed default: @@ -954,6 +939,11 @@ public class TunerResourceManagerService extends SystemService implements IBinde for (FrontendResource fr : getFrontendResources().values()) { if (fr.getType() == request.frontendType) { if (!fr.isInUse()) { + // Unused resource cannot be acquired if the max is already reached, but + // TRM still has to look for the reclaim candidate + if (isFrontendMaxNumUseReached(request.frontendType)) { + continue; + } // Grant unused frontend with no exclusive group members first. if (fr.getExclusiveGroupMemberFeHandles().isEmpty()) { grantingFrontendHandle = fr.getHandle(); @@ -1021,6 +1011,9 @@ public class TunerResourceManagerService extends SystemService implements IBinde for (int inUseHandle : newOwnerProfile.getInUseFrontendHandles()) { getFrontendResource(inUseHandle).setOwner(newOwnerId); } + // change the primary frontend + newOwnerProfile.setPrimaryFrontend(currentOwnerProfile.getPrimaryFrontend()); + currentOwnerProfile.setPrimaryFrontend(TunerResourceManager.INVALID_RESOURCE_HANDLE); // double check there is no other resources tied to the previous owner for (int inUseHandle : currentOwnerProfile.getInUseFrontendHandles()) { int ownerId = getFrontendResource(inUseHandle).getOwnerClientId(); @@ -1657,11 +1650,13 @@ public class TunerResourceManagerService extends SystemService implements IBinde FrontendResource grantingFrontend = getFrontendResource(grantingHandle); ClientProfile ownerProfile = getClientProfile(ownerClientId); grantingFrontend.setOwner(ownerClientId); + increFrontendNum(mFrontendUsedNums, grantingFrontend.getType()); ownerProfile.useFrontend(grantingHandle); for (int exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeHandles()) { getFrontendResource(exclusiveGroupMember).setOwner(ownerClientId); ownerProfile.useFrontend(exclusiveGroupMember); } + ownerProfile.setPrimaryFrontend(grantingHandle); } private void updateLnbClientMappingOnNewGrant(int grantingHandle, int ownerClientId) { @@ -1755,6 +1750,109 @@ public class TunerResourceManagerService extends SystemService implements IBinde return mFrontendResources; } + private boolean setMaxNumberOfFrontendsInternal(int frontendType, int maxUsableNum) { + int usedNum = mFrontendUsedNums.get(frontendType, INVALID_FE_COUNT); + if (usedNum == INVALID_FE_COUNT || usedNum <= maxUsableNum) { + mFrontendMaxUsableNums.put(frontendType, maxUsableNum); + return true; + } else { + Slog.e(TAG, "max number of frontend for frontendType: " + frontendType + + " cannot be set to a value lower than the current usage count." + + " (requested max num = " + maxUsableNum + ", current usage = " + usedNum); + return false; + } + } + + private int getMaxNumberOfFrontendsInternal(int frontendType) { + int existingNum = mFrontendExistingNums.get(frontendType, INVALID_FE_COUNT); + if (existingNum == INVALID_FE_COUNT) { + Log.e(TAG, "existingNum is -1 for " + frontendType); + return -1; + } + int maxUsableNum = mFrontendMaxUsableNums.get(frontendType, INVALID_FE_COUNT); + if (maxUsableNum == INVALID_FE_COUNT) { + return existingNum; + } else { + return maxUsableNum; + } + } + + private boolean isFrontendMaxNumUseReached(int frontendType) { + int maxUsableNum = mFrontendMaxUsableNums.get(frontendType, INVALID_FE_COUNT); + if (maxUsableNum == INVALID_FE_COUNT) { + return false; + } + int useNum = mFrontendUsedNums.get(frontendType, INVALID_FE_COUNT); + if (useNum == INVALID_FE_COUNT) { + useNum = 0; + } + return useNum >= maxUsableNum; + } + + private void increFrontendNum(SparseIntArray targetNums, int frontendType) { + int num = targetNums.get(frontendType, INVALID_FE_COUNT); + if (num == INVALID_FE_COUNT) { + targetNums.put(frontendType, 1); + } else { + targetNums.put(frontendType, num + 1); + } + } + + private void decreFrontendNum(SparseIntArray targetNums, int frontendType) { + int num = targetNums.get(frontendType, INVALID_FE_COUNT); + if (num != INVALID_FE_COUNT) { + targetNums.put(frontendType, num - 1); + } + } + + private void replaceFeResourceMap(Map<Integer, FrontendResource> srcMap, Map<Integer, + FrontendResource> dstMap) { + if (dstMap != null) { + dstMap.clear(); + if (srcMap != null && srcMap.size() > 0) { + dstMap.putAll(srcMap); + } + } + } + + private void replaceFeCounts(SparseIntArray srcCounts, SparseIntArray dstCounts) { + if (dstCounts != null) { + dstCounts.clear(); + if (srcCounts != null) { + for (int i = 0; i < srcCounts.size(); i++) { + dstCounts.put(srcCounts.keyAt(i), srcCounts.valueAt(i)); + } + } + } + } + private void dumpMap(Map<?, ?> targetMap, String headline, String delimiter, + IndentingPrintWriter pw) { + if (targetMap != null) { + pw.println(headline); + pw.increaseIndent(); + for (Map.Entry<?, ?> entry : targetMap.entrySet()) { + pw.print(entry.getKey() + " : " + entry.getValue()); + pw.print(delimiter); + } + pw.println(); + pw.decreaseIndent(); + } + } + + private void dumpSIA(SparseIntArray array, String headline, String delimiter, + IndentingPrintWriter pw) { + if (array != null) { + pw.println(headline); + pw.increaseIndent(); + for (int i = 0; i < array.size(); i++) { + pw.print(array.keyAt(i) + " : " + array.valueAt(i)); + pw.print(delimiter); + } + pw.println(); + pw.decreaseIndent(); + } + } + private void addFrontendResource(FrontendResource newFe) { // Update the exclusive group member list in all the existing Frontend resource for (FrontendResource fe : getFrontendResources().values()) { @@ -1771,6 +1869,8 @@ public class TunerResourceManagerService extends SystemService implements IBinde } // Update resource list and available id list mFrontendResources.put(newFe.getHandle(), newFe); + increFrontendNum(mFrontendExistingNums, newFe.getType()); + } private void removeFrontendResource(int removingHandle) { @@ -1789,6 +1889,7 @@ public class TunerResourceManagerService extends SystemService implements IBinde getFrontendResource(excGroupmemberFeHandle) .removeExclusiveGroupMemberFeId(fe.getHandle()); } + decreFrontendNum(mFrontendExistingNums, fe.getType()); mFrontendResources.remove(removingHandle); } @@ -1918,6 +2019,15 @@ public class TunerResourceManagerService extends SystemService implements IBinde } } + + int primaryFeId = profile.getPrimaryFrontend(); + if (primaryFeId != TunerResourceManager.INVALID_RESOURCE_HANDLE) { + FrontendResource primaryFe = getFrontendResource(primaryFeId); + if (primaryFe != null) { + decreFrontendNum(mFrontendUsedNums, primaryFe.getType()); + } + } + profile.releaseFrontend(); } diff --git a/services/core/java/com/android/server/utils/WatchedArrayList.java b/services/core/java/com/android/server/utils/WatchedArrayList.java index 6059f9675e34..07474a63cb2a 100644 --- a/services/core/java/com/android/server/utils/WatchedArrayList.java +++ b/services/core/java/com/android/server/utils/WatchedArrayList.java @@ -416,7 +416,7 @@ public class WatchedArrayList<E> extends WatchableImpl dst.mStorage.ensureCapacity(end); for (int i = 0; i < end; i++) { final E val = Snapshots.maybeSnapshot(src.get(i)); - dst.add(i, val); + dst.mStorage.add(val); } dst.seal(); } diff --git a/services/core/java/com/android/server/utils/WatchedArrayMap.java b/services/core/java/com/android/server/utils/WatchedArrayMap.java index 7c1cde8502bd..63441aac8e1f 100644 --- a/services/core/java/com/android/server/utils/WatchedArrayMap.java +++ b/services/core/java/com/android/server/utils/WatchedArrayMap.java @@ -465,7 +465,7 @@ public class WatchedArrayMap<K, V> extends WatchableImpl for (int i = 0; i < end; i++) { final V val = Snapshots.maybeSnapshot(src.valueAt(i)); final K key = src.keyAt(i); - dst.put(key, val); + dst.mStorage.put(key, val); } dst.seal(); } diff --git a/services/core/java/com/android/server/utils/WatchedArraySet.java b/services/core/java/com/android/server/utils/WatchedArraySet.java index ec80261a2196..a2eaed72dd65 100644 --- a/services/core/java/com/android/server/utils/WatchedArraySet.java +++ b/services/core/java/com/android/server/utils/WatchedArraySet.java @@ -427,7 +427,7 @@ public class WatchedArraySet<E> extends WatchableImpl dst.mStorage.ensureCapacity(end); for (int i = 0; i < end; i++) { final E val = Snapshots.maybeSnapshot(src.valueAt(i)); - dst.append(val); + dst.mStorage.append(val); } dst.seal(); } diff --git a/services/core/java/com/android/server/utils/WatchedLongSparseArray.java b/services/core/java/com/android/server/utils/WatchedLongSparseArray.java index bf23de12ffef..9da9e75f98fb 100644 --- a/services/core/java/com/android/server/utils/WatchedLongSparseArray.java +++ b/services/core/java/com/android/server/utils/WatchedLongSparseArray.java @@ -410,7 +410,7 @@ public class WatchedLongSparseArray<E> extends WatchableImpl for (int i = 0; i < end; i++) { final E val = Snapshots.maybeSnapshot(src.valueAt(i)); final long key = src.keyAt(i); - dst.put(key, val); + dst.mStorage.put(key, val); } dst.seal(); } diff --git a/services/core/java/com/android/server/utils/WatchedSparseArray.java b/services/core/java/com/android/server/utils/WatchedSparseArray.java index 9b99b9176d19..6ce38b71d9cd 100644 --- a/services/core/java/com/android/server/utils/WatchedSparseArray.java +++ b/services/core/java/com/android/server/utils/WatchedSparseArray.java @@ -490,7 +490,7 @@ public class WatchedSparseArray<E> extends WatchableImpl for (int i = 0; i < end; i++) { final E val = Snapshots.maybeSnapshot(src.valueAt(i)); final int key = src.keyAt(i); - dst.put(key, val); + dst.mStorage.put(key, val); } dst.seal(); } diff --git a/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java b/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java index 772a8d07cffb..50e2272afb72 100644 --- a/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java +++ b/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java @@ -310,7 +310,7 @@ public class WatchedSparseBooleanArray extends WatchableImpl } final int end = src.size(); for (int i = 0; i < end; i++) { - dst.put(src.keyAt(i), src.valueAt(i)); + dst.mStorage.put(src.keyAt(i), src.valueAt(i)); } dst.seal(); } diff --git a/services/core/java/com/android/server/utils/WatchedSparseIntArray.java b/services/core/java/com/android/server/utils/WatchedSparseIntArray.java index 72705bf24199..53d168245180 100644 --- a/services/core/java/com/android/server/utils/WatchedSparseIntArray.java +++ b/services/core/java/com/android/server/utils/WatchedSparseIntArray.java @@ -315,7 +315,7 @@ public class WatchedSparseIntArray extends WatchableImpl } final int end = src.size(); for (int i = 0; i < end; i++) { - dst.put(src.keyAt(i), src.valueAt(i)); + dst.mStorage.put(src.keyAt(i), src.valueAt(i)); } dst.seal(); } diff --git a/services/core/java/com/android/server/utils/WatchedSparseSetArray.java b/services/core/java/com/android/server/utils/WatchedSparseSetArray.java index 05db12e49a13..77750ed92273 100644 --- a/services/core/java/com/android/server/utils/WatchedSparseSetArray.java +++ b/services/core/java/com/android/server/utils/WatchedSparseSetArray.java @@ -169,7 +169,7 @@ public class WatchedSparseSetArray<T> extends WatchableImpl implements Snappable final ArraySet set = src.get(i); final int setSize = set.size(); for (int j = 0; j < setSize; j++) { - dst.add(src.keyAt(i), set.valueAt(j)); + dst.mStorage.add(src.keyAt(i), set.valueAt(j)); } } dst.seal(); diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 63619e543a6d..f1dbad61a76d 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -446,6 +446,43 @@ final class AccessibilityController { // Not relevant for the window observer. } + public Pair<Matrix, MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec( + IBinder token) { + synchronized (mService.mGlobalLock) { + final Matrix transformationMatrix = new Matrix(); + final MagnificationSpec magnificationSpec = new MagnificationSpec(); + + final WindowState windowState = mService.mWindowMap.get(token); + if (windowState != null) { + windowState.getTransformationMatrix(new float[9], transformationMatrix); + + if (hasCallbacks()) { + final MagnificationSpec otherMagnificationSpec = + getMagnificationSpecForWindow(windowState); + if (otherMagnificationSpec != null && !otherMagnificationSpec.isNop()) { + magnificationSpec.setTo(otherMagnificationSpec); + } + } + } + + return new Pair<>(transformationMatrix, magnificationSpec); + } + } + + MagnificationSpec getMagnificationSpecForWindow(WindowState windowState) { + if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) { + mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForWindow", + FLAGS_MAGNIFICATION_CALLBACK, + "windowState={" + windowState + "}"); + } + final int displayId = windowState.getDisplayId(); + final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); + if (displayMagnifier != null) { + return displayMagnifier.getMagnificationSpecForWindow(windowState); + } + return null; + } + boolean hasCallbacks() { if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) { diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index cb6559715c55..701fc9441acb 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -68,7 +68,6 @@ import static com.android.server.wm.AppTransition.isNormalTransit; import static com.android.server.wm.NonAppWindowAnimationAdapter.shouldAttachNavBarToApp; import static com.android.server.wm.NonAppWindowAnimationAdapter.shouldStartNonAppWindowAnimationsForKeyguardExit; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; -import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static com.android.server.wm.WallpaperAnimationAdapter.shouldStartWallpaperAnimation; import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; @@ -1143,17 +1142,13 @@ public class AppTransitionController { if (activity == null) { continue; } - if (activity.isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) { - ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, - "Delaying app transition for recents animation to finish"); - return false; - } ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Check opening app=%s: allDrawn=%b startingDisplayed=%b " + "startingMoved=%b isRelaunching()=%b startingWindow=%s", activity, activity.allDrawn, activity.startingDisplayed, activity.startingMoved, activity.isRelaunching(), activity.mStartingWindow); + final boolean allDrawn = activity.allDrawn && !activity.isRelaunching(); if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) { return false; diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java index ee7d9a9b3f2d..46ce43335f01 100644 --- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java +++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java @@ -153,10 +153,10 @@ class BLASTSyncEngine { for (WindowContainer wc : mRootMembers) { wc.waitForSyncTransactionCommit(wcAwaitingCommit); } - final Runnable callback = new Runnable() { + class CommitCallback implements Runnable { // Can run a second time if the action completes after the timeout. boolean ran = false; - public void run() { + public void onCommitted() { synchronized (mWm.mGlobalLock) { if (ran) { return; @@ -171,8 +171,23 @@ class BLASTSyncEngine { wcAwaitingCommit.clear(); } } + + // Called in timeout + @Override + public void run() { + // Sometimes we get a trace, sometimes we get a bugreport without + // a trace. Since these kind of ANRs can trigger such an issue, + // try and ensure we will have some visibility in both cases. + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionCommitTimeout"); + Slog.e(TAG, "WM sent Transaction to organized, but never received" + + " commit callback. Application ANR likely to follow."); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + onCommitted(); + + } }; - merged.addTransactionCommittedListener((r) -> { r.run(); }, callback::run); + CommitCallback callback = new CommitCallback(); + merged.addTransactionCommittedListener((r) -> { r.run(); }, callback::onCommitted); mWm.mH.postDelayed(callback, BLAST_TIMEOUT_DURATION); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionReady"); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index a480c37fbcf3..2f00bc821678 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1584,14 +1584,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return true; } - void forAllWindowContainers(Consumer<WindowContainer> callback) { - callback.accept(this); - final int count = mChildren.size(); - for (int i = 0; i < count; i++) { - mChildren.get(i).forAllWindowContainers(callback); - } - } - /** * For all windows at or below this container call the callback. * @param callback Calls the {@link ToBooleanFunction#apply} method for each window found and diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index f8bc26a7d91d..c0d7d1362ac3 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -23,17 +23,18 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ClipData; import android.content.Context; +import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.Region; import android.hardware.display.DisplayManagerInternal; import android.os.Bundle; import android.os.IBinder; +import android.util.Pair; import android.view.Display; import android.view.IInputFilter; import android.view.IRemoteAnimationFinishedCallback; import android.view.IWindow; import android.view.InputChannel; -import android.view.InputWindowHandle; import android.view.MagnificationSpec; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; @@ -459,6 +460,17 @@ public abstract class WindowManagerInternal { public abstract void getWindowFrame(IBinder token, Rect outBounds); /** + * Get the transformation matrix and MagnificationSpec given its token. + * + * @param token The token. + * @return The pair of the transformation matrix and magnification spec. + */ + // TODO (b/231663133): Long term solution for tracking window when the + // FLAG_RETRIEVE_INTERACTIVE_WINDOWS is unset. + public abstract Pair<Matrix, MagnificationSpec> + getWindowTransformationMatrixAndMagnificationSpec(IBinder token); + + /** * Opens the global actions dialog. */ public abstract void showGlobalActions(); @@ -849,24 +861,12 @@ public abstract class WindowManagerInternal { public abstract SurfaceControl getHandwritingSurfaceForDisplay(int displayId); /** - * Replaces the touchable region of the provided input surface with the crop of the window with - * the provided token. This method will associate the inputSurface with a copy of - * the given inputWindowHandle, where the copy is configured using - * {@link InputWindowHandle#replaceTouchableRegionWithCrop(SurfaceControl)} with the surface - * of the provided windowToken. - * - * This is a no-op if windowToken is not valid or the window is not found. - * - * This does not change any other properties of the inputSurface. - * - * This method exists to avoid leaking the window's SurfaceControl outside WindowManagerService. + * Returns {@code true} if the given point is within the window bounds of the given window. * - * @param inputSurface The surface for which the touchable region should be set. - * @param inputWindowHandle The {@link InputWindowHandle} for the input surface. - * @param windowToken The window whose bounds should be used as the touchable region for the - * inputSurface. + * @param windowToken the window whose bounds should be used for the hit test. + * @param displayX the x coordinate of the test point in the display's coordinate space. + * @param displayY the y coordinate of the test point in the display's coordinate space. */ - public abstract void replaceInputSurfaceTouchableRegionWithWindowCrop( - @NonNull SurfaceControl inputSurface, @NonNull InputWindowHandle inputWindowHandle, - @NonNull IBinder windowToken); + public abstract boolean isPointInsideWindow( + @NonNull IBinder windowToken, int displayId, float displayX, float displayY); } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index a9f56d3d51e1..8f1f7ece897b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -118,7 +118,6 @@ import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL; import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING; import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL; -import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; @@ -173,6 +172,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.database.ContentObserver; import android.graphics.Bitmap; +import android.graphics.Matrix; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; @@ -224,6 +224,7 @@ import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.MergedConfiguration; +import android.util.Pair; import android.util.Slog; import android.util.SparseBooleanArray; import android.util.TimeUtils; @@ -3051,22 +3052,13 @@ public class WindowManagerService extends IWindowManager.Stub } } - void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) { if (mRecentsAnimationController != null) { final RecentsAnimationController controller = mRecentsAnimationController; mRecentsAnimationController = null; controller.cleanupAnimation(reorderMode); - // TODO(multi-display): currently only default display support recents animation. - // Cancel any existing app transition animation running in the legacy transition - // framework. - final DisplayContent dc = getDefaultDisplayContentLocked(); - dc.mAppTransition.freeze(); - dc.forAllWindowContainers((wc) -> { - if (wc.isAnimating(TRANSITION, ANIMATION_TYPE_APP_TRANSITION)) { - wc.cancelAnimation(); - } - }); + // TODO(mult-display): currently only default display support recents animation. + getDefaultDisplayContentLocked().mAppTransition.updateBooster(); } } @@ -7769,6 +7761,13 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public Pair<Matrix, MagnificationSpec> getWindowTransformationMatrixAndMagnificationSpec( + IBinder token) { + return mAccessibilityController + .getWindowTransformationMatrixAndMagnificationSpec(token); + } + + @Override public void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId) { final WindowContainer container = displayId == INVALID_DISPLAY ? mRoot : mRoot.getDisplayContent(displayId); @@ -8241,23 +8240,15 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void replaceInputSurfaceTouchableRegionWithWindowCrop( - @NonNull SurfaceControl inputSurface, - @NonNull InputWindowHandle inputWindowHandle, - @NonNull IBinder windowToken) { + public boolean isPointInsideWindow(@NonNull IBinder windowToken, int displayId, + float displayX, float displayY) { synchronized (mGlobalLock) { final WindowState w = mWindowMap.get(windowToken); - if (w == null) { - return; + if (w == null || w.getDisplayId() != displayId) { + return false; } - // Make a copy of the InputWindowHandle to avoid leaking the window's - // SurfaceControl. - final InputWindowHandle localHandle = new InputWindowHandle(inputWindowHandle); - localHandle.replaceTouchableRegionWithCrop(w.getSurfaceControl()); - final SurfaceControl.Transaction t = mTransactionFactory.get(); - t.setInputWindowInfo(inputSurface, localHandle); - t.apply(); - t.close(); + + return w.getBounds().contains((int) displayX, (int) displayY); } } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java index ac542935fa0a..009dae51e94b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java @@ -582,6 +582,7 @@ public final class BackgroundRestrictionTest { DeviceConfigSession<Boolean> bgCurrentDrainMonitor = null; DeviceConfigSession<Long> bgCurrentDrainWindow = null; + DeviceConfigSession<Long> bgCurrentDrainInteractionGracePeriod = null; DeviceConfigSession<Float> bgCurrentDrainRestrictedBucketThreshold = null; DeviceConfigSession<Float> bgCurrentDrainBgRestrictedThreshold = null; DeviceConfigSession<Boolean> bgPromptFgsWithNotiToBgRestricted = null; @@ -617,6 +618,14 @@ public final class BackgroundRestrictionTest { R.integer.config_bg_current_drain_window)); bgCurrentDrainWindow.set(windowMs); + bgCurrentDrainInteractionGracePeriod = new DeviceConfigSession<>( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_INTERACTION_GRACE_PERIOD, + DeviceConfig::getLong, + (long) mContext.getResources().getInteger( + R.integer.config_bg_current_drain_window)); + bgCurrentDrainInteractionGracePeriod.set(windowMs); + bgCurrentDrainRestrictedBucketThreshold = new DeviceConfigSession<>( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET, @@ -768,6 +777,32 @@ public final class BackgroundRestrictionTest { clearInvocations(mInjector.getAppStandbyInternal()); + // It won't be restricted since user just interacted with it. + runTestBgCurrentDrainMonitorOnce(listener, stats, uids, + zeros, new double[]{0, restrictBucketThresholdMah - 1}, + zeros, new double[]{restrictBucketThresholdMah + 1, 0}, + () -> { + doReturn(mCurrentTimeMillis).when(stats).getStatsStartTimestamp(); + doReturn(mCurrentTimeMillis + windowMs) + .when(stats).getStatsEndTimestamp(); + mCurrentTimeMillis += windowMs + 1; + try { + listener.verify(timeout, testUid, testPkgName, + RESTRICTION_LEVEL_RESTRICTED_BUCKET); + fail("There shouldn't be any level change events"); + } catch (Exception e) { + // Expected. + } + verify(mInjector.getAppStandbyInternal(), never()).restrictApp( + eq(testPkgName), + eq(testUser), + anyInt(), anyInt()); + }); + + // Sleep a while. + Thread.sleep(windowMs); + clearInvocations(mInjector.getAppStandbyInternal()); + // Now it should have been restricted. runTestBgCurrentDrainMonitorOnce(listener, stats, uids, zeros, new double[]{0, restrictBucketThresholdMah - 1}, zeros, new double[]{restrictBucketThresholdMah + 1, 0}, @@ -1061,6 +1096,7 @@ public final class BackgroundRestrictionTest { } finally { closeIfNotNull(bgCurrentDrainMonitor); closeIfNotNull(bgCurrentDrainWindow); + closeIfNotNull(bgCurrentDrainInteractionGracePeriod); closeIfNotNull(bgCurrentDrainRestrictedBucketThreshold); closeIfNotNull(bgCurrentDrainBgRestrictedThreshold); closeIfNotNull(bgPromptFgsWithNotiToBgRestricted); @@ -1610,6 +1646,7 @@ public final class BackgroundRestrictionTest { DeviceConfigSession<Boolean> bgCurrentDrainMonitor = null; DeviceConfigSession<Long> bgCurrentDrainWindow = null; + DeviceConfigSession<Long> bgCurrentDrainInteractionGracePeriod = null; DeviceConfigSession<Float> bgCurrentDrainRestrictedBucketThreshold = null; DeviceConfigSession<Float> bgCurrentDrainBgRestrictedThreshold = null; DeviceConfigSession<Float> bgCurrentDrainRestrictedBucketHighThreshold = null; @@ -1655,6 +1692,14 @@ public final class BackgroundRestrictionTest { R.integer.config_bg_current_drain_window)); bgCurrentDrainWindow.set(windowMs); + bgCurrentDrainInteractionGracePeriod = new DeviceConfigSession<>( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_INTERACTION_GRACE_PERIOD, + DeviceConfig::getLong, + (long) mContext.getResources().getInteger( + R.integer.config_bg_current_drain_window)); + bgCurrentDrainInteractionGracePeriod.set(windowMs); + bgCurrentDrainRestrictedBucketThreshold = new DeviceConfigSession<>( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET, @@ -2176,6 +2221,7 @@ public final class BackgroundRestrictionTest { } finally { closeIfNotNull(bgCurrentDrainMonitor); closeIfNotNull(bgCurrentDrainWindow); + closeIfNotNull(bgCurrentDrainInteractionGracePeriod); closeIfNotNull(bgCurrentDrainRestrictedBucketThreshold); closeIfNotNull(bgCurrentDrainBgRestrictedThreshold); closeIfNotNull(bgCurrentDrainRestrictedBucketHighThreshold); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java index 08df4381ad62..19df5a2ddd8b 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java @@ -89,8 +89,10 @@ import android.os.PowerManager; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; +import android.util.Pair; import android.view.Display; import android.view.KeyEvent; +import android.view.MagnificationSpec; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityWindowInfo; import android.view.accessibility.IAccessibilityInteractionConnection; @@ -145,6 +147,8 @@ public class AbstractAccessibilityServiceConnectionTest { private static final int USER_ID = 1; private static final int USER_ID2 = 2; private static final int INTERACTION_ID = 199; + private static final Pair<float[], MagnificationSpec> FAKE_MATRIX_AND_MAG_SPEC = + new Pair<>(new float[9], new MagnificationSpec()); private static final int PID = Process.myPid(); private static final long TID = Process.myTid(); private static final int UID = Process.myUid(); @@ -188,6 +192,8 @@ public class AbstractAccessibilityServiceConnectionTest { .thenReturn(mMockFingerprintGestureDispatcher); when(mMockSystemSupport.getMagnificationProcessor()) .thenReturn(mMockMagnificationProcessor); + when(mMockSystemSupport.getWindowTransformationMatrixAndMagnificationSpec(anyInt())) + .thenReturn(FAKE_MATRIX_AND_MAG_SPEC); PowerManager powerManager = new PowerManager(mMockContext, mMockIPowerManager, mMockIThermalService, mHandler); diff --git a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java index d1390c68e130..e68a8a0f3af8 100644 --- a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java +++ b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java @@ -49,10 +49,11 @@ public class DropboxRateLimiterTest { assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); + assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); // Different processes and tags should not get rate limited either. assertFalse(mRateLimiter.shouldRateLimit("tag", "process2").shouldRateLimit()); assertFalse(mRateLimiter.shouldRateLimit("tag2", "process").shouldRateLimit()); - // The 6th entry of the same process should be rate limited. + // The 7th entry of the same process should be rate limited. assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); } @@ -64,12 +65,13 @@ public class DropboxRateLimiterTest { assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); - // The 6th entry of the same process should be rate limited. + assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); + // The 7th entry of the same process should be rate limited. assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); - // After 11 seconds there should be nothing left in the buffer and the same type of entry + // After 11 minutes there should be nothing left in the buffer and the same type of entry // should not get rate limited anymore. - mClock.setOffsetMillis(11000); + mClock.setOffsetMillis(11 * 60 * 1000); assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit()); } @@ -86,13 +88,15 @@ public class DropboxRateLimiterTest { mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated()); assertEquals(0, mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated()); + assertEquals(0, + mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated()); assertEquals(1, mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated()); assertEquals(2, mRateLimiter.shouldRateLimit("tag", "p").droppedCountSinceRateLimitActivated()); - // After 11 seconds the rate limiting buffer will be cleared and rate limiting will stop. - mClock.setOffsetMillis(11000); + // After 11 minutes the rate limiting buffer will be cleared and rate limiting will stop. + mClock.setOffsetMillis(11 * 60 * 1000); // The first call after rate limiting stops will still return the number of dropped events. assertEquals(2, diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java index 20486b3e396d..8167b44ee59d 100644 --- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java @@ -454,14 +454,14 @@ public class SystemConfigTest { + " <library \n" + " name=\"foo\"\n" + " file=\"" + mFooJar + "\"\n" - + " on-bootclasspath-before=\"Q\"\n" + + " on-bootclasspath-before=\"A\"\n" + " on-bootclasspath-since=\"W\"\n" + " />\n\n" + " </permissions>"; parseSharedLibraries(contents); assertFooIsOnlySharedLibrary(); SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo"); - assertThat(entry.onBootclasspathBefore).isEqualTo("Q"); + assertThat(entry.onBootclasspathBefore).isEqualTo("A"); assertThat(entry.onBootclasspathSince).isEqualTo("W"); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java index e9515fa7fd9d..ffc0dcdca5fc 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ImportanceExtractorTest.java @@ -82,8 +82,6 @@ public class ImportanceExtractorTest extends UiServiceTestCase { ImportanceExtractor extractor = new ImportanceExtractor(); extractor.setConfig(mConfig); - when(mConfig.getImportance(anyString(), anyInt())).thenReturn( - NotificationManager.IMPORTANCE_MIN); NotificationChannel channel = new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_UNSPECIFIED); @@ -101,8 +99,6 @@ public class ImportanceExtractorTest extends UiServiceTestCase { ImportanceExtractor extractor = new ImportanceExtractor(); extractor.setConfig(mConfig); - when(mConfig.getImportance(anyString(), anyInt())).thenReturn( - NotificationManager.IMPORTANCE_MIN); NotificationChannel channel = new NotificationChannel("a", "a", NotificationManager.IMPORTANCE_HIGH); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelLoggerFake.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelLoggerFake.java index f609306e44b0..6f7bacedc467 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelLoggerFake.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelLoggerFake.java @@ -28,6 +28,13 @@ public class NotificationChannelLoggerFake implements NotificationChannelLogger CallRecord(NotificationChannelEvent event) { this.event = event; } + + @Override + public String toString() { + return "CallRecord{" + + "event=" + event + + '}'; + } } private List<CallRecord> mCalls = new ArrayList<>(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index c0cd7a755e25..267a50675231 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -52,6 +52,7 @@ import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.app.PendingIntent.FLAG_MUTABLE; import static android.app.PendingIntent.FLAG_ONE_SHOT; import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; +import static android.content.pm.PackageManager.FEATURE_TELECOM; import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -187,6 +188,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.Pair; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; import android.util.Xml; @@ -220,6 +222,7 @@ import com.android.server.wm.WindowManagerInternal; import com.google.common.collect.ImmutableList; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -272,6 +275,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { private WindowManagerInternal mWindowManagerInternal; @Mock private PermissionHelper mPermissionHelper; + private NotificationChannelLoggerFake mLogger = new NotificationChannelLoggerFake(); private TestableContext mContext = spy(getContext()); private final String PKG = mContext.getPackageName(); private TestableLooper mTestableLooper; @@ -384,9 +388,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { "android.permission.WRITE_DEVICE_CONFIG", "android.permission.READ_DEVICE_CONFIG", "android.permission.READ_CONTACTS"); - Settings.Secure.putIntForUser( - getContext().getContentResolver(), - Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 0, USER_SYSTEM); MockitoAnnotations.initMocks(this); @@ -448,6 +449,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class)); when(mUm.getProfileIds(0, false)).thenReturn(new int[]{0}); + when(mPackageManagerClient.hasSystemFeature(FEATURE_TELECOM)).thenReturn(true); + ActivityManager.AppTask task = mock(ActivityManager.AppTask.class); List<ActivityManager.AppTask> taskList = new ArrayList<>(); ActivityManager.RecentTaskInfo taskInfo = new ActivityManager.RecentTaskInfo(); @@ -506,7 +509,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mAppOpsManager, mAppOpsService, mUm, mHistoryManager, mStatsManager, mock(TelephonyManager.class), mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class), - mTelecomManager); + mTelecomManager, mLogger); // Return first true for RoleObserver main-thread check when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper); @@ -582,6 +585,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertNotNull(mBinderService.getNotificationChannel( PKG, mContext.getUserId(), PKG, TEST_CHANNEL_ID)); clearInvocations(mRankingHandler); + when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); } @After @@ -1234,8 +1238,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testEnqueuedBlockedNotifications_blockedApp() throws Exception { when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false); - - mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false); + when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); final StatusBarNotification sbn = generateNotificationRecord(null).getSbn(); mBinderService.enqueueNotificationWithTag(PKG, PKG, @@ -1248,8 +1251,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testEnqueuedBlockedNotifications_blockedAppForegroundService() throws Exception { when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false); - - mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false); + when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); final StatusBarNotification sbn = generateNotificationRecord(null).getSbn(); sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE; @@ -1342,6 +1344,30 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testSetNotificationsEnabledForPackage_noChange() throws Exception { + when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); + mBinderService.setNotificationsEnabledForPackage(mContext.getPackageName(), mUid, true); + + verify(mPermissionHelper, never()).setNotificationPermission( + anyString(), anyInt(), anyBoolean(), anyBoolean()); + } + + @Test + public void testSetNotificationsEnabledForPackage() throws Exception { + when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); + mBinderService.setNotificationsEnabledForPackage(mContext.getPackageName(), mUid, false); + + verify(mPermissionHelper).setNotificationPermission( + mContext.getPackageName(), UserHandle.getUserId(mUid), false, true); + + verify(mAppOpsManager, never()).setMode(anyInt(), anyInt(), anyString(), anyInt()); + List<NotificationChannelLoggerFake.CallRecord> calls = mLogger.getCalls(); + Assert.assertEquals( + NotificationChannelLogger.NotificationChannelEvent.APP_NOTIFICATIONS_BLOCKED, + calls.get(calls.size() -1).event); + } + + @Test public void testBlockedNotifications_blockedByAssistant() throws Exception { when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false); when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); @@ -1368,7 +1394,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testBlockedNotifications_blockedByUser() throws Exception { - mService.setPreferencesHelper(mPreferencesHelper); when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false); when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); @@ -1377,7 +1402,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { NotificationRecord r = generateNotificationRecord(channel); mService.addEnqueuedNotification(r); - when(mPreferencesHelper.getImportance(anyString(), anyInt())).thenReturn(IMPORTANCE_NONE); + when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false); NotificationManagerService.PostNotificationRunnable runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), @@ -1390,8 +1415,32 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testEnqueueNotificationInternal_noChannel() throws Exception { + when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); + NotificationRecord nr = generateNotificationRecord( + new NotificationChannel("did not create", "", IMPORTANCE_DEFAULT)); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(), + nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); + waitForIdle(); + + verify(mPermissionHelper).hasPermission(mUid); + verify(mPermissionHelper, never()).hasPermission(Process.SYSTEM_UID); + + reset(mPermissionHelper); + when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); + + mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(), + nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); + waitForIdle(); + + verify(mPermissionHelper).hasPermission(mUid); + assertThat(mService.mChannelToastsSent).contains(mUid); + } + + @Test public void testEnqueueNotification_appBlocked() throws Exception { - mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false); + when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); mBinderService.enqueueNotificationWithTag(PKG, PKG, "testEnqueueNotification_appBlocked", 0, @@ -2686,6 +2735,49 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testDefaultChannelUpdatesApp_postMigrationToPermissions() throws Exception { + final NotificationChannel defaultChannel = mBinderService.getNotificationChannel( + PKG_N_MR1, ActivityManager.getCurrentUser(), PKG_N_MR1, + NotificationChannel.DEFAULT_CHANNEL_ID); + defaultChannel.setImportance(IMPORTANCE_NONE); + + mBinderService.updateNotificationChannelForPackage(PKG_N_MR1, mUid, defaultChannel); + + verify(mPermissionHelper).setNotificationPermission( + PKG_N_MR1, ActivityManager.getCurrentUser(), false, true); + } + + @Test + public void testPostNotification_appPermissionFixed() throws Exception { + when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); + when(mPermissionHelper.isPermissionFixed(PKG, 0)).thenReturn(true); + + NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel); + mBinderService.enqueueNotificationWithTag(PKG, PKG, + "testPostNotification_appPermissionFixed", 0, + temp.getNotification(), 0); + waitForIdle(); + assertThat(mService.getNotificationRecordCount()).isEqualTo(1); + StatusBarNotification[] notifs = + mBinderService.getActiveNotifications(PKG); + assertThat(mService.getNotificationRecord(notifs[0].getKey()).isImportanceFixed()).isTrue(); + } + + @Test + public void testSummaryNotification_appPermissionFixed() { + NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel); + mService.addNotification(temp); + + when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); + when(mPermissionHelper.isPermissionFixed(PKG, temp.getUserId())).thenReturn(true); + + NotificationRecord r = mService.createAutoGroupSummary( + temp.getUserId(), temp.getSbn().getPackageName(), temp.getKey(), false); + + assertThat(r.isImportanceFixed()).isTrue(); + } + + @Test public void testTvExtenderChannelOverride_onTv() throws Exception { mService.setIsTelevision(true); mService.setPreferencesHelper(mPreferencesHelper); @@ -2718,13 +2810,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testUpdateAppNotifyCreatorBlock() throws Exception { - mService.setPreferencesHelper(mPreferencesHelper); - when(mPreferencesHelper.getImportance(PKG, mUid)).thenReturn(IMPORTANCE_DEFAULT); + when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); - // should trigger a broadcast mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false); Thread.sleep(500); waitForIdle(); + ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null)); @@ -2736,7 +2827,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testUpdateAppNotifyCreatorBlock_notIfMatchesExistingSetting() throws Exception { - mService.setPreferencesHelper(mPreferencesHelper); + when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); mBinderService.setNotificationsEnabledForPackage(PKG, 0, false); verify(mContext, never()).sendBroadcastAsUser(any(), any(), eq(null)); @@ -2744,15 +2835,12 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testUpdateAppNotifyCreatorUnblock() throws Exception { - mService.setPreferencesHelper(mPreferencesHelper); - - // should not trigger a broadcast - when(mAppOpsManager.checkOpNoThrow(anyInt(), eq(mUid), eq(PKG))).thenReturn(MODE_ALLOWED); + when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); - // should trigger a broadcast - mBinderService.setNotificationsEnabledForPackage(PKG, 0, true); + mBinderService.setNotificationsEnabledForPackage(PKG, mUid, true); Thread.sleep(500); waitForIdle(); + ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null)); @@ -4344,7 +4432,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(IMPORTANCE_LOW, mService.getNotificationRecord(sbn.getKey()).getImportance()); - assertEquals(IMPORTANCE_UNSPECIFIED, mBinderService.getPackageImportance( + assertEquals(IMPORTANCE_DEFAULT, mBinderService.getPackageImportance( sbn.getPackageName())); nb = new Notification.Builder(mContext) @@ -4860,6 +4948,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Test public void testBackup() throws Exception { + mService.setPreferencesHelper(mPreferencesHelper); int systemChecks = mService.countSystemChecks; when(mListeners.queryPackageForServices(anyString(), anyInt(), anyInt())) .thenReturn(new ArraySet<>()); @@ -5963,8 +6052,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .thenReturn(false); // notifications from this package are blocked by the user - mService.setPreferencesHelper(mPreferencesHelper); - when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE); + when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); setAppInForegroundForToasts(mUid, true); @@ -6260,8 +6348,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .thenReturn(false); // notifications from this package are blocked by the user - mService.setPreferencesHelper(mPreferencesHelper); - when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE); + when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); setAppInForegroundForToasts(mUid, false); @@ -6347,8 +6434,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .thenReturn(true); // notifications from this package are NOT blocked by the user - mService.setPreferencesHelper(mPreferencesHelper); - when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_LOW); + when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); // enqueue toast -> no toasts enqueued ((INotificationManager) mService.mService).enqueueToast(testPackage, new Binder(), @@ -6369,8 +6455,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .thenReturn(false); // notifications from this package are blocked by the user - mService.setPreferencesHelper(mPreferencesHelper); - when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE); + when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); setAppInForegroundForToasts(mUid, false); @@ -6393,8 +6478,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .thenReturn(true); // notifications from this package ARE blocked by the user - mService.setPreferencesHelper(mPreferencesHelper); - when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE); + when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); setAppInForegroundForToasts(mUid, false); @@ -7303,6 +7387,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testAreNotificationsEnabledForPackage() throws Exception { + mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(), + mUid); + + verify(mPermissionHelper).hasPermission(mUid); + } + + @Test public void testAreNotificationsEnabledForPackage_crossUser() throws Exception { try { mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(), @@ -7311,21 +7403,31 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } catch (SecurityException e) { // pass } + verify(mPermissionHelper, never()).hasPermission(anyInt()); // cross user, with permission, no problem enableInteractAcrossUsers(); mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(), mUid + UserHandle.PER_USER_RANGE); - verify(mPermissionHelper, never()).hasPermission(anyInt()); + verify(mPermissionHelper).hasPermission(mUid + UserHandle.PER_USER_RANGE); } @Test - public void testAreNotificationsEnabledForPackage_viaInternalService() throws Exception { - assertEquals(mInternalService.areNotificationsEnabledForPackage( - mContext.getPackageName(), mUid), - mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(), mUid)); - verify(mPermissionHelper, never()).hasPermission(anyInt()); + public void testAreNotificationsEnabledForPackage_viaInternalService() { + mInternalService.areNotificationsEnabledForPackage(mContext.getPackageName(), mUid); + verify(mPermissionHelper).hasPermission(mUid); + } + + @Test + public void testGetPackageImportance() throws Exception { + when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); + assertThat(mBinderService.getPackageImportance(mContext.getPackageName())) + .isEqualTo(IMPORTANCE_DEFAULT); + + when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); + assertThat(mBinderService.getPackageImportance(mContext.getPackageName())) + .isEqualTo(IMPORTANCE_NONE); } @Test @@ -8975,48 +9077,13 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testMigrationDisabledByDefault() { - assertThat(mService.mEnableAppSettingMigration).isFalse(); - } - - @Test - public void testPostNotification_channelLockedFixed() throws Exception { - mTestNotificationChannel.setImportanceLockedByOEM(true); - - NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel); - mBinderService.enqueueNotificationWithTag(PKG, PKG, - "testPostNotification_appPermissionFixed", 0, - temp.getNotification(), 0); - waitForIdle(); - assertThat(mService.getNotificationRecordCount()).isEqualTo(1); - StatusBarNotification[] notifs = - mBinderService.getActiveNotifications(PKG); - assertThat(mService.getNotificationRecord(notifs[0].getKey()).isImportanceFixed()).isTrue(); - - mBinderService.cancelAllNotifications(PKG, 0); - waitForIdle(); - - mTestNotificationChannel.setImportanceLockedByOEM(false); - mTestNotificationChannel.setImportanceLockedByCriticalDeviceFunction(true); - - temp = generateNotificationRecord(mTestNotificationChannel); - mBinderService.enqueueNotificationWithTag(PKG, PKG, - "testPostNotification_appPermissionFixed", 0, - temp.getNotification(), 0); - waitForIdle(); - assertThat(mService.getNotificationRecordCount()).isEqualTo(1); - notifs = mBinderService.getActiveNotifications(PKG); - assertThat(mService.getNotificationRecord(notifs[0].getKey()).isImportanceFixed()).isTrue(); - } - - @Test public void testGetNotificationChannelsBypassingDnd_blocked() throws RemoteException { mService.setPreferencesHelper(mPreferencesHelper); - when(mPreferencesHelper.getImportance(PKG, mUid)).thenReturn(IMPORTANCE_NONE); + + when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); assertThat(mBinderService.getNotificationChannelsBypassingDnd(PKG, mUid).getList()) .isEmpty(); - verify(mPermissionHelper, never()).hasPermission(anyInt()); verify(mPreferencesHelper, never()).getNotificationChannelsBypassingDnd(PKG, mUid); } @@ -9110,8 +9177,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - mBinderService.setNotificationsEnabledForPackage( - r.getSbn().getPackageName(), r.getUid(), false); + when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); // normal blocked notifications - blocked assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), @@ -9149,6 +9215,67 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testMediaNotificationsBypassBlock_atPost() throws Exception { + when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false); + when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); + + Notification.Builder nb = new Notification.Builder( + mContext, mTestNotificationChannel.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .addAction(new Notification.Action.Builder(null, "test", null).build()); + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, + nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); + NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false); + + mService.addEnqueuedNotification(r); + NotificationManagerService.PostNotificationRunnable runnable = + mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), + r.getUid(), SystemClock.elapsedRealtime()); + runnable.run(); + waitForIdle(); + + verify(mUsageStats).registerBlocked(any()); + verify(mUsageStats, never()).registerPostedByApp(any()); + + // just using the style - blocked + mService.clearNotifications(); + reset(mUsageStats); + nb.setStyle(new Notification.MediaStyle()); + sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, + nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); + r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mService.addEnqueuedNotification(r); + runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), + r.getUid(), SystemClock.elapsedRealtime()); + runnable.run(); + waitForIdle(); + + verify(mUsageStats).registerBlocked(any()); + verify(mUsageStats, never()).registerPostedByApp(any()); + + // style + media session - bypasses block + mService.clearNotifications(); + reset(mUsageStats); + nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class))); + sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, + nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); + r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mService.addEnqueuedNotification(r); + runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), + r.getUid(), SystemClock.elapsedRealtime()); + runnable.run(); + waitForIdle(); + + verify(mUsageStats, never()).registerBlocked(any()); + verify(mUsageStats).registerPostedByApp(any()); + } + + @Test public void testCallNotificationsBypassBlock() throws Exception { when(mAmi.getPendingIntentFlags(any(IIntentSender.class))) .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT); @@ -9163,8 +9290,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - mBinderService.setNotificationsEnabledForPackage( - r.getSbn().getPackageName(), r.getUid(), false); + when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); // normal blocked notifications - blocked assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), @@ -9194,12 +9320,152 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { r.getSbn().getPackageName(), r.getUser())).thenReturn(true); assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue(); + + // set telecom manager to null - blocked + mService.setTelecomManager(null); + assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), + r.getSbn().getId(), r.getSbn().getTag(), r, false)) + .isFalse(); + + // set telecom feature to false - blocked + when(mPackageManagerClient.hasSystemFeature(FEATURE_TELECOM)).thenReturn(false); + assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), + r.getSbn().getId(), r.getSbn().getTag(), r, false)) + .isFalse(); + } + + @Test + public void testCallNotificationsBypassBlock_atPost() throws Exception { + when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false); + when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); + + Notification.Builder nb = + new Notification.Builder(mContext, mTestNotificationChannel.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .addAction(new Notification.Action.Builder(null, "test", null).build()); + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, + nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); + NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mBinderService.setNotificationsEnabledForPackage( + r.getSbn().getPackageName(), r.getUid(), false); + + // normal blocked notifications - blocked + mService.addEnqueuedNotification(r); + NotificationManagerService.PostNotificationRunnable runnable = + mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), + r.getUid(), SystemClock.elapsedRealtime()); + runnable.run(); + waitForIdle(); + + verify(mUsageStats).registerBlocked(any()); + verify(mUsageStats, never()).registerPostedByApp(any()); + + // just using the style - blocked + mService.clearNotifications(); + reset(mUsageStats); + Person person = new Person.Builder().setName("caller").build(); + nb.setStyle(Notification.CallStyle.forOngoingCall(person, mock(PendingIntent.class))); + nb.setFullScreenIntent(mock(PendingIntent.class), true); + sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, nb.build(), + UserHandle.getUserHandleForUid(mUid), null, 0); + r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mService.addEnqueuedNotification(r); + runnable = mService.new PostNotificationRunnable( + r.getKey(), r.getSbn().getPackageName(), r.getUid(), SystemClock.elapsedRealtime()); + runnable.run(); + waitForIdle(); + + verify(mUsageStats).registerBlocked(any()); + verify(mUsageStats, never()).registerPostedByApp(any()); + + // style + managed call - bypasses block + mService.clearNotifications(); + reset(mUsageStats); + when(mTelecomManager.isInManagedCall()).thenReturn(true); + + mService.addEnqueuedNotification(r); + runnable.run(); + waitForIdle(); + + verify(mUsageStats, never()).registerBlocked(any()); + verify(mUsageStats).registerPostedByApp(any()); + + // style + self managed call - bypasses block + mService.clearNotifications(); + reset(mUsageStats); + when(mTelecomManager.isInSelfManagedCall(r.getSbn().getPackageName(), r.getUser())) + .thenReturn(true); + + mService.addEnqueuedNotification(r); + runnable.run(); + waitForIdle(); + + verify(mUsageStats, never()).registerBlocked(any()); + verify(mUsageStats).registerPostedByApp(any()); + + // set telecom manager to null - notifications should be blocked + // but post notifications runnable should not crash + mService.clearNotifications(); + reset(mUsageStats); + mService.setTelecomManager(null); + + mService.addEnqueuedNotification(r); + runnable.run(); + waitForIdle(); + + verify(mUsageStats).registerBlocked(any()); + verify(mUsageStats, never()).registerPostedByApp(any()); + + // set FEATURE_TELECOM to false - notifications should be blocked + // but post notifications runnable should not crash + mService.setTelecomManager(mTelecomManager); + when(mPackageManagerClient.hasSystemFeature(FEATURE_TELECOM)).thenReturn(false); + reset(mUsageStats); + mService.setTelecomManager(null); + + mService.addEnqueuedNotification(r); + runnable.run(); + waitForIdle(); + + verify(mUsageStats).registerBlocked(any()); + verify(mUsageStats, never()).registerPostedByApp(any()); } @Test - public void testGetAllUsersNotificationPermissions_migrationNotEnabled() { - // make sure we don't bother if the migration is not enabled - assertThat(mService.getAllUsersNotificationPermissions()).isNull(); + public void testGetAllUsersNotificationPermissions() { + // In this case, there are multiple users each with notification permissions (and also, + // for good measure, some without). + // make sure the collection returned contains info for all of them + final List<UserInfo> userInfos = new ArrayList<>(); + userInfos.add(new UserInfo(0, "user0", 0)); + userInfos.add(new UserInfo(1, "user1", 0)); + userInfos.add(new UserInfo(2, "user2", 0)); + when(mUm.getUsers()).thenReturn(userInfos); + + // construct the permissions for each of them + ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> permissions0 = new ArrayMap<>(), + permissions1 = new ArrayMap<>(); + permissions0.put(new Pair<>(10, "package1"), new Pair<>(true, false)); + permissions0.put(new Pair<>(20, "package2"), new Pair<>(false, true)); + permissions1.put(new Pair<>(11, "package1"), new Pair<>(false, false)); + permissions1.put(new Pair<>(21, "package2"), new Pair<>(true, true)); + when(mPermissionHelper.getNotificationPermissionValues(0)).thenReturn(permissions0); + when(mPermissionHelper.getNotificationPermissionValues(1)).thenReturn(permissions1); + when(mPermissionHelper.getNotificationPermissionValues(2)).thenReturn(new ArrayMap<>()); + + ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> combinedPermissions = + mService.getAllUsersNotificationPermissions(); + assertTrue(combinedPermissions.get(new Pair<>(10, "package1")).first); + assertFalse(combinedPermissions.get(new Pair<>(10, "package1")).second); + assertFalse(combinedPermissions.get(new Pair<>(20, "package2")).first); + assertTrue(combinedPermissions.get(new Pair<>(20, "package2")).second); + assertFalse(combinedPermissions.get(new Pair<>(11, "package1")).first); + assertFalse(combinedPermissions.get(new Pair<>(11, "package1")).second); + assertTrue(combinedPermissions.get(new Pair<>(21, "package2")).first); + assertTrue(combinedPermissions.get(new Pair<>(21, "package2")).second); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java deleted file mode 100755 index b751c7fc73ea..000000000000 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java +++ /dev/null @@ -1,877 +0,0 @@ -/* - * Copyright (C) 2021 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.server.notification; - -import static android.app.AppOpsManager.MODE_ALLOWED; -import static android.app.AppOpsManager.MODE_IGNORED; -import static android.app.NotificationManager.EXTRA_BLOCKED_STATE; -import static android.app.NotificationManager.IMPORTANCE_DEFAULT; -import static android.app.NotificationManager.IMPORTANCE_NONE; -import static android.app.PendingIntent.FLAG_MUTABLE; -import static android.app.PendingIntent.FLAG_ONE_SHOT; -import static android.content.pm.PackageManager.FEATURE_WATCH; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.os.UserHandle.USER_SYSTEM; - -import static com.google.common.truth.Truth.assertThat; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertTrue; -import static junit.framework.Assert.fail; - -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.ActivityManager; -import android.app.ActivityManagerInternal; -import android.app.AlarmManager; -import android.app.AppOpsManager; -import android.app.IActivityManager; -import android.app.INotificationManager; -import android.app.IUriGrantsManager; -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.StatsManager; -import android.app.admin.DevicePolicyManagerInternal; -import android.app.usage.UsageStatsManagerInternal; -import android.companion.ICompanionDeviceManager; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.IIntentSender; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.ApplicationInfo; -import android.content.pm.IPackageManager; -import android.content.pm.LauncherApps; -import android.content.pm.PackageManager; -import android.content.pm.PackageManagerInternal; -import android.content.pm.ParceledListSlice; -import android.content.pm.ShortcutInfo; -import android.content.pm.ShortcutServiceInternal; -import android.content.pm.UserInfo; -import android.content.res.Resources; -import android.media.AudioManager; -import android.media.session.MediaSession; -import android.os.Binder; -import android.os.Build; -import android.os.Bundle; -import android.os.IBinder; -import android.os.Looper; -import android.os.Process; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.UserHandle; -import android.os.UserManager; -import android.provider.Settings; -import android.service.notification.NotificationListenerFilter; -import android.service.notification.StatusBarNotification; -import android.telecom.TelecomManager; -import android.telephony.TelephonyManager; -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableContext; -import android.testing.TestableLooper; -import android.testing.TestableLooper.RunWithLooper; -import android.testing.TestablePermissions; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.AtomicFile; -import android.util.Pair; - -import androidx.test.InstrumentationRegistry; - -import com.android.internal.app.IAppOpsService; -import com.android.internal.logging.InstanceIdSequence; -import com.android.internal.logging.InstanceIdSequenceFake; -import com.android.server.DeviceIdleInternal; -import com.android.server.LocalServices; -import com.android.server.SystemService; -import com.android.server.UiServiceTestCase; -import com.android.server.lights.LightsManager; -import com.android.server.lights.LogicalLight; -import com.android.server.notification.NotificationManagerService.NotificationAssistants; -import com.android.server.notification.NotificationManagerService.NotificationListeners; -import com.android.server.statusbar.StatusBarManagerInternal; -import com.android.server.uri.UriGrantsManagerInternal; -import com.android.server.utils.quota.MultiRateLimiter; -import com.android.server.wm.ActivityTaskManagerInternal; -import com.android.server.wm.WindowManagerInternal; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.stubbing.Answer; - -import java.io.File; -import java.io.FileOutputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -/** - * Tests that NMS reads/writes the app notification state from Package/PermissionManager when - * migration is enabled. Because the migration field is read onStart - * TODO (b/194833441): migrate these tests to NotificationManagerServiceTest when the migration is - * permanently enabled. - */ -public class NotificationPermissionMigrationTest extends UiServiceTestCase { - private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId"; - private static final int UID_HEADLESS = 1000000; - - private final int mUid = Binder.getCallingUid(); - private TestableNotificationManagerService mService; - private INotificationManager mBinderService; - private NotificationManagerInternal mInternalService; - private ShortcutHelper mShortcutHelper; - @Mock - private IPackageManager mPackageManager; - @Mock - private PackageManager mPackageManagerClient; - @Mock - private PackageManagerInternal mPackageManagerInternal; - @Mock - private WindowManagerInternal mWindowManagerInternal; - @Mock - private PermissionHelper mPermissionHelper; - private TestableContext mContext = spy(getContext()); - private final String PKG = mContext.getPackageName(); - private TestableLooper mTestableLooper; - @Mock - private RankingHelper mRankingHelper; - @Mock private PreferencesHelper mPreferencesHelper; - AtomicFile mPolicyFile; - File mFile; - @Mock - private NotificationUsageStats mUsageStats; - @Mock - private UsageStatsManagerInternal mAppUsageStats; - @Mock - private AudioManager mAudioManager; - @Mock - private LauncherApps mLauncherApps; - @Mock - private ShortcutServiceInternal mShortcutServiceInternal; - @Mock - private UserManager mUserManager; - @Mock - ActivityManager mActivityManager; - @Mock - Resources mResources; - @Mock - RankingHandler mRankingHandler; - @Mock - ActivityManagerInternal mAmi; - @Mock - private Looper mMainLooper; - - @Mock - IIntentSender pi1; - - private static final int MAX_POST_DELAY = 1000; - - private NotificationChannel mTestNotificationChannel = new NotificationChannel( - TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT); - - private static final String VALID_CONVO_SHORTCUT_ID = "shortcut"; - - @Mock - private NotificationListeners mListeners; - @Mock - private NotificationListenerFilter mNlf; - @Mock private NotificationAssistants mAssistants; - @Mock private ConditionProviders mConditionProviders; - private ManagedServices.ManagedServiceInfo mListener; - @Mock private ICompanionDeviceManager mCompanionMgr; - @Mock SnoozeHelper mSnoozeHelper; - @Mock GroupHelper mGroupHelper; - @Mock - IBinder mPermOwner; - @Mock - IActivityManager mAm; - @Mock - ActivityTaskManagerInternal mAtm; - @Mock - IUriGrantsManager mUgm; - @Mock - UriGrantsManagerInternal mUgmInternal; - @Mock - AppOpsManager mAppOpsManager; - @Mock - private TestableNotificationManagerService.NotificationAssistantAccessGrantedCallback - mNotificationAssistantAccessGrantedCallback; - @Mock - UserManager mUm; - @Mock - NotificationHistoryManager mHistoryManager; - @Mock - StatsManager mStatsManager; - @Mock - AlarmManager mAlarmManager; - @Mock - MultiRateLimiter mToastRateLimiter; - BroadcastReceiver mPackageIntentReceiver; - NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake(); - private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake( - 1 << 30); - @Mock - StatusBarManagerInternal mStatusBar; - - private NotificationManagerService.WorkerHandler mWorkerHandler; - - @Before - public void setUp() throws Exception { - // These should be the only difference in setup from NMSTest - Settings.Secure.putIntForUser( - getContext().getContentResolver(), - Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 1, USER_SYSTEM); - Settings.Global.putInt(getContext().getContentResolver(), - Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, 1); - - // Shell permissions will override permissions of our app, so add all necessary permissions - // for this test here: - InstrumentationRegistry.getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( - "android.permission.WRITE_DEVICE_CONFIG", - "android.permission.READ_DEVICE_CONFIG", - "android.permission.READ_CONTACTS"); - - MockitoAnnotations.initMocks(this); - - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); - - DeviceIdleInternal deviceIdleInternal = mock(DeviceIdleInternal.class); - when(deviceIdleInternal.getNotificationAllowlistDuration()).thenReturn(3000L); - - LocalServices.removeServiceForTest(UriGrantsManagerInternal.class); - LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal); - LocalServices.removeServiceForTest(WindowManagerInternal.class); - LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal); - LocalServices.removeServiceForTest(StatusBarManagerInternal.class); - LocalServices.addService(StatusBarManagerInternal.class, mStatusBar); - LocalServices.removeServiceForTest(DeviceIdleInternal.class); - LocalServices.addService(DeviceIdleInternal.class, deviceIdleInternal); - LocalServices.removeServiceForTest(ActivityManagerInternal.class); - LocalServices.addService(ActivityManagerInternal.class, mAmi); - LocalServices.removeServiceForTest(PackageManagerInternal.class); - LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal); - mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager); - when(mUm.getProfileIds(0, false)).thenReturn(new int[]{0}); - - doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any()); - - mService = new TestableNotificationManagerService(mContext, mNotificationRecordLogger, - mNotificationInstanceIdSequence); - - // Use this testable looper. - mTestableLooper = TestableLooper.get(this); - // MockPackageManager - default returns ApplicationInfo with matching calling UID - mContext.setMockPackageManager(mPackageManagerClient); - - when(mPackageManager.getApplicationInfo(anyString(), anyLong(), anyInt())) - .thenAnswer((Answer<ApplicationInfo>) invocation -> { - Object[] args = invocation.getArguments(); - return getApplicationInfo((String) args[0], mUid); - }); - when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) - .thenAnswer((Answer<ApplicationInfo>) invocation -> { - Object[] args = invocation.getArguments(); - return getApplicationInfo((String) args[0], mUid); - }); - when(mPackageManagerClient.getPackageUidAsUser(any(), anyInt())).thenReturn(mUid); - when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenAnswer( - (Answer<Boolean>) invocation -> { - Object[] args = invocation.getArguments(); - return (int) args[1] == mUid; - }); - final LightsManager mockLightsManager = mock(LightsManager.class); - when(mockLightsManager.getLight(anyInt())).thenReturn(mock(LogicalLight.class)); - when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); - when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false); - when(mUgmInternal.newUriPermissionOwner(anyString())).thenReturn(mPermOwner); - when(mPackageManager.getPackagesForUid(mUid)).thenReturn(new String[]{PKG}); - when(mPackageManagerClient.getPackagesForUid(anyInt())).thenReturn(new String[]{PKG}); - mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class)); - - // write to a test file; the system file isn't readable from tests - mFile = new File(mContext.getCacheDir(), "test.xml"); - mFile.createNewFile(); - final String preupgradeXml = "<notification-policy></notification-policy>"; - mPolicyFile = new AtomicFile(mFile); - FileOutputStream fos = mPolicyFile.startWrite(); - fos.write(preupgradeXml.getBytes()); - mPolicyFile.finishWrite(fos); - - // Setup managed services - when(mNlf.isTypeAllowed(anyInt())).thenReturn(true); - when(mNlf.isPackageAllowed(any())).thenReturn(true); - when(mNlf.isPackageAllowed(null)).thenReturn(true); - when(mListeners.getNotificationListenerFilter(any())).thenReturn(mNlf); - mListener = mListeners.new ManagedServiceInfo( - null, new ComponentName(PKG, "test_class"), - UserHandle.getUserId(mUid), true, null, 0, 123); - ComponentName defaultComponent = ComponentName.unflattenFromString("config/device"); - ArraySet<ComponentName> components = new ArraySet<>(); - components.add(defaultComponent); - when(mListeners.getDefaultComponents()).thenReturn(components); - when(mConditionProviders.getDefaultPackages()) - .thenReturn(new ArraySet<>(Arrays.asList("config"))); - when(mAssistants.getDefaultComponents()).thenReturn(components); - when(mAssistants.queryPackageForServices( - anyString(), anyInt(), anyInt())).thenReturn(components); - when(mListeners.checkServiceTokenLocked(null)).thenReturn(mListener); - ManagedServices.Config listenerConfig = new ManagedServices.Config(); - listenerConfig.xmlTag = NotificationListeners.TAG_ENABLED_NOTIFICATION_LISTENERS; - when(mListeners.getConfig()).thenReturn(listenerConfig); - ManagedServices.Config assistantConfig = new ManagedServices.Config(); - assistantConfig.xmlTag = NotificationAssistants.TAG_ENABLED_NOTIFICATION_ASSISTANTS; - when(mAssistants.getConfig()).thenReturn(assistantConfig); - ManagedServices.Config dndConfig = new ManagedServices.Config(); - dndConfig.xmlTag = ConditionProviders.TAG_ENABLED_DND_APPS; - when(mConditionProviders.getConfig()).thenReturn(dndConfig); - - when(mAssistants.isAdjustmentAllowed(anyString())).thenReturn(true); - - // apps allowed as convos - mService.setStringArrayResourceValue(PKG_O); - - mWorkerHandler = spy(mService.new WorkerHandler(mTestableLooper.getLooper())); - mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient, - mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr, - mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, mGroupHelper, mAm, mAtm, - mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, - mAppOpsManager, mock(IAppOpsService.class), mUm, mHistoryManager, mStatsManager, - mock(TelephonyManager.class), mAmi, mToastRateLimiter, mPermissionHelper, - mock(UsageStatsManagerInternal.class), mock(TelecomManager.class)); - // Return first true for RoleObserver main-thread check - when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false); - mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper); - - mService.setAudioManager(mAudioManager); - - mShortcutHelper = mService.getShortcutHelper(); - mShortcutHelper.setLauncherApps(mLauncherApps); - mShortcutHelper.setShortcutServiceInternal(mShortcutServiceInternal); - mShortcutHelper.setUserManager(mUserManager); - - // Capture PackageIntentReceiver - ArgumentCaptor<BroadcastReceiver> broadcastReceiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); - ArgumentCaptor<IntentFilter> intentFilterCaptor = - ArgumentCaptor.forClass(IntentFilter.class); - - verify(mContext, atLeastOnce()).registerReceiverAsUser(broadcastReceiverCaptor.capture(), - any(), intentFilterCaptor.capture(), any(), any()); - verify(mContext, atLeastOnce()).registerReceiver(broadcastReceiverCaptor.capture(), - intentFilterCaptor.capture()); - List<BroadcastReceiver> broadcastReceivers = broadcastReceiverCaptor.getAllValues(); - List<IntentFilter> intentFilters = intentFilterCaptor.getAllValues(); - - for (int i = 0; i < intentFilters.size(); i++) { - final IntentFilter filter = intentFilters.get(i); - if (filter.hasAction(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED) - && filter.hasAction(Intent.ACTION_PACKAGES_UNSUSPENDED) - && filter.hasAction(Intent.ACTION_PACKAGES_SUSPENDED)) { - mPackageIntentReceiver = broadcastReceivers.get(i); - } - } - assertNotNull("package intent receiver should exist", mPackageIntentReceiver); - - // Pretend the shortcut exists - List<ShortcutInfo> shortcutInfos = new ArrayList<>(); - ShortcutInfo info = mock(ShortcutInfo.class); - when(info.getPackage()).thenReturn(PKG); - when(info.getId()).thenReturn(VALID_CONVO_SHORTCUT_ID); - when(info.getUserId()).thenReturn(USER_SYSTEM); - when(info.isLongLived()).thenReturn(true); - when(info.isEnabled()).thenReturn(true); - shortcutInfos.add(info); - when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcutInfos); - when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(), - anyString(), anyInt(), any())).thenReturn(true); - when(mUserManager.isUserUnlocked(any(UserHandle.class))).thenReturn(true); - - // Set the testable bubble extractor - RankingHelper rankingHelper = mService.getRankingHelper(); - BubbleExtractor extractor = rankingHelper.findExtractor(BubbleExtractor.class); - extractor.setActivityManager(mActivityManager); - - // Tests call directly into the Binder. - mBinderService = mService.getBinderService(); - mInternalService = mService.getInternalService(); - - mBinderService.createNotificationChannels( - PKG, new ParceledListSlice(Arrays.asList(mTestNotificationChannel))); - mBinderService.createNotificationChannels( - PKG_P, new ParceledListSlice(Arrays.asList(mTestNotificationChannel))); - mBinderService.createNotificationChannels( - PKG_O, new ParceledListSlice(Arrays.asList(mTestNotificationChannel))); - assertNotNull(mBinderService.getNotificationChannel( - PKG, mContext.getUserId(), PKG, TEST_CHANNEL_ID)); - clearInvocations(mRankingHandler); - } - - @After - public void tearDown() throws Exception { - if (mFile != null) mFile.delete(); - - try { - mService.onDestroy(); - } catch (IllegalStateException | IllegalArgumentException e) { - // can throw if a broadcast receiver was never registered - } - - InstrumentationRegistry.getInstrumentation() - .getUiAutomation().dropShellPermissionIdentity(); - // Remove scheduled messages that would be processed when the test is already done, and - // could cause issues, for example, messages that remove/cancel shown toasts (this causes - // problematic interactions with mocks when they're no longer working as expected). - mWorkerHandler.removeCallbacksAndMessages(null); - } - - private ApplicationInfo getApplicationInfo(String pkg, int uid) { - final ApplicationInfo applicationInfo = new ApplicationInfo(); - applicationInfo.uid = uid; - switch (pkg) { - case PKG_N_MR1: - applicationInfo.targetSdkVersion = Build.VERSION_CODES.N_MR1; - break; - case PKG_O: - applicationInfo.targetSdkVersion = Build.VERSION_CODES.O; - break; - case PKG_P: - applicationInfo.targetSdkVersion = Build.VERSION_CODES.P; - break; - default: - applicationInfo.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; - break; - } - return applicationInfo; - } - - public void waitForIdle() { - mTestableLooper.processAllMessages(); - } - - private NotificationRecord generateNotificationRecord(NotificationChannel channel) { - return generateNotificationRecord(channel, null); - } - - private NotificationRecord generateNotificationRecord(NotificationChannel channel, - Notification.TvExtender extender) { - if (channel == null) { - channel = mTestNotificationChannel; - } - Notification.Builder nb = new Notification.Builder(mContext, channel.getId()) - .setContentTitle("foo") - .setSmallIcon(android.R.drawable.sym_def_app_icon) - .addAction(new Notification.Action.Builder(null, "test", null).build()); - if (extender != null) { - nb.extend(extender); - } - StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - return new NotificationRecord(mContext, sbn, channel); - } - - private void enableInteractAcrossUsers() { - TestablePermissions perms = mContext.getTestablePermissions(); - perms.setPermission(android.Manifest.permission.INTERACT_ACROSS_USERS, PERMISSION_GRANTED); - } - - @Test - public void testAreNotificationsEnabledForPackage() throws Exception { - mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(), - mUid); - - verify(mPermissionHelper).hasPermission(mUid); - } - - @Test - public void testAreNotificationsEnabledForPackage_crossUser() throws Exception { - try { - mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(), - mUid + UserHandle.PER_USER_RANGE); - fail("Cannot call cross user without permission"); - } catch (SecurityException e) { - // pass - } - verify(mPermissionHelper, never()).hasPermission(anyInt()); - - // cross user, with permission, no problem - enableInteractAcrossUsers(); - mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(), - mUid + UserHandle.PER_USER_RANGE); - - verify(mPermissionHelper).hasPermission(mUid + UserHandle.PER_USER_RANGE); - } - - @Test - public void testAreNotificationsEnabledForPackage_viaInternalService() { - mInternalService.areNotificationsEnabledForPackage(mContext.getPackageName(), mUid); - verify(mPermissionHelper).hasPermission(mUid); - } - - @Test - public void testGetPackageImportance() throws Exception { - when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); - assertThat(mBinderService.getPackageImportance(mContext.getPackageName())) - .isEqualTo(IMPORTANCE_DEFAULT); - - when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); - assertThat(mBinderService.getPackageImportance(mContext.getPackageName())) - .isEqualTo(IMPORTANCE_NONE); - } - - @Test - public void testEnqueueNotificationInternal_noChannel() throws Exception { - when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); - NotificationRecord nr = generateNotificationRecord( - new NotificationChannel("did not create", "", IMPORTANCE_DEFAULT)); - - mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(), - nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); - waitForIdle(); - - verify(mPermissionHelper).hasPermission(mUid); - verify(mPermissionHelper, never()).hasPermission(Process.SYSTEM_UID); - - reset(mPermissionHelper); - when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); - - mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.getSbn().getTag(), - nr.getSbn().getId(), nr.getSbn().getNotification(), nr.getSbn().getUserId()); - waitForIdle(); - - verify(mPermissionHelper).hasPermission(mUid); - assertThat(mService.mChannelToastsSent).contains(mUid); - } - - @Test - public void testSetNotificationsEnabledForPackage_noChange() throws Exception { - when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); - mBinderService.setNotificationsEnabledForPackage(mContext.getPackageName(), mUid, true); - - verify(mPermissionHelper, never()).setNotificationPermission( - anyString(), anyInt(), anyBoolean(), anyBoolean()); - } - - @Test - public void testSetNotificationsEnabledForPackage() throws Exception { - when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); - mBinderService.setNotificationsEnabledForPackage(mContext.getPackageName(), mUid, false); - - verify(mPermissionHelper).setNotificationPermission( - mContext.getPackageName(), UserHandle.getUserId(mUid), false, true); - - verify(mAppOpsManager, never()).setMode(anyInt(), anyInt(), anyString(), anyInt()); - } - - @Test - public void testUpdateAppNotifyCreatorBlock() throws Exception { - when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); - - mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false); - Thread.sleep(500); - waitForIdle(); - - ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); - verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null)); - - assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED, - captor.getValue().getAction()); - assertEquals(PKG, captor.getValue().getPackage()); - assertTrue(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true)); - } - - @Test - public void testUpdateAppNotifyCreatorUnblock() throws Exception { - when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); - - mBinderService.setNotificationsEnabledForPackage(PKG, mUid, true); - Thread.sleep(500); - waitForIdle(); - - ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); - verify(mContext, times(1)).sendBroadcastAsUser(captor.capture(), any(), eq(null)); - - assertEquals(NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED, - captor.getValue().getAction()); - assertEquals(PKG, captor.getValue().getPackage()); - assertFalse(captor.getValue().getBooleanExtra(EXTRA_BLOCKED_STATE, true)); - } - - @Test - public void testGetNotificationChannelsBypassingDnd_blocked() throws RemoteException { - mService.setPreferencesHelper(mPreferencesHelper); - - when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); - - assertThat(mBinderService.getNotificationChannelsBypassingDnd(PKG, mUid).getList()) - .isEmpty(); - verify(mPreferencesHelper, never()).getImportance(anyString(), anyInt()); - verify(mPreferencesHelper, never()).getNotificationChannelsBypassingDnd(PKG, mUid); - } - - @Test - public void testBlockedNotifications_blockedByUser() throws Exception { - when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false); - when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); - - NotificationChannel channel = new NotificationChannel("id", "name", - NotificationManager.IMPORTANCE_HIGH); - NotificationRecord r = generateNotificationRecord(channel); - mService.addEnqueuedNotification(r); - - when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false); - - NotificationManagerService.PostNotificationRunnable runnable = - mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), SystemClock.elapsedRealtime()); - runnable.run(); - waitForIdle(); - - verify(mUsageStats).registerBlocked(any()); - verify(mUsageStats, never()).registerPostedByApp(any()); - } - - @Test - public void testEnqueueNotification_appBlocked() throws Exception { - when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); - - mBinderService.enqueueNotificationWithTag(PKG, PKG, - "testEnqueueNotification_appBlocked", 0, - generateNotificationRecord(null).getNotification(), 0); - waitForIdle(); - verify(mWorkerHandler, never()).post( - any(NotificationManagerService.EnqueueNotificationRunnable.class)); - } - - @Test - public void testDefaultChannelDoesNotUpdateApp_postMigrationToPermissions() throws Exception { - final NotificationChannel defaultChannel = mBinderService.getNotificationChannel( - PKG_N_MR1, ActivityManager.getCurrentUser(), PKG_N_MR1, - NotificationChannel.DEFAULT_CHANNEL_ID); - defaultChannel.setImportance(IMPORTANCE_NONE); - - mBinderService.updateNotificationChannelForPackage(PKG_N_MR1, mUid, defaultChannel); - - verify(mPermissionHelper).setNotificationPermission( - PKG_N_MR1, ActivityManager.getCurrentUser(), false, true); - } - - @Test - public void testPostNotification_appPermissionFixed() throws Exception { - when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); - when(mPermissionHelper.isPermissionFixed(PKG, 0)).thenReturn(true); - - NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel); - mBinderService.enqueueNotificationWithTag(PKG, PKG, - "testPostNotification_appPermissionFixed", 0, - temp.getNotification(), 0); - waitForIdle(); - assertThat(mService.getNotificationRecordCount()).isEqualTo(1); - StatusBarNotification[] notifs = - mBinderService.getActiveNotifications(PKG); - assertThat(mService.getNotificationRecord(notifs[0].getKey()).isImportanceFixed()).isTrue(); - } - - @Test - public void testSummaryNotification_appPermissionFixed() { - NotificationRecord temp = generateNotificationRecord(mTestNotificationChannel); - mService.addNotification(temp); - - when(mPermissionHelper.hasPermission(mUid)).thenReturn(true); - when(mPermissionHelper.isPermissionFixed(PKG, temp.getUserId())).thenReturn(true); - - NotificationRecord r = mService.createAutoGroupSummary( - temp.getUserId(), temp.getSbn().getPackageName(), temp.getKey(), false); - - assertThat(r.isImportanceFixed()).isTrue(); - } - - @Test - public void testMediaNotificationsBypassBlock() throws Exception { - when(mAmi.getPendingIntentFlags(any(IIntentSender.class))) - .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT); - when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); - - Notification.Builder nb = new Notification.Builder( - mContext, mTestNotificationChannel.getId()) - .setContentTitle("foo") - .setSmallIcon(android.R.drawable.sym_def_app_icon) - .addAction(new Notification.Action.Builder(null, "test", null).build()); - StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - - when(mPermissionHelper.hasPermission(mUid)).thenReturn(false); - - // normal blocked notifications - blocked - assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), - r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse(); - - // just using the style - blocked - nb.setStyle(new Notification.MediaStyle()); - sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - - assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), - r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse(); - - // using the style, but incorrect type in session - blocked - nb.setStyle(new Notification.MediaStyle()); - Bundle extras = new Bundle(); - extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, new Intent()); - nb.addExtras(extras); - sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - - assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), - r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse(); - - // style + media session - bypasses block - nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class))); - sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - - assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), - r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue(); - } - - @Test - public void testMediaNotificationsBypassBlock_atPost() throws Exception { - when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false); - when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); - - Notification.Builder nb = new Notification.Builder( - mContext, mTestNotificationChannel.getId()) - .setContentTitle("foo") - .setSmallIcon(android.R.drawable.sym_def_app_icon) - .addAction(new Notification.Action.Builder(null, "test", null).build()); - StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - - when(mPermissionHelper.hasPermission(anyInt())).thenReturn(false); - - mService.addEnqueuedNotification(r); - NotificationManagerService.PostNotificationRunnable runnable = - mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), SystemClock.elapsedRealtime()); - runnable.run(); - waitForIdle(); - - verify(mUsageStats).registerBlocked(any()); - verify(mUsageStats, never()).registerPostedByApp(any()); - - // just using the style - blocked - mService.clearNotifications(); - reset(mUsageStats); - nb.setStyle(new Notification.MediaStyle()); - sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - - mService.addEnqueuedNotification(r); - runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), SystemClock.elapsedRealtime()); - runnable.run(); - waitForIdle(); - - verify(mUsageStats).registerBlocked(any()); - verify(mUsageStats, never()).registerPostedByApp(any()); - - // style + media session - bypasses block - mService.clearNotifications(); - reset(mUsageStats); - nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class))); - sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, - nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); - r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); - - mService.addEnqueuedNotification(r); - runnable = mService.new PostNotificationRunnable(r.getKey(), r.getSbn().getPackageName(), - r.getUid(), SystemClock.elapsedRealtime()); - runnable.run(); - waitForIdle(); - - verify(mUsageStats, never()).registerBlocked(any()); - verify(mUsageStats).registerPostedByApp(any()); - } - - @Test - public void testGetAllUsersNotificationPermissions() { - // In this case, there are multiple users each with notification permissions (and also, - // for good measure, some without). - // make sure the collection returned contains info for all of them - final List<UserInfo> userInfos = new ArrayList<>(); - userInfos.add(new UserInfo(0, "user0", 0)); - userInfos.add(new UserInfo(1, "user1", 0)); - userInfos.add(new UserInfo(2, "user2", 0)); - when(mUm.getUsers()).thenReturn(userInfos); - - // construct the permissions for each of them - ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> permissions0 = new ArrayMap<>(), - permissions1 = new ArrayMap<>(); - permissions0.put(new Pair<>(10, "package1"), new Pair<>(true, false)); - permissions0.put(new Pair<>(20, "package2"), new Pair<>(false, true)); - permissions1.put(new Pair<>(11, "package1"), new Pair<>(false, false)); - permissions1.put(new Pair<>(21, "package2"), new Pair<>(true, true)); - when(mPermissionHelper.getNotificationPermissionValues(0)).thenReturn(permissions0); - when(mPermissionHelper.getNotificationPermissionValues(1)).thenReturn(permissions1); - when(mPermissionHelper.getNotificationPermissionValues(2)).thenReturn(new ArrayMap<>()); - - ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> combinedPermissions = - mService.getAllUsersNotificationPermissions(); - assertTrue(combinedPermissions.get(new Pair<>(10, "package1")).first); - assertFalse(combinedPermissions.get(new Pair<>(10, "package1")).second); - assertFalse(combinedPermissions.get(new Pair<>(20, "package2")).first); - assertTrue(combinedPermissions.get(new Pair<>(20, "package2")).second); - assertFalse(combinedPermissions.get(new Pair<>(11, "package1")).first); - assertFalse(combinedPermissions.get(new Pair<>(11, "package1")).second); - assertTrue(combinedPermissions.get(new Pair<>(21, "package2")).first); - assertTrue(combinedPermissions.get(new Pair<>(21, "package2")).second); - } -} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java index d89141cc1000..5468220d9564 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java @@ -1065,9 +1065,8 @@ public class NotificationRecordTest extends UiServiceTestCase { } @Test - public void testApplyImportanceAdjustmentsForNonOemDefaultAppLockedChannels() { + public void testApplyImportanceAdjustments() { NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_DEFAULT); - channel.setImportanceLockedByOEM(false); StatusBarNotification sbn = getNotification(PKG_O, true /* noisy */, true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */, diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java index 46b47f4dcfdd..3a352cbe1900 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java @@ -88,51 +88,13 @@ public class PermissionHelperTest extends UiServiceTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager, true, false); + mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager, false); PackageInfo testPkgInfo = new PackageInfo(); testPkgInfo.requestedPermissions = new String[]{ Manifest.permission.POST_NOTIFICATIONS }; when(mPackageManager.getPackageInfo(anyString(), anyLong(), anyInt())) .thenReturn(testPkgInfo); } - // TODO (b/194833441): Remove when the migration is enabled - @Test - public void testMethodsThrowIfMigrationDisabled() throws IllegalAccessException, - InvocationTargetException { - PermissionHelper permHelper = - new PermissionHelper(mPmi, mPackageManager, mPermManager, false, false); - - Method[] allMethods = PermissionHelper.class.getDeclaredMethods(); - for (Method method : allMethods) { - if (Modifier.isPublic(method.getModifiers()) && - !Objects.equals("isMigrationEnabled", method.getName())) { - Parameter[] params = method.getParameters(); - List<Object> args = Lists.newArrayListWithCapacity(params.length); - for (int i = 0; i < params.length; i++) { - Type type = params[i].getParameterizedType(); - if (type.getTypeName().equals("java.lang.String")) { - args.add(""); - } else if (type.getTypeName().equals("boolean")){ - args.add(false); - } else if (type.getTypeName().equals("int")) { - args.add(1); - } else if (type.getTypeName().equals( - "com.android.server.notification.PermissionHelper$PackagePermission")) { - args.add(null); - } - } - try { - method.invoke(permHelper, args.toArray()); - fail("Method should have thrown because migration flag is disabled"); - } catch (InvocationTargetException e) { - if (!(e.getTargetException() instanceof IllegalStateException)) { - throw e; - } - } - } - } - } - @Test public void testHasPermission() throws Exception { when(mPmi.checkPostNotificationsPermissionGrantedOrLegacyAccess(anyInt())) @@ -304,7 +266,7 @@ public class PermissionHelperTest extends UiServiceTestCase { @Test public void testSetNotificationPermission_pkgPerm_grantedByDefaultPermSet_allUserSet() throws Exception { - mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager, true, true); + mPermissionHelper = new PermissionHelper(mPmi, mPackageManager, mPermManager, true); when(mPmi.checkPermission(anyString(), anyString(), anyInt())) .thenReturn(PERMISSION_DENIED); when(mPermManager.getPermissionFlags(anyString(), diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index 6d0895935877..a5cec7e01e9a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -278,6 +278,14 @@ public class PreferencesHelperTest extends UiServiceTestCase { when(mAppOpsManager.noteOpNoThrow(anyInt(), anyInt(), anyString(), eq(null), anyString())).thenReturn(MODE_DEFAULT); + ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); + appPermissions.put(new Pair(UID_P, PKG_P), new Pair(true, false)); + appPermissions.put(new Pair(UID_O, PKG_O), new Pair(true, false)); + appPermissions.put(new Pair(UID_N_MR1, PKG_N_MR1), new Pair(true, false)); + + when(mPermissionHelper.getNotificationPermissionValues(USER_SYSTEM)) + .thenReturn(appPermissions); + mStatsEventBuilderFactory = new WrappedSysUiStatsEvent.WrappedBuilderFactory(); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, @@ -408,6 +416,13 @@ public class PreferencesHelperTest extends UiServiceTestCase { NotificationChannel channel10 = new NotificationChannel("id10", "name10", IMPORTANCE_HIGH); assertTrue(mHelper.createNotificationChannel(package10, uid10, channel10, true, false)); + ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); + appPermissions.put(new Pair(uid0, package0), new Pair(false, false)); + appPermissions.put(new Pair(uid10, package10), new Pair(true, false)); + + when(mPermissionHelper.getNotificationPermissionValues(10)) + .thenReturn(appPermissions); + ByteArrayOutputStream baos = writeXmlAndPurge(package10, uid10, true, 10); // Reset state. @@ -433,6 +448,12 @@ public class PreferencesHelperTest extends UiServiceTestCase { NotificationChannel channel0 = new NotificationChannel("id0", "name0", IMPORTANCE_HIGH); assertTrue(mHelper.createNotificationChannel(package0, uid0, channel0, true, false)); + ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); + appPermissions.put(new Pair(uid0, package0), new Pair(true, false)); + + when(mPermissionHelper.getNotificationPermissionValues(USER_SYSTEM)) + .thenReturn(appPermissions); + ByteArrayOutputStream baos = writeXmlAndPurge(package0, uid0, true, 0); // Reset state. @@ -478,7 +499,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertTrue(mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel2, false, false)); mHelper.setShowBadge(PKG_N_MR1, UID_N_MR1, true); - mHelper.setAppImportanceLocked(PKG_N_MR1, UID_N_MR1); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, false, UserHandle.USER_ALL, channel1.getId(), channel2.getId(), @@ -489,7 +509,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { loadStreamXml(baos, false, UserHandle.USER_ALL); assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1)); - assertTrue(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1)); assertEquals(channel1, mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1.getId(), false)); compareChannels(channel2, @@ -550,8 +569,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.setInvalidMsgAppDemoted(PKG_P, UID_P, true); mHelper.setValidBubbleSent(PKG_P, UID_P); - mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_NONE); - ByteArrayOutputStream baos = writeXmlAndPurge(PKG_N_MR1, UID_N_MR1, true, USER_SYSTEM, channel1.getId(), channel2.getId(), channel3.getId(), NotificationChannel.DEFAULT_CHANNEL_ID); @@ -562,7 +579,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { loadStreamXml(baos, true, USER_SYSTEM); - assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_O, UID_O)); assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1)); assertTrue(mHelper.hasSentInvalidMsg(PKG_P, UID_P)); assertFalse(mHelper.hasSentInvalidMsg(PKG_N_MR1, UID_N_MR1)); @@ -601,7 +617,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_oldXml_migrates() throws Exception { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); @@ -672,7 +687,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_oldXml_backup_migratesWhenPkgInstalled() throws Exception { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); @@ -751,7 +765,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_newXml_noMigration_showPermissionNotification() throws Exception { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); @@ -809,7 +822,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_newXml_noMigration_noPermissionNotification() throws Exception { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); @@ -866,7 +878,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_oldXml_migration_NoUid() throws Exception { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); @@ -900,7 +911,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testReadXml_newXml_noMigration_NoUid() throws Exception { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); @@ -933,7 +943,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testChannelXmlForNonBackup_postMigration() throws Exception { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); @@ -1014,7 +1023,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testChannelXmlForBackup_postMigration() throws Exception { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); @@ -1101,7 +1109,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testChannelXmlForBackup_postMigration_noExternal() throws Exception { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); @@ -1181,7 +1188,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testChannelXmlForBackup_postMigration_noLocalSettings() throws Exception { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); @@ -1303,6 +1309,12 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testBackupRestoreXml_withNullSoundUri() throws Exception { + ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); + appPermissions.put(new Pair(UID_N_MR1, PKG_N_MR1), new Pair(true, false)); + + when(mPermissionHelper.getNotificationPermissionValues(USER_SYSTEM)) + .thenReturn(appPermissions); + NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_LOW); channel.setSound(null, mAudioAttributes); @@ -1472,14 +1484,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testCreateChannel_blocked() throws Exception { - mHelper.setImportance(PKG_N_MR1, UID_N_MR1, IMPORTANCE_NONE); - - assertTrue(mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, - new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false)); - } - - @Test public void testCreateChannel_badImportance() throws Exception { try { mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, @@ -1543,12 +1547,10 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testUpdate_preUpgrade_updatesAppFields() throws Exception { - mHelper.setImportance(PKG_N_MR1, UID_N_MR1, IMPORTANCE_UNSPECIFIED); assertTrue(mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1)); assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1)); assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE, mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1)); - assertFalse(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1)); NotificationChannel defaultChannel = mHelper.getNotificationChannel( PKG_N_MR1, UID_N_MR1, NotificationChannel.DEFAULT_CHANNEL_ID, false); @@ -1566,8 +1568,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertEquals(Notification.PRIORITY_MAX, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1)); assertEquals(Notification.VISIBILITY_SECRET, mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1)); - assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_N_MR1, UID_N_MR1)); - assertTrue(mHelper.getIsAppImportanceLocked(PKG_N_MR1, UID_N_MR1)); } @Test @@ -1592,9 +1592,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_O, UID_O)); assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE, mHelper.getPackageVisibility(PKG_O, UID_O)); - assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_O, - UID_O)); - assertFalse(mHelper.getIsAppImportanceLocked(PKG_O, UID_O)); } @Test @@ -1629,8 +1626,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG_N_MR1, UID_N_MR1)); assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE, mHelper.getPackageVisibility(PKG_N_MR1, UID_N_MR1)); - assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1, - UID_N_MR1)); } @Test @@ -2015,8 +2010,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testCreateAndDeleteCanChannelsBypassDnd_localSettings() throws Exception { + public void testCreateAndDeleteCanChannelsBypassDnd_localSettings() { int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1; + when(mPermissionHelper.hasPermission(uid)).thenReturn(true); // create notification channel that can't bypass dnd // expected result: areChannelsBypassingDnd = false @@ -2029,7 +2025,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { // create notification channel that can bypass dnd // expected result: areChannelsBypassingDnd = true - assertTrue(mHelper.getImportance(PKG_N_MR1, uid) != IMPORTANCE_NONE); NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW); channel2.setBypassDnd(true); mHelper.createNotificationChannel(PKG_N_MR1, uid, channel2, true, true); @@ -2052,8 +2047,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testCreateAndUpdateChannelsBypassingDnd_permissionHelper() { int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1; - - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); when(mPermissionHelper.hasPermission(uid)).thenReturn(true); // create notification channel that can't bypass dnd @@ -2076,10 +2069,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testCreateAndDeleteCanChannelsBypassDnd_permissionHelper() throws Exception { + public void testCreateAndDeleteCanChannelsBypassDnd_permissionHelper() { int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1; - - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); when(mPermissionHelper.hasPermission(uid)).thenReturn(true); // create notification channel that can't bypass dnd @@ -2113,8 +2104,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testBlockedGroupDoesNotBypassDnd() throws Exception { + public void testBlockedGroupDoesNotBypassDnd() { int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1; + when(mPermissionHelper.hasPermission(uid)).thenReturn(true); // start in a 'allowed to bypass dnd state' mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, @@ -2140,8 +2132,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testBlockedAppsDoNotBypassDnd_localSettings() throws Exception { + public void testBlockedAppsDoNotBypassDnd_localSettings() { int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1; + when(mPermissionHelper.hasPermission(uid)).thenReturn(false); // start in a 'allowed to bypass dnd state' mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, @@ -2151,7 +2144,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory); - mHelper.setImportance(PKG_N_MR1, uid, IMPORTANCE_NONE); // create notification channel that can bypass dnd, but app is blocked // expected result: areChannelsBypassingDnd = false NotificationChannel channel2 = new NotificationChannel("id2", "name2", IMPORTANCE_LOW); @@ -2163,10 +2155,10 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testBlockedAppsDoNotBypassDnd_permissionHelper() throws Exception { + public void testBlockedAppsDoNotBypassDnd_permissionHelper() { int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1; - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); when(mPermissionHelper.hasPermission(uid)).thenReturn(false); + // start in a 'allowed to bypass dnd state' mTestNotificationPolicy = new NotificationManager.Policy(0, 0, 0, 0, NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0); @@ -2186,8 +2178,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testUpdateCanChannelsBypassDnd() throws Exception { + public void testUpdateCanChannelsBypassDnd() { int uid = UserManager.isHeadlessSystemUserMode() ? UID_HEADLESS : UID_N_MR1; + when(mPermissionHelper.hasPermission(uid)).thenReturn(true); // create notification channel that can't bypass dnd // expected result: areChannelsBypassingDnd = false @@ -2405,8 +2398,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { when(mPm.getApplicationInfoAsUser(eq(PKG_N_MR1), anyInt(), anyInt())).thenReturn(legacy); // create records with the default channel for all user 0 and user 1 uids - mHelper.getImportance(PKG_N_MR1, user0Uids[i]); - mHelper.getImportance(PKG_N_MR1, user1Uids[i]); + mHelper.canShowBadge(PKG_N_MR1, user0Uids[i]); + mHelper.canShowBadge(PKG_N_MR1, user1Uids[i]); } mHelper.onUserRemoved(1); @@ -2445,17 +2438,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testOnPackageChanged_packageRemoval_importance() throws Exception { - mHelper.setImportance(PKG_N_MR1, UID_N_MR1, NotificationManager.IMPORTANCE_HIGH); - - mHelper.onPackagesChanged(true, USER_SYSTEM, new String[]{PKG_N_MR1}, new int[]{ - UID_N_MR1}); - - assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1, - UID_N_MR1)); - } - - @Test public void testOnPackageChanged_packageRemoval_groups() throws Exception { NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1"); mHelper.createNotificationChannelGroup(PKG_N_MR1, UID_N_MR1, ncg, true); @@ -2496,17 +2478,14 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false); mHelper.createNotificationChannelGroup( PKG_O, UID_O, new NotificationChannelGroup("1", "bye"), true); - mHelper.lockChannelsForOEM(pkg.toArray(new String[]{})); mHelper.updateDefaultApps(UserHandle.getUserId(UID_O), null, pkgPair); mHelper.setNotificationDelegate(PKG_O, UID_O, "", 1); - mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_NONE); mHelper.setBubblesAllowed(PKG_O, UID_O, DEFAULT_BUBBLE_PREFERENCE); mHelper.setShowBadge(PKG_O, UID_O, false); mHelper.setAppImportanceLocked(PKG_O, UID_O); mHelper.clearData(PKG_O, UID_O); - assertEquals(IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_O, UID_O)); assertEquals(mHelper.getBubblePreference(PKG_O, UID_O), DEFAULT_BUBBLE_PREFERENCE); assertTrue(mHelper.canShowBadge(PKG_O, UID_O)); assertNull(mHelper.getNotificationDelegate(PKG_O, UID_O)); @@ -2518,13 +2497,10 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.createNotificationChannel(PKG_O, UID_O, channel, true, false); assertTrue(channel.isImportanceLockedByCriticalDeviceFunction()); - assertTrue(channel.isImportanceLockedByOEM()); } @Test public void testRecordDefaults() throws Exception { - assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1, - UID_N_MR1)); assertEquals(true, mHelper.canShowBadge(PKG_N_MR1, UID_N_MR1)); assertEquals(1, mHelper.getNotificationChannels(PKG_N_MR1, UID_N_MR1, false).getList().size()); } @@ -2760,69 +2736,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testDumpJson_prePermissionMigration() throws Exception { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(false); - // before the migration is active, we want to verify that: - // - all notification importance info should come from package preferences - // - if there are permissions granted or denied from packages PreferencesHelper doesn't - // know about, those are ignored if migration is not enabled - - // package permissions map to be passed in - ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); - appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs - appPermissions.put(new Pair(3, "third"), new Pair(false, false)); // not in local prefs - appPermissions.put(new Pair(UID_P, PKG_P), new Pair(true, false)); // in local prefs - appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs - - NotificationChannel channel1 = - new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH); - NotificationChannel channel3 = new NotificationChannel("id3", "name3", IMPORTANCE_HIGH); - - mHelper.createNotificationChannel(PKG_P, UID_P, channel1, true, false); - mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_LOW); - mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, false, false); - mHelper.setImportance(PKG_N_MR1, UID_N_MR1, IMPORTANCE_NONE); - mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false); - mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH); - - // in the json array, all of the individual package preferences are simply elements in the - // values array. this set is to collect expected outputs for each of our packages. - // the key/value pairs are: (userId, package name) -> expected importance - ArrayMap<Pair<Integer, String>, String> expected = new ArrayMap<>(); - expected.put(new Pair(UserHandle.getUserId(UID_P), PKG_P), "LOW"); - expected.put(new Pair(UserHandle.getUserId(UID_O), PKG_O), "HIGH"); - expected.put(new Pair(UserHandle.getUserId(UID_N_MR1), PKG_N_MR1), "NONE"); - - JSONArray actual = (JSONArray) mHelper.dumpJson( - new NotificationManagerService.DumpFilter(), appPermissions) - .get("PackagePreferencess"); - assertThat(actual.length()).isEqualTo(expected.size()); - for (int i = 0; i < actual.length(); i++) { - JSONObject pkgInfo = actual.getJSONObject(i); - Pair<Integer, String> pkgKey = - new Pair(pkgInfo.getInt("userId"), pkgInfo.getString("packageName")); - assertTrue(expected.containsKey(pkgKey)); - assertThat(pkgInfo.getString("importance")).isEqualTo(expected.get(pkgKey)); - } - - // also make sure that (more likely to actually happen) if we don't provide an array of - // app preferences (and do null instead), the same thing happens, so do the same checks - JSONArray actualWithNullInput = (JSONArray) mHelper.dumpJson( - new NotificationManagerService.DumpFilter(), null) - .get("PackagePreferencess"); - assertThat(actualWithNullInput.length()).isEqualTo(expected.size()); - for (int i = 0; i < actualWithNullInput.length(); i++) { - JSONObject pkgInfo = actualWithNullInput.getJSONObject(i); - Pair<Integer, String> pkgKey = - new Pair(pkgInfo.getInt("userId"), pkgInfo.getString("packageName")); - assertTrue(expected.containsKey(pkgKey)); - assertThat(pkgInfo.getString("importance")).isEqualTo(expected.get(pkgKey)); - } - } - - @Test public void testDumpJson_postPermissionMigration() throws Exception { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); // when getting a json dump, we want to verify that: // - all notification importance info should come from the permission, even if the data // isn't there yet but is present in package preferences @@ -2844,11 +2758,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { mHelper.createNotificationChannel(PKG_P, UID_P, channel1, true, false); mHelper.createNotificationChannel(PKG_P, UID_P, channel2, false, false); - mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_LOW); mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel3, false, false); - mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE); mHelper.createNotificationChannel(PKG_O, UID_O, getChannel(), true, false); - mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH); // in the json array, all of the individual package preferences are simply elements in the // values array. this set is to collect expected outputs for each of our packages. @@ -2887,11 +2798,10 @@ public class PreferencesHelperTest extends UiServiceTestCase { public void testDumpJson_givenNullInput_postMigration() throws Exception { // simple test just to make sure nothing dies if we pass in null input even post migration // for some reason, even though in practice this should not be how one calls this method - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); - // some packages exist, with some importance info that won't be looked at - mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH); - mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE); + // some packages exist + mHelper.canShowBadge(PKG_O, UID_O); + mHelper.canShowBadge(PKG_P, UID_P); JSONArray actual = (JSONArray) mHelper.dumpJson( new NotificationManagerService.DumpFilter(), null) @@ -2908,44 +2818,16 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testDumpBansJson_prePermissionMigration() throws Exception { - // confirm that the package bans that are in json are only from package preferences, and - // not from the passed-in permissions map - when(mPermissionHelper.isMigrationEnabled()).thenReturn(false); - - ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); - appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs - appPermissions.put(new Pair(3, "third"), new Pair(false, false)); // not in local prefs - appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs - - // package preferences: only PKG_P is banned - mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH); - mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE); - - // make sure that's the only thing in the package ban output - JSONArray actual = mHelper.dumpBansJson( - new NotificationManagerService.DumpFilter(), appPermissions); - assertThat(actual.length()).isEqualTo(1); - - JSONObject ban = actual.getJSONObject(0); - assertThat(ban.getInt("userId")).isEqualTo(UserHandle.getUserId(UID_P)); - assertThat(ban.getString("packageName")).isEqualTo(PKG_P); - } - - @Test public void testDumpBansJson_postPermissionMigration() throws Exception { // confirm that the package bans that are in the output include all packages that // have their permission set to false, and not based on PackagePreferences importance - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs appPermissions.put(new Pair(3, "third"), new Pair(false, false)); // not in local prefs appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs - // package preferences: PKG_O not banned based on local importance, and PKG_P is - mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH); - mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE); + mHelper.canShowBadge(PKG_O, UID_O); // expected output ArraySet<Pair<Integer, String>> expected = new ArraySet<>(); @@ -2967,10 +2849,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testDumpBansJson_givenNullInput() throws Exception { // no one should do this, but... - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); - - mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH); - mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE); JSONArray actual = mHelper.dumpBansJson( new NotificationManagerService.DumpFilter(), null); @@ -2978,59 +2856,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testDumpString_prePermissionMigration() { - // confirm that the string resulting from dumpImpl contains only info from package prefs - when(mPermissionHelper.isMigrationEnabled()).thenReturn(false); - - ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); - appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs - appPermissions.put(new Pair(3, "third"), new Pair(false, true)); // not in local prefs - appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs - - // local package preferences: PKG_O is not banned even though the permissions would - // indicate so - mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH); - mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE); - - // get dump output as a string so we can inspect the contents later - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - mHelper.dump(pw, "", new NotificationManagerService.DumpFilter(), appPermissions); - pw.flush(); - String actual = sw.toString(); - - // expected (substring) output for each preference - ArrayList<String> expected = new ArrayList<>(); - expected.add(PKG_O + " (" + UID_O + ") importance=HIGH"); - expected.add(PKG_P + " (" + UID_P + ") importance=NONE"); - - // make sure the things in app permissions do NOT show up - ArrayList<String> notExpected = new ArrayList<>(); - notExpected.add("first (1) importance=DEFAULT"); - notExpected.add("third (3) importance=NONE"); - notExpected.add("userSet="); // no user-set information pre migration - - for (String exp : expected) { - assertTrue(actual.contains(exp)); - } - - for (String notExp : notExpected) { - assertFalse(actual.contains(notExp)); - } - - // also make sure it works the same if we pass in a null input - StringWriter sw2 = new StringWriter(); - PrintWriter pw2 = new PrintWriter(sw2); - mHelper.dump(pw2, "", new NotificationManagerService.DumpFilter(), null); - pw.flush(); - String actualWithNullInput = sw2.toString(); - assertThat(actualWithNullInput).isEqualTo(actual); - } - - @Test public void testDumpString_postPermissionMigration() { // confirm that the string resulting from dumpImpl contains only importances from permission - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs @@ -3038,8 +2865,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs // local package preferences - mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH); - mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE); + mHelper.canShowBadge(PKG_O, UID_O); + mHelper.canShowBadge(PKG_P, UID_P); // get dump output as a string so we can inspect the contents later StringWriter sw = new StringWriter(); @@ -3072,11 +2899,10 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testDumpString_givenNullInput() { // test that this doesn't choke on null input - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); // local package preferences - mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH); - mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE); + mHelper.canShowBadge(PKG_O, UID_O); + mHelper.canShowBadge(PKG_P, UID_P); // get dump output StringWriter sw = new StringWriter(); @@ -3090,48 +2916,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testDumpProto_prePermissionMigration() throws Exception { - // test that dumping to proto gets the importances from the right place - when(mPermissionHelper.isMigrationEnabled()).thenReturn(false); - - ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); - appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs - appPermissions.put(new Pair(3, "third"), new Pair(false, false)); // not in local prefs - appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs - - // local package preferences - mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH); - mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE); - - // expected output: only the local preferences - // map format: (uid, package name) -> importance (int) - ArrayMap<Pair<Integer, String>, Integer> expected = new ArrayMap<>(); - expected.put(new Pair(UID_O, PKG_O), IMPORTANCE_HIGH); - expected.put(new Pair(UID_P, PKG_P), IMPORTANCE_NONE); - - // get the proto output and inspect its contents - ProtoOutputStream proto = new ProtoOutputStream(); - mHelper.dump(proto, new NotificationManagerService.DumpFilter(), appPermissions); - - RankingHelperProto actual = RankingHelperProto.parseFrom(proto.getBytes()); - assertThat(actual.records.length).isEqualTo(expected.size()); - for (int i = 0; i < actual.records.length; i++) { - RankingHelperProto.RecordProto record = actual.records[i]; - Pair<Integer, String> pkgKey = new Pair(record.uid, record.package_); - assertTrue(expected.containsKey(pkgKey)); - assertThat(record.importance).isEqualTo(expected.get(pkgKey)); - } - - // also check that it's the same as passing in null input - ProtoOutputStream proto2 = new ProtoOutputStream(); - mHelper.dump(proto2, new NotificationManagerService.DumpFilter(), null); - assertThat(proto.getBytes()).isEqualTo(proto2.getBytes()); - } - - @Test public void testDumpProto_postPermissionMigration() throws Exception { // test that dumping to proto gets the importances from the right place - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); // permissions -- these should take precedence ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); @@ -3140,8 +2926,8 @@ public class PreferencesHelperTest extends UiServiceTestCase { appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs // local package preferences - mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH); - mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_LOW); + mHelper.canShowBadge(PKG_O, UID_O); + mHelper.canShowBadge(PKG_P, UID_P); // expected output: all the packages, but only the ones provided via appPermissions // should have importance set (aka not PKG_P) @@ -3429,14 +3215,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testAppBlockedLogging() { - mHelper.setEnabled(PKG_N_MR1, 1020, false); - assertEquals(1, mLogger.getCalls().size()); - assertEquals( - NotificationChannelLogger.NotificationChannelEvent.APP_NOTIFICATIONS_BLOCKED, - mLogger.get(0).event); - } - @Test public void testXml_statusBarIcons_default() throws Exception { String preQXml = "<ranking version=\"1\">\n" + "<package name=\"" + PKG_N_MR1 + "\" show_badge=\"true\">\n" @@ -3517,7 +3295,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testIsDelegateAllowed_noDelegate() { - mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_UNSPECIFIED); + mHelper.canShowBadge(PKG_O, UID_O); assertFalse(mHelper.isDelegateAllowed(PKG_O, UID_O, "whatever", 0)); } @@ -3555,7 +3333,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testDelegateXml_noDelegate() throws Exception { - mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_UNSPECIFIED); + mHelper.canShowBadge(PKG_O, UID_O); ByteArrayOutputStream baos = writeXmlAndPurge(PKG_O, UID_O, false, UserHandle.USER_ALL); mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper, @@ -3744,337 +3522,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testLockChannelsForOEM_emptyList() { - mHelper.lockChannelsForOEM(null); - mHelper.lockChannelsForOEM(new String[0]); - // no exception - } - - @Test - public void testLockChannelsForOEM_appWide() { - NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); - NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW); - NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT); - // different uids, same package - mHelper.createNotificationChannel(PKG_O, 3, a, true, false); - mHelper.createNotificationChannel(PKG_O, 3, b, false, false); - mHelper.createNotificationChannel(PKG_O, 30, c, true, true); - - mHelper.lockChannelsForOEM(new String[] {PKG_O}); - - assertTrue(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false) - .isImportanceLockedByOEM()); - assertTrue(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false) - .isImportanceLockedByOEM()); - assertTrue(mHelper.getNotificationChannel(PKG_O, 30, c.getId(), false) - .isImportanceLockedByOEM()); - } - - @Test - public void testLockChannelsForOEM_onlyGivenPkg() { - NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); - NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW); - mHelper.createNotificationChannel(PKG_O, 3, a, true, false); - mHelper.createNotificationChannel(PKG_N_MR1, 30, b, false, false); - - mHelper.lockChannelsForOEM(new String[] {PKG_O}); - - assertTrue(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false) - .isImportanceLockedByOEM()); - assertFalse(mHelper.getNotificationChannel(PKG_N_MR1, 30, b.getId(), false) - .isImportanceLockedByOEM()); - } - - @Test - public void testLockChannelsForOEM_channelSpecific() { - NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); - NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW); - NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT); - // different uids, same package - mHelper.createNotificationChannel(PKG_O, 3, a, true, false); - mHelper.createNotificationChannel(PKG_O, 3, b, false, false); - mHelper.createNotificationChannel(PKG_O, 30, c, true, true); - - mHelper.lockChannelsForOEM(new String[] {PKG_O + ":b", PKG_O + ":c"}); - - assertFalse(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false) - .isImportanceLockedByOEM()); - assertTrue(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false) - .isImportanceLockedByOEM()); - assertTrue(mHelper.getNotificationChannel(PKG_O, 30, c.getId(), false) - .isImportanceLockedByOEM()); - } - - @Test - public void testLockChannelsForOEM_onlyGivenPkg_appDoesNotExistYet() { - mHelper.lockChannelsForOEM(new String[] {PKG_O}); - - NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); - NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW); - mHelper.createNotificationChannel(PKG_O, 3, a, true, false); - mHelper.createNotificationChannel(PKG_N_MR1, 30, b, false, false); - - assertTrue(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false) - .isImportanceLockedByOEM()); - assertFalse(mHelper.getNotificationChannel(PKG_N_MR1, 30, b.getId(), false) - .isImportanceLockedByOEM()); - } - - @Test - public void testLockChannelsForOEM_channelSpecific_appDoesNotExistYet() { - mHelper.lockChannelsForOEM(new String[] {PKG_O + ":b", PKG_O + ":c"}); - - NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); - NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW); - NotificationChannel c = new NotificationChannel("c", "c", IMPORTANCE_DEFAULT); - // different uids, same package - mHelper.createNotificationChannel(PKG_O, 3, a, true, false); - mHelper.createNotificationChannel(PKG_O, 3, b, false, false); - mHelper.createNotificationChannel(PKG_O, 30, c, true, true); - - assertFalse(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false) - .isImportanceLockedByOEM()); - assertTrue(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false) - .isImportanceLockedByOEM()); - assertTrue(mHelper.getNotificationChannel(PKG_O, 30, c.getId(), false) - .isImportanceLockedByOEM()); - } - - @Test - public void testLockChannelsForOEM_onlyGivenPkg_appDoesNotExistYet_restoreData() - throws Exception { - mHelper.lockChannelsForOEM(new String[] {PKG_O}); - - final String xml = "<ranking version=\"1\">\n" - + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" - + "<channel id=\"a\" name=\"a\" importance=\"3\"/>" - + "<channel id=\"b\" name=\"b\" importance=\"3\"/>" - + "</package>" - + "<package name=\"" + PKG_N_MR1 + "\" uid=\"" + UID_N_MR1 + "\" >\n" - + "<channel id=\"a\" name=\"a\" importance=\"3\"/>" - + "<channel id=\"b\" name=\"b\" importance=\"3\"/>" - + "</package>" - + "</ranking>"; - TypedXmlPullParser parser = Xml.newFastPullParser(); - parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), - null); - parser.nextTag(); - mHelper.readXml(parser, false, UserHandle.USER_ALL); - - assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, "a", false) - .isImportanceLockedByOEM()); - assertFalse(mHelper.getNotificationChannel(PKG_N_MR1, UID_N_MR1, "b", false) - .isImportanceLockedByOEM()); - } - - @Test - public void testLockChannelsForOEM_onlyGivenPkg_appDoesNotExistYet_restoreData_postMigration() - throws Exception { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); - mHelper.lockChannelsForOEM(new String[] {PKG_O}); - - final String xml = "<ranking version=\"1\">\n" - + "<package name=\"" + PKG_O + "\" uid=\"" + UID_O + "\" >\n" - + "<channel id=\"a\" name=\"a\" importance=\"3\"/>" - + "<channel id=\"b\" name=\"b\" importance=\"3\"/>" - + "</package>" - + "</ranking>"; - TypedXmlPullParser parser = Xml.newFastPullParser(); - parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), - null); - parser.nextTag(); - mHelper.readXml(parser, false, UserHandle.USER_ALL); - - assertFalse(mHelper.getNotificationChannel(PKG_O, UID_O, "a", false) - .isImportanceLockedByOEM()); - } - - @Test - public void testLockChannelsForOEM_channelSpecific_appDoesNotExistYet_restoreData() - throws Exception { - mHelper.lockChannelsForOEM(new String[] {PKG_O + ":b", PKG_O + ":c"}); - - final String xml = "<ranking version=\"1\">\n" - + "<package name=\"" + PKG_O + "\" uid=\"" + 3 + "\" >\n" - + "<channel id=\"a\" name=\"a\" importance=\"3\"/>" - + "<channel id=\"b\" name=\"b\" importance=\"3\"/>" - + "</package>" - + "<package name=\"" + PKG_O + "\" uid=\"" + 30 + "\" >\n" - + "<channel id=\"c\" name=\"c\" importance=\"3\"/>" - + "</package>" - + "</ranking>"; - TypedXmlPullParser parser = Xml.newFastPullParser(); - parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), - null); - parser.nextTag(); - mHelper.readXml(parser, false, UserHandle.USER_ALL); - - assertFalse(mHelper.getNotificationChannel(PKG_O, 3, "a", false) - .isImportanceLockedByOEM()); - assertTrue(mHelper.getNotificationChannel(PKG_O, 3, "b", false) - .isImportanceLockedByOEM()); - assertTrue(mHelper.getNotificationChannel(PKG_O, 30, "c", false) - .isImportanceLockedByOEM()); - } - - @Test - public void testLockChannelsForOEM_channelSpecific_appDoesNotExistYet_restoreData_postMigration() - throws Exception { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); - mHelper.lockChannelsForOEM(new String[] {PKG_O + ":b", PKG_O + ":c"}); - - final String xml = "<ranking version=\"1\">\n" - + "<package name=\"" + PKG_O + "\" uid=\"" + 3 + "\" >\n" - + "<channel id=\"a\" name=\"a\" importance=\"3\"/>" - + "<channel id=\"b\" name=\"b\" importance=\"3\"/>" - + "</package>" - + "<package name=\"" + PKG_O + "\" uid=\"" + 30 + "\" >\n" - + "<channel id=\"c\" name=\"c\" importance=\"3\"/>" - + "</package>" - + "</ranking>"; - TypedXmlPullParser parser = Xml.newFastPullParser(); - parser.setInput(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes())), - null); - parser.nextTag(); - mHelper.readXml(parser, false, UserHandle.USER_ALL); - - assertFalse(mHelper.getNotificationChannel(PKG_O, 3, "a", false) - .isImportanceLockedByOEM()); - assertFalse(mHelper.getNotificationChannel(PKG_O, 3, "b", false) - .isImportanceLockedByOEM()); - assertFalse(mHelper.getNotificationChannel(PKG_O, 30, "c", false) - .isImportanceLockedByOEM()); - } - - @Test - public void testLockChannelsForOEM_channelSpecific_clearData() { - NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); - mHelper.getImportance(PKG_O, UID_O); - mHelper.lockChannelsForOEM(new String[] {PKG_O + ":" + a.getId()}); - mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); - assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false) - .isImportanceLockedByOEM()); - - mHelper.clearData(PKG_O, UID_O); - - // it's back! - mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); - // and still locked - assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false) - .isImportanceLockedByOEM()); - } - - @Test - public void testLockChannelsForOEM_channelDoesNotExistYet_appWide() { - NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); - NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW); - mHelper.createNotificationChannel(PKG_O, 3, a, true, false); - - mHelper.lockChannelsForOEM(new String[] {PKG_O}); - - assertTrue(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false) - .isImportanceLockedByOEM()); - - mHelper.createNotificationChannel(PKG_O, 3, b, true, false); - assertTrue(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false) - .isImportanceLockedByOEM()); - } - - @Test - public void testLockChannelsForOEM_channelDoesNotExistYet_channelSpecific() { - NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); - NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW); - mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); - - mHelper.lockChannelsForOEM(new String[] {PKG_O + ":a", PKG_O + ":b"}); - - assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false) - .isImportanceLockedByOEM()); - - mHelper.createNotificationChannel(PKG_O, UID_O, b, true, false); - assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, b.getId(), false) - .isImportanceLockedByOEM()); - } - - @Test - public void testLockChannelsForOEM_channelSpecific_clearData_postMigration() { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); - NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); - mHelper.getImportance(PKG_O, UID_O); - mHelper.lockChannelsForOEM(new String[] {PKG_O + ":" + a.getId()}); - mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); - assertFalse(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false) - .isImportanceLockedByOEM()); - - mHelper.clearData(PKG_O, UID_O); - - // it's back! - mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); - // and never locked - assertFalse(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false) - .isImportanceLockedByOEM()); - } - - @Test - public void testLockChannelsForOEM_channelDoesNotExistYet_appWide_postMigration() { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); - NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); - NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW); - mHelper.createNotificationChannel(PKG_O, 3, a, true, false); - - mHelper.lockChannelsForOEM(new String[] {PKG_O}); - - assertFalse(mHelper.getNotificationChannel(PKG_O, 3, a.getId(), false) - .isImportanceLockedByOEM()); - - mHelper.createNotificationChannel(PKG_O, 3, b, true, false); - assertFalse(mHelper.getNotificationChannel(PKG_O, 3, b.getId(), false) - .isImportanceLockedByOEM()); - } - - @Test - public void testLockChannelsForOEM_channelDoesNotExistYet_channelSpecific_postMigration() { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); - NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); - NotificationChannel b = new NotificationChannel("b", "b", IMPORTANCE_LOW); - mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); - - mHelper.lockChannelsForOEM(new String[] {PKG_O + ":a", PKG_O + ":b"}); - - assertFalse(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false) - .isImportanceLockedByOEM()); - - mHelper.createNotificationChannel(PKG_O, UID_O, b, true, false); - assertFalse(mHelper.getNotificationChannel(PKG_O, UID_O, b.getId(), false) - .isImportanceLockedByOEM()); - } - - @Test - public void testUpdateNotificationChannel_oemLockedImportance() { - NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); - mHelper.createNotificationChannel(PKG_O, UID_O, a, true, false); - - mHelper.lockChannelsForOEM(new String[] {PKG_O}); - - NotificationChannel update = new NotificationChannel("a", "a", IMPORTANCE_NONE); - update.setAllowBubbles(false); - - mHelper.updateNotificationChannel(PKG_O, UID_O, update, true); - - assertEquals(IMPORTANCE_HIGH, - mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance()); - assertEquals(false, - mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).canBubble()); - - mHelper.updateNotificationChannel(PKG_O, UID_O, update, true); - - assertEquals(IMPORTANCE_HIGH, - mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false).getImportance()); - } - - @Test public void testUpdateNotificationChannel_fixedPermission() { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(true); NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); @@ -4093,7 +3541,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testUpdateNotificationChannel_fixedPermission_butUserPreviouslyBlockedIt() { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(true); NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_NONE); @@ -4112,7 +3559,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testUpdateNotificationChannel_fixedPermission_butAppAllowsIt() { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(true); NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); @@ -4132,7 +3578,6 @@ public class PreferencesHelperTest extends UiServiceTestCase { @Test public void testUpdateNotificationChannel_notFixedPermission() { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); when(mPermissionHelper.isPermissionFixed(PKG_O, 0)).thenReturn(false); NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); @@ -5312,56 +4757,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { } @Test - public void testPullPackagePreferencesStats_prePermissionMigration() { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(false); - - // build a collection of app permissions that should be passed in but ignored - ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); - appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs - appPermissions.put(new Pair(3, "third"), new Pair(false, false)); // not in local prefs - appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs - - // package preferences: PKG_O not banned based on local importance, and PKG_P is - mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH); - mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE); - - // expected output. format: uid -> importance, as only uid (and not package name) - // is in PackageNotificationPreferences - ArrayMap<Integer, Integer> expected = new ArrayMap<>(); - expected.put(UID_O, IMPORTANCE_HIGH); - expected.put(UID_P, IMPORTANCE_NONE); - - // unexpected output. these UIDs should not show up in the output at all - ArraySet<Integer> unexpected = new ArraySet<>(); - unexpected.add(1); - unexpected.add(3); - - ArrayList<StatsEvent> events = new ArrayList<>(); - mHelper.pullPackagePreferencesStats(events, appPermissions); - - for (WrappedSysUiStatsEvent.WrappedBuilder builder : mStatsEventBuilderFactory.builders) { - if (builder.getAtomId() == PACKAGE_NOTIFICATION_PREFERENCES) { - int uid = builder.getInt(PackageNotificationPreferences.UID_FIELD_NUMBER); - - // this shouldn't be any of the forbidden uids - assertFalse(unexpected.contains(uid)); - - // if it's one of the expected ids, then make sure the importance matches - assertTrue(expected.containsKey(uid)); - assertThat(expected.get(uid)).isEqualTo( - builder.getInt(PackageNotificationPreferences.IMPORTANCE_FIELD_NUMBER)); - - // pre-migration, the userSet field will always default to false - boolean userSet = builder.getBoolean( - PackageNotificationPreferences.USER_SET_IMPORTANCE_FIELD_NUMBER); - assertFalse(userSet); - } - } - } - - @Test public void testPullPackagePreferencesStats_postPermissionMigration() { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); // build a collection of app permissions that should be passed in but ignored ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>(); @@ -5369,9 +4765,9 @@ public class PreferencesHelperTest extends UiServiceTestCase { appPermissions.put(new Pair(3, "third"), new Pair(false, true)); // not in local prefs appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, true)); // in local prefs - // package preferences: PKG_O not banned based on local importance, and PKG_P is - mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH); - mHelper.setImportance(PKG_P, UID_P, IMPORTANCE_NONE); + // local preferences + mHelper.canShowBadge(PKG_O, UID_O); + mHelper.canShowBadge(PKG_P, UID_P); // expected output. format: uid -> importance, as only uid (and not package name) // is in PackageNotificationPreferences @@ -5433,27 +4829,4 @@ public class PreferencesHelperTest extends UiServiceTestCase { assertTrue((channelB.getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0); assertTrue((channelC.getUserLockedFields() & USER_LOCKED_IMPORTANCE) == 0); } - - @Test - public void testDefaultChannelUpdatesApp_preMigrationToPermissions() throws Exception { - final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG_N_MR1, - UID_N_MR1, - NotificationChannel.DEFAULT_CHANNEL_ID, false); - defaultChannel.setImportance(IMPORTANCE_NONE); - mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true); - - assertEquals(IMPORTANCE_NONE, mHelper.getImportance(PKG_N_MR1, UID_N_MR1)); - } - - @Test - public void testDefaultChannelDoesNotUpdateApp_postMigrationToPermissions() throws Exception { - when(mPermissionHelper.isMigrationEnabled()).thenReturn(true); - final NotificationChannel defaultChannel = mHelper.getNotificationChannel(PKG_N_MR1, - UID_N_MR1, - NotificationChannel.DEFAULT_CHANNEL_ID, false); - defaultChannel.setImportance(IMPORTANCE_NONE); - mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, defaultChannel, true); - - assertEquals(IMPORTANCE_UNSPECIFIED, mHelper.getImportance(PKG_N_MR1, UID_N_MR1)); - } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java index 0bfd2020622f..98c156e6f3b5 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java @@ -168,7 +168,8 @@ public class RoleObserverTest extends UiServiceTestCase { mock(StatsManager.class), mock(TelephonyManager.class), mock(ActivityManagerInternal.class), mock(MultiRateLimiter.class), mock(PermissionHelper.class), - mock(UsageStatsManagerInternal.class), mock (TelecomManager.class)); + mock(UsageStatsManagerInternal.class), mock (TelecomManager.class), + mock(NotificationChannelLogger.class)); } catch (SecurityException e) { if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { throw e; diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index 35921585d56f..eb6395b46120 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -39,10 +39,8 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.junit.Assert.assertEquals; @@ -50,9 +48,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import android.graphics.Rect; import android.os.Binder; @@ -380,7 +376,7 @@ public class AppTransitionTests extends WindowTestsBase { doReturn(false).when(dc).onDescendantOrientationChanged(any()); final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "exiting app"); - final ActivityRecord exitingActivity = exitingAppWindow.mActivityRecord; + final ActivityRecord exitingActivity= exitingAppWindow.mActivityRecord; // Wait until everything in animation handler get executed to prevent the exiting window // from being removed during WindowSurfacePlacer Traversal. waitUntilHandlersIdle(); @@ -409,41 +405,6 @@ public class AppTransitionTests extends WindowTestsBase { } @Test - public void testDelayWhileRecents() { - final DisplayContent dc = createNewDisplay(Display.STATE_ON); - doReturn(false).when(dc).onDescendantOrientationChanged(any()); - final Task task = createTask(dc); - - // Simulate activity1 launches activity2. - final ActivityRecord activity1 = createActivityRecord(task); - activity1.setVisible(true); - activity1.mVisibleRequested = false; - activity1.allDrawn = true; - final ActivityRecord activity2 = createActivityRecord(task); - activity2.setVisible(false); - activity2.mVisibleRequested = true; - activity2.allDrawn = true; - - dc.mClosingApps.add(activity1); - dc.mOpeningApps.add(activity2); - dc.prepareAppTransition(TRANSIT_OPEN); - assertTrue(dc.mAppTransition.containsTransitRequest(TRANSIT_OPEN)); - - // Wait until everything in animation handler get executed to prevent the exiting window - // from being removed during WindowSurfacePlacer Traversal. - waitUntilHandlersIdle(); - - // Start recents - doReturn(true).when(task) - .isSelfAnimating(anyInt(), eq(ANIMATION_TYPE_RECENTS)); - - dc.mAppTransitionController.handleAppTransitionReady(); - - verify(activity1, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean()); - verify(activity2, never()).commitVisibility(anyBoolean(), anyBoolean(), anyBoolean()); - } - - @Test public void testGetAnimationStyleResId() { // Verify getAnimationStyleResId will return as LayoutParams.windowAnimations when without // specifying window type. diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java index e3485deb9080..ec94f8a1829f 100644 --- a/telecomm/java/android/telecom/PhoneAccountHandle.java +++ b/telecomm/java/android/telecom/PhoneAccountHandle.java @@ -46,6 +46,14 @@ import java.util.Objects; * See {@link PhoneAccount}, {@link TelecomManager}. */ public final class PhoneAccountHandle implements Parcelable { + /** + * Expected component name of Telephony phone accounts; ONLY used to determine if we should log + * the phone account handle ID. + */ + private static final ComponentName TELEPHONY_COMPONENT_NAME = + new ComponentName("com.android.phone", + "com.android.services.telephony.TelephonyConnectionService"); + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 127403196) private final ComponentName mComponentName; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) @@ -136,14 +144,23 @@ public final class PhoneAccountHandle implements Parcelable { @Override public String toString() { - // Note: Log.pii called for mId as it can contain personally identifying phone account - // information such as SIP account IDs. - return new StringBuilder().append(mComponentName) - .append(", ") - .append(Log.pii(mId)) - .append(", ") - .append(mUserHandle) - .toString(); + StringBuilder sb = new StringBuilder() + .append(mComponentName) + .append(", "); + + if (TELEPHONY_COMPONENT_NAME.equals(mComponentName)) { + // Telephony phone account handles are now keyed by subscription id which is not + // sensitive. + sb.append(mId); + } else { + // Note: Log.pii called for mId as it can contain personally identifying phone account + // information such as SIP account IDs. + sb.append(Log.pii(mId)); + } + sb.append(", "); + sb.append(mUserHandle); + + return sb.toString(); } @Override diff --git a/telephony/java/android/telephony/AnomalyReporter.java b/telephony/java/android/telephony/AnomalyReporter.java index f47cf3384791..e7d95e4f53b3 100644 --- a/telephony/java/android/telephony/AnomalyReporter.java +++ b/telephony/java/android/telephony/AnomalyReporter.java @@ -16,6 +16,8 @@ package android.telephony; +import static android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID; + import static com.android.internal.telephony.TelephonyStatsLog.TELEPHONY_ANOMALY_DETECTED; import android.annotation.NonNull; @@ -73,6 +75,7 @@ public final class AnomalyReporter { * * This method sends the {@link TelephonyManager#ACTION_ANOMALY_REPORTED} broadcast, which is * system protected. Invoking this method unless you are the system will result in an error. + * Carrier Id will be set as UNKNOWN_CARRIER_ID. * * @param eventId a fixed event ID that will be sent for each instance of the same event. This * ID should be generated randomly. @@ -81,6 +84,23 @@ public final class AnomalyReporter { * static and must not contain any sensitive information (especially PII). */ public static void reportAnomaly(@NonNull UUID eventId, String description) { + reportAnomaly(eventId, description, UNKNOWN_CARRIER_ID); + } + + /** + * If enabled, build and send an intent to a Debug Service for logging. + * + * This method sends the {@link TelephonyManager#ACTION_ANOMALY_REPORTED} broadcast, which is + * system protected. Invoking this method unless you are the system will result in an error. + * + * @param eventId a fixed event ID that will be sent for each instance of the same event. This + * ID should be generated randomly. + * @param description an optional description, that if included will be used as the subject for + * identification and discussion of this event. This description should ideally be + * static and must not contain any sensitive information (especially PII). + * @param carrierId the carrier of the id associated with this event. + */ + public static void reportAnomaly(@NonNull UUID eventId, String description, int carrierId) { if (sContext == null) { Rlog.w(TAG, "AnomalyReporter not yet initialized, dropping event=" + eventId); return; @@ -88,7 +108,7 @@ public final class AnomalyReporter { TelephonyStatsLog.write( TELEPHONY_ANOMALY_DETECTED, - 0, // TODO: carrier id needs to be populated + carrierId, eventId.getLeastSignificantBits(), eventId.getMostSignificantBits()); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index e21301eb32af..70fe6b10ef20 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -4980,6 +4980,25 @@ public class CarrierConfigManager { KEY_PREFIX + "use_sip_uri_for_presence_subscribe_bool"; /** + * Flag indicating whether or not to use TEL URI when setting the entity uri field and + * contact element of each tuple. + * + * When {@code true}, the device sets the entity uri field and contact element to be + * TEL URI. This is done by first searching for the first TEL URI provided in + * p-associated-uri header. If there are no TEL URIs in the p-associated-uri header, we will + * convert the first SIP URI provided in the header to a TEL URI. If there are no URIs in + * the p-associated-uri header, we will then fall back to using the SIM card to generate the + * TEL URI. + * If {@code false}, the first URI provided in the p-associated-uri header is used, + * independent of the URI scheme. If there are no URIs available from p-associated-uri + * header, we will try to generate a SIP URI or TEL URI from the information provided by the + * SIM card, depending on the information available. + * @hide + */ + public static final String KEY_USE_TEL_URI_FOR_PIDF_XML_BOOL = + KEY_PREFIX + "use_tel_uri_for_pidf_xml"; + + /** * An integer key associated with the period of time in seconds the non-rcs capability * information of each contact is cached on the device. * <p> diff --git a/tests/ApkVerityTest/Android.bp b/tests/ApkVerityTest/Android.bp index d4fa1dda8bdf..62e16a5b83de 100644 --- a/tests/ApkVerityTest/Android.bp +++ b/tests/ApkVerityTest/Android.bp @@ -37,8 +37,8 @@ java_test_host { "general-tests", "vts", ], - data_device_bins: [ - "block_device_writer", + target_required: [ + "block_device_writer_module", ], data: [ ":ApkVerityTestCertDer", diff --git a/tests/ApkVerityTest/block_device_writer/Android.bp b/tests/ApkVerityTest/block_device_writer/Android.bp index e5d009dc10fd..fdfa41fd4ca9 100644 --- a/tests/ApkVerityTest/block_device_writer/Android.bp +++ b/tests/ApkVerityTest/block_device_writer/Android.bp @@ -24,7 +24,12 @@ package { } cc_test { - name: "block_device_writer", + // Depending on how the test runs, the executable may be uploaded to different location. + // Before the bug in the file pusher is fixed, workaround by making the name unique. + // See b/124718249#comment12. + name: "block_device_writer_module", + stem: "block_device_writer", + srcs: ["block_device_writer.cpp"], cflags: [ "-D_FILE_OFFSET_BITS=64", @@ -37,7 +42,22 @@ cc_test { "libbase", "libutils", ], - compile_multilib: "first", + // For some reasons, cuttlefish (x86) uses x86_64 test suites for testing. Unfortunately, when + // the uploader does not pick up the executable from correct output location. The following + // workaround allows the test to: + // * upload the 32-bit exectuable for both 32 and 64 bits devices to use + // * refer to the same executable name in Java + // * no need to force the Java test to be archiecture specific. + // + // See b/145573317 for details. + multilib: { + lib32: { + suffix: "", + }, + lib64: { + suffix: "64", // not really used + }, + }, auto_gen_config: false, test_suites: [ diff --git a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java index 730daf32f20d..5c2c15b22bb0 100644 --- a/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java +++ b/tests/ApkVerityTest/block_device_writer/src/com/android/blockdevicewriter/BlockDeviceWriter.java @@ -32,7 +32,7 @@ import java.util.ArrayList; * <p>To use this class, please push block_device_writer binary to /data/local/tmp. * 1. In Android.bp, add: * <pre> - * data_device_bins: ["block_device_writer"], + * target_required: ["block_device_writer_module"], * </pre> * 2. In AndroidText.xml, add: * <pre> |