Merge "Add initial plugin for contextual auth prototypes to support BP and bouncer." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 0bce29f..56831d7 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -966,6 +966,13 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.app.flags-aconfig-java-host",
+ aconfig_declarations: "android.app.flags-aconfig",
+ host_supported: true,
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Broadcast Radio
aconfig_declarations {
name: "android.hardware.radio.flags-aconfig",
diff --git a/core/java/android/app/BroadcastStickyCache.java b/core/java/android/app/BroadcastStickyCache.java
new file mode 100644
index 0000000..fe2e107
--- /dev/null
+++ b/core/java/android/app/BroadcastStickyCache.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2024 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.app;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.content.Context.RegisterReceiverFlags;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbManager;
+import android.media.AudioManager;
+import android.net.ConnectivityManager;
+import android.net.TetheringManager;
+import android.net.nsd.NsdManager;
+import android.net.wifi.WifiManager;
+import android.net.wifi.p2p.WifiP2pManager;
+import android.os.IpcDataCache;
+import android.os.IpcDataCache.Config;
+import android.os.UpdateLock;
+import android.telephony.TelephonyManager;
+import android.util.ArrayMap;
+import android.view.WindowManagerPolicyConstants;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+/** @hide */
+public class BroadcastStickyCache {
+
+ @VisibleForTesting
+ public static final String[] STICKY_BROADCAST_ACTIONS = {
+ AudioManager.ACTION_HDMI_AUDIO_PLUG,
+ AudioManager.ACTION_HEADSET_PLUG,
+ AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED,
+ AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED,
+ AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION,
+ AudioManager.RINGER_MODE_CHANGED_ACTION,
+ ConnectivityManager.CONNECTIVITY_ACTION,
+ Intent.ACTION_BATTERY_CHANGED,
+ Intent.ACTION_DEVICE_STORAGE_FULL,
+ Intent.ACTION_DEVICE_STORAGE_LOW,
+ Intent.ACTION_SIM_STATE_CHANGED,
+ NsdManager.ACTION_NSD_STATE_CHANGED,
+ TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED,
+ TetheringManager.ACTION_TETHER_STATE_CHANGED,
+ UpdateLock.UPDATE_LOCK_CHANGED,
+ UsbManager.ACTION_USB_STATE,
+ WifiManager.ACTION_WIFI_SCAN_AVAILABILITY_CHANGED,
+ WifiManager.NETWORK_STATE_CHANGED_ACTION,
+ WifiManager.SUPPLICANT_STATE_CHANGED_ACTION,
+ WifiManager.WIFI_STATE_CHANGED_ACTION,
+ WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION,
+ WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED,
+ "android.net.conn.INET_CONDITION_ACTION" // ConnectivityManager.INET_CONDITION_ACTION
+ };
+
+ @VisibleForTesting
+ public static final ArrayMap<String, String> sActionApiNameMap = new ArrayMap<>();
+
+ private static final ArrayMap<String, IpcDataCache.Config> sActionConfigMap = new ArrayMap<>();
+
+ private static final ArrayMap<StickyBroadcastFilter, IpcDataCache<Void, Intent>>
+ sFilterCacheMap = new ArrayMap<>();
+
+ static {
+ sActionApiNameMap.put(AudioManager.ACTION_HDMI_AUDIO_PLUG, "hdmi_audio_plug");
+ sActionApiNameMap.put(AudioManager.ACTION_HEADSET_PLUG, "headset_plug");
+ sActionApiNameMap.put(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED,
+ "sco_audio_state_changed");
+ sActionApiNameMap.put(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED,
+ "action_sco_audio_state_updated");
+ sActionApiNameMap.put(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION,
+ "internal_ringer_mode_changed_action");
+ sActionApiNameMap.put(AudioManager.RINGER_MODE_CHANGED_ACTION,
+ "ringer_mode_changed");
+ sActionApiNameMap.put(ConnectivityManager.CONNECTIVITY_ACTION,
+ "connectivity_change");
+ sActionApiNameMap.put(Intent.ACTION_BATTERY_CHANGED, "battery_changed");
+ sActionApiNameMap.put(Intent.ACTION_DEVICE_STORAGE_FULL, "device_storage_full");
+ sActionApiNameMap.put(Intent.ACTION_DEVICE_STORAGE_LOW, "device_storage_low");
+ sActionApiNameMap.put(Intent.ACTION_SIM_STATE_CHANGED, "sim_state_changed");
+ sActionApiNameMap.put(NsdManager.ACTION_NSD_STATE_CHANGED, "nsd_state_changed");
+ sActionApiNameMap.put(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED,
+ "service_providers_updated");
+ sActionApiNameMap.put(TetheringManager.ACTION_TETHER_STATE_CHANGED,
+ "tether_state_changed");
+ sActionApiNameMap.put(UpdateLock.UPDATE_LOCK_CHANGED, "update_lock_changed");
+ sActionApiNameMap.put(UsbManager.ACTION_USB_STATE, "usb_state");
+ sActionApiNameMap.put(WifiManager.ACTION_WIFI_SCAN_AVAILABILITY_CHANGED,
+ "wifi_scan_availability_changed");
+ sActionApiNameMap.put(WifiManager.NETWORK_STATE_CHANGED_ACTION,
+ "network_state_change");
+ sActionApiNameMap.put(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION,
+ "supplicant_state_change");
+ sActionApiNameMap.put(WifiManager.WIFI_STATE_CHANGED_ACTION, "wifi_state_changed");
+ sActionApiNameMap.put(
+ WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION, "wifi_p2p_state_changed");
+ sActionApiNameMap.put(
+ WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED, "hdmi_plugged");
+ sActionApiNameMap.put(
+ "android.net.conn.INET_CONDITION_ACTION", "inet_condition_action");
+ }
+
+ /**
+ * Checks whether we can use caching for the given filter.
+ */
+ public static boolean useCache(@Nullable IntentFilter filter) {
+ return Flags.useStickyBcastCache()
+ && filter != null
+ && filter.safeCountActions() == 1
+ && ArrayUtils.contains(STICKY_BROADCAST_ACTIONS, filter.getAction(0));
+ }
+
+ public static void invalidateCache(@NonNull String action) {
+ if (!Flags.useStickyBcastCache()
+ || !ArrayUtils.contains(STICKY_BROADCAST_ACTIONS, action)) {
+ return;
+ }
+ IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM,
+ sActionApiNameMap.get(action));
+ }
+
+ public static void invalidateAllCaches() {
+ for (int i = sActionApiNameMap.size() - 1; i >= 0; i--) {
+ IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM,
+ sActionApiNameMap.valueAt(i));
+ }
+ }
+
+ /**
+ * Returns the cached {@link Intent} based on the filter, if exits otherwise
+ * fetches the value from the service.
+ */
+ @Nullable
+ public static Intent getIntent(
+ @NonNull IApplicationThread applicationThread,
+ @NonNull String mBasePackageName,
+ @Nullable String attributionTag,
+ @NonNull IntentFilter filter,
+ @Nullable String broadcastPermission,
+ @UserIdInt int userId,
+ @RegisterReceiverFlags int flags) {
+ IpcDataCache<Void, Intent> intentDataCache = findIpcDataCache(filter);
+
+ if (intentDataCache == null) {
+ final String action = filter.getAction(0);
+ final StickyBroadcastFilter stickyBroadcastFilter =
+ new StickyBroadcastFilter(filter, action);
+ final Config config = getConfig(action);
+
+ intentDataCache =
+ new IpcDataCache<>(config,
+ (query) -> ActivityManager.getService().registerReceiverWithFeature(
+ applicationThread,
+ mBasePackageName,
+ attributionTag,
+ /* receiverId= */ "null",
+ /* receiver= */ null,
+ filter,
+ broadcastPermission,
+ userId,
+ flags));
+ sFilterCacheMap.put(stickyBroadcastFilter, intentDataCache);
+ }
+ return intentDataCache.query(null);
+ }
+
+ @VisibleForTesting
+ public static void clearCacheForTest() {
+ sFilterCacheMap.clear();
+ }
+
+ @Nullable
+ private static IpcDataCache<Void, Intent> findIpcDataCache(
+ @NonNull IntentFilter filter) {
+ for (int i = sFilterCacheMap.size() - 1; i >= 0; i--) {
+ StickyBroadcastFilter existingFilter = sFilterCacheMap.keyAt(i);
+ if (filter.getAction(0).equals(existingFilter.action())
+ && IntentFilter.filterEquals(existingFilter.filter(), filter)) {
+ return sFilterCacheMap.valueAt(i);
+ }
+ }
+ return null;
+ }
+
+ @NonNull
+ private static IpcDataCache.Config getConfig(@NonNull String action) {
+ if (!sActionConfigMap.containsKey(action)) {
+ // We only need 1 entry per cache but just to be on the safer side we are choosing 32
+ // although we don't expect more than 1.
+ sActionConfigMap.put(action,
+ new Config(32, IpcDataCache.MODULE_SYSTEM, sActionApiNameMap.get(action)));
+ }
+
+ return sActionConfigMap.get(action);
+ }
+
+ @VisibleForTesting
+ private record StickyBroadcastFilter(@NonNull IntentFilter filter, @NonNull String action) {
+ }
+}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index cd56957..dcbdc23 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1922,10 +1922,23 @@
}
}
try {
- final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
- mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(),
- AppOpsManager.toReceiverId(receiver), rd, filter, broadcastPermission, userId,
- flags);
+ final Intent intent;
+ if (receiver == null && BroadcastStickyCache.useCache(filter)) {
+ intent = BroadcastStickyCache.getIntent(
+ mMainThread.getApplicationThread(),
+ mBasePackageName,
+ getAttributionTag(),
+ filter,
+ broadcastPermission,
+ userId,
+ flags);
+ } else {
+ intent = ActivityManager.getService().registerReceiverWithFeature(
+ mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(),
+ AppOpsManager.toReceiverId(receiver), rd, filter, broadcastPermission,
+ userId, flags);
+ }
+
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
// TODO: determine at registration time if caller is
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index 5ed1f4e..637187e 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -177,6 +177,10 @@
{
"file_patterns": ["(/|^)AppOpsManager.java"],
"name": "CtsAppOpsTestCases"
+ },
+ {
+ "file_patterns": ["(/|^)BroadcastStickyCache.java"],
+ "name": "BroadcastUnitTests"
}
]
}
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 94768d1..8f6a508 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -98,6 +98,7 @@
private static final int VULKAN_1_1 = 0x00401000;
private static final int VULKAN_1_2 = 0x00402000;
private static final int VULKAN_1_3 = 0x00403000;
+ private static final int VULKAN_1_4 = 0x00404000;
// Values for UPDATABLE_DRIVER_ALL_APPS
// 0: Default (Invalid values fallback to default as well)
@@ -179,6 +180,10 @@
private int getVulkanVersion(PackageManager pm) {
// PackageManager doesn't have an API to retrieve the version of a specific feature, and we
// need to avoid retrieving all system features here and looping through them.
+ if (pm.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, VULKAN_1_4)) {
+ return VULKAN_1_4;
+ }
+
if (pm.hasSystemFeature(PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, VULKAN_1_3)) {
return VULKAN_1_3;
}
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index 14a14e6..fba8e42 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -170,6 +170,9 @@
}
parcel.writeInt(event.mSaveDialogNotShowReason);
parcel.writeInt(event.mUiType);
+ if (Flags.addLastFocusedIdToFillEventHistory()) {
+ parcel.writeParcelable(event.mFocusedId, 0);
+ }
}
}
}
@@ -375,6 +378,8 @@
@UiType
private final int mUiType;
+ @Nullable private final AutofillId mFocusedId;
+
/**
* Returns the type of the event.
*
@@ -388,7 +393,7 @@
@FlaggedApi(FLAG_AUTOFILL_W_METRICS)
@Nullable
public AutofillId getFocusedId() {
- return null;
+ return mFocusedId;
}
/**
@@ -624,6 +629,7 @@
* @param manuallyFilledDatasetIds The ids of datasets that had values matching the
* respective entry on {@code manuallyFilledFieldIds}.
* @param detectedFieldClassifications the field classification matches.
+ * @param focusedId the field which was focused at the time of event trigger
*
* @throws IllegalArgumentException If the length of {@code changedFieldIds} and
* {@code changedDatasetIds} doesn't match.
@@ -640,11 +646,12 @@
@Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
@Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
@Nullable AutofillId[] detectedFieldIds,
- @Nullable FieldClassification[] detectedFieldClassifications) {
+ @Nullable FieldClassification[] detectedFieldClassifications,
+ @Nullable AutofillId focusedId) {
this(eventType, datasetId, clientState, selectedDatasetIds, ignoredDatasetIds,
changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
- NO_SAVE_UI_REASON_NONE);
+ NO_SAVE_UI_REASON_NONE, focusedId);
}
/**
@@ -665,6 +672,7 @@
* respective entry on {@code manuallyFilledFieldIds}.
* @param detectedFieldClassifications the field classification matches.
* @param saveDialogNotShowReason The reason why a save dialog was not shown.
+ * @param focusedId the field which was focused at the time of event trigger
*
* @throws IllegalArgumentException If the length of {@code changedFieldIds} and
* {@code changedDatasetIds} doesn't match.
@@ -682,11 +690,12 @@
@Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
@Nullable AutofillId[] detectedFieldIds,
@Nullable FieldClassification[] detectedFieldClassifications,
- int saveDialogNotShowReason) {
+ int saveDialogNotShowReason,
+ @Nullable AutofillId focusedId) {
this(eventType, datasetId, clientState, selectedDatasetIds, ignoredDatasetIds,
changedFieldIds, changedDatasetIds, manuallyFilledFieldIds,
manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications,
- saveDialogNotShowReason, UI_TYPE_UNKNOWN);
+ saveDialogNotShowReason, UI_TYPE_UNKNOWN, focusedId);
}
/**
@@ -708,6 +717,7 @@
* @param detectedFieldClassifications the field classification matches.
* @param saveDialogNotShowReason The reason why a save dialog was not shown.
* @param uiType The ui presentation type for fill suggestion.
+ * @param focusedId the field which was focused at the time of event trigger
*
* @throws IllegalArgumentException If the length of {@code changedFieldIds} and
* {@code changedDatasetIds} doesn't match.
@@ -725,7 +735,7 @@
@Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
@Nullable AutofillId[] detectedFieldIds,
@Nullable FieldClassification[] detectedFieldClassifications,
- int saveDialogNotShowReason, int uiType) {
+ int saveDialogNotShowReason, int uiType, @Nullable AutofillId focusedId) {
mEventType = Preconditions.checkArgumentInRange(eventType, 0,
TYPE_VIEW_REQUESTED_AUTOFILL, "eventType");
mDatasetId = datasetId;
@@ -756,6 +766,7 @@
NO_SAVE_UI_REASON_NONE, NO_SAVE_UI_REASON_DATASET_MATCH,
"saveDialogNotShowReason");
mUiType = uiType;
+ mFocusedId = focusedId;
}
@Override
@@ -852,13 +863,17 @@
: null;
final int saveDialogNotShowReason = parcel.readInt();
final int uiType = parcel.readInt();
+ AutofillId focusedId = null;
+ if (Flags.addLastFocusedIdToFillEventHistory()) {
+ focusedId = parcel.readParcelable(null, AutofillId.class);
+ }
selection.addEvent(new Event(eventType, datasetId, clientState,
selectedDatasetIds, ignoredDatasets,
changedFieldIds, changedDatasetIds,
manuallyFilledFieldIds, manuallyFilledDatasetIds,
detectedFieldIds, detectedFieldClassifications,
- saveDialogNotShowReason, uiType));
+ saveDialogNotShowReason, uiType, focusedId));
}
return selection;
}
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index e8831ec..e2be1f5 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -360,43 +360,44 @@
// Avoid non-a11y users accidentally turning shortcut on without reading this carefully.
// Put "don't turn on" as the primary action.
- final AlertDialog alertDialog = mFrameworkObjectProvider.getAlertDialogBuilder(
- // Use SystemUI context so we pick up any theme set in a vendor overlay
- mFrameworkObjectProvider.getSystemUiContext())
- .setTitle(getShortcutWarningTitle(targets))
- .setMessage(getShortcutWarningMessage(targets))
- .setCancelable(false)
- .setNegativeButton(R.string.accessibility_shortcut_on,
- (DialogInterface d, int which) -> enableDefaultHardwareShortcut(userId))
- .setPositiveButton(R.string.accessibility_shortcut_off,
- (DialogInterface d, int which) -> {
- Set<String> targetServices =
- ShortcutUtils.getShortcutTargetsFromSettings(
- mContext,
- HARDWARE,
+ final AlertDialog alertDialog =
+ mFrameworkObjectProvider
+ .getAlertDialogBuilder(
+ // Use SystemUI context so we pick up any theme set in a vendor
+ // overlay
+ mFrameworkObjectProvider.getSystemUiContext())
+ .setTitle(getShortcutWarningTitle(targets))
+ .setMessage(getShortcutWarningMessage(targets))
+ .setCancelable(false)
+ .setNegativeButton(
+ R.string.accessibility_shortcut_on,
+ (DialogInterface d, int which) ->
+ enableDefaultHardwareShortcut(userId))
+ .setPositiveButton(
+ R.string.accessibility_shortcut_off,
+ (DialogInterface d, int which) -> {
+ Set<String> targetServices =
+ ShortcutUtils.getShortcutTargetsFromSettings(
+ mContext, HARDWARE, userId);
+ am.enableShortcutsForTargets(
+ false, HARDWARE, targetServices, userId);
+ // If canceled, treat as if the dialog has never been shown
+ Settings.Secure.putIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
+ DialogStatus.NOT_SHOWN,
userId);
- if (Flags.migrateEnableShortcuts()) {
- am.enableShortcutsForTargets(
- false, HARDWARE, targetServices, userId);
- } else {
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, "",
- userId);
- ShortcutUtils.updateInvisibleToggleAccessibilityServiceEnableState(
- mContext, targetServices, userId);
- }
- // If canceled, treat as if the dialog has never been shown
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
- DialogStatus.NOT_SHOWN, userId);
- })
- .setOnCancelListener((DialogInterface d) -> {
- // If canceled, treat as if the dialog has never been shown
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
- DialogStatus.NOT_SHOWN, userId);
- })
- .create();
+ })
+ .setOnCancelListener(
+ (DialogInterface d) -> {
+ // If canceled, treat as if the dialog has never been shown
+ Settings.Secure.putIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
+ DialogStatus.NOT_SHOWN,
+ userId);
+ })
+ .create();
return alertDialog;
}
@@ -531,14 +532,8 @@
// Default service is invalid, so nothing we can do here.
return;
}
- if (Flags.migrateEnableShortcuts()) {
- accessibilityManager.enableShortcutsForTargets(true, HARDWARE,
- Set.of(defaultServiceComponent.flattenToString()), userId);
- } else {
- Settings.Secure.putStringForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
- defaultServiceComponent.flattenToString(), userId);
- }
+ accessibilityManager.enableShortcutsForTargets(true, HARDWARE,
+ Set.of(defaultServiceComponent.flattenToString()), userId);
}
private boolean performTtsPrompt(AlertDialog alertDialog) {
diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
index a753110..f0582d0 100644
--- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
+++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTarget.java
@@ -19,8 +19,6 @@
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.HARDWARE;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
-import static com.android.internal.accessibility.util.ShortcutUtils.optInValueToSettings;
-import static com.android.internal.accessibility.util.ShortcutUtils.optOutValueFromSettings;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -31,7 +29,6 @@
import android.os.UserHandle;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.Flags;
import com.android.internal.accessibility.common.ShortcutConstants;
import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
@@ -118,18 +115,9 @@
@Override
public void onCheckedChanged(boolean isChecked) {
setShortcutEnabled(isChecked);
- if (Flags.migrateEnableShortcuts()) {
- final AccessibilityManager am =
- getContext().getSystemService(AccessibilityManager.class);
- am.enableShortcutsForTargets(
- isChecked, getShortcutType(), Set.of(mId), UserHandle.myUserId());
- } else {
- if (isChecked) {
- optInValueToSettings(getContext(), getShortcutType(), getId());
- } else {
- optOutValueFromSettings(getContext(), getShortcutType(), getId());
- }
- }
+ final AccessibilityManager am = getContext().getSystemService(AccessibilityManager.class);
+ am.enableShortcutsForTargets(
+ isChecked, getShortcutType(), Set.of(mId), UserHandle.myUserId());
}
public void setStateDescription(CharSequence stateDescription) {
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index f78bc92..a6466c5 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -16,7 +16,6 @@
package com.android.internal.accessibility;
-import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS;
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN;
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE;
@@ -64,7 +63,6 @@
import android.os.Handler;
import android.os.Message;
import android.os.Vibrator;
-import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
@@ -423,7 +421,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void testClickingDisableButtonInDialog_shouldClearShortcutId() throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
configureValidShortcutService();
@@ -446,58 +443,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
- public void testClickingDisableButtonInDialog_shouldClearShortcutId_old() throws Exception {
- configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
- configureValidShortcutService();
- Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
- AccessibilityShortcutController.DialogStatus.NOT_SHOWN);
- getController().performAccessibilityShortcut();
-
- ArgumentCaptor<DialogInterface.OnClickListener> captor =
- ArgumentCaptor.forClass(DialogInterface.OnClickListener.class);
- verify(mAlertDialogBuilder).setPositiveButton(eq(R.string.accessibility_shortcut_off),
- captor.capture());
- captor.getValue().onClick(null, DialogInterface.BUTTON_POSITIVE);
-
- assertThat(
- Settings.Secure.getString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)
- ).isEmpty();
- assertThat(Settings.Secure.getInt(
- mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)).isEqualTo(
- AccessibilityShortcutController.DialogStatus.NOT_SHOWN);
- }
-
- @Test
- @EnableFlags(Flags.FLAG_UPDATE_ALWAYS_ON_A11Y_SERVICE)
- @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
- public void turnOffVolumeShortcutForAlwaysOnA11yService_shouldTurnOffA11yService()
- throws Exception {
- configureApplicationTargetSdkVersion(Build.VERSION_CODES.R);
- turnOffVolumeKeyShortcutForA11yService(/* alwaysOnService= */ true);
-
- assertThat(
- Settings.Secure.getString(mContentResolver, ENABLED_ACCESSIBILITY_SERVICES)
- ).isEmpty();
- }
-
- @Test
- @EnableFlags(Flags.FLAG_UPDATE_ALWAYS_ON_A11Y_SERVICE)
- @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
- public void turnOffVolumeShortcutForAlwaysOnA11yService_hasOtherTypesShortcut_shouldNotTurnOffA11yService()
- throws Exception {
- configureApplicationTargetSdkVersion(Build.VERSION_CODES.R);
- Settings.Secure.putString(
- mContentResolver, ACCESSIBILITY_BUTTON_TARGETS, SERVICE_NAME_STRING);
-
- turnOffVolumeKeyShortcutForA11yService(/* alwaysOnService= */ true);
-
- assertThat(
- Settings.Secure.getString(mContentResolver, ENABLED_ACCESSIBILITY_SERVICES)
- ).isEqualTo(SERVICE_NAME_STRING);
- }
-
- @Test
public void turnOffVolumeShortcutForStandardA11yService_shouldNotTurnOffA11yService()
throws Exception {
turnOffVolumeKeyShortcutForA11yService(/* alwaysOnService= */ false);
@@ -530,7 +475,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void testTurnOnDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOn()
throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
@@ -554,30 +498,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
- public void testTurnOnDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOn_old()
- throws Exception {
- configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
- configureDefaultAccessibilityService();
- Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
- AccessibilityShortcutController.DialogStatus.NOT_SHOWN);
- getController().performAccessibilityShortcut();
-
- ArgumentCaptor<DialogInterface.OnClickListener> captor =
- ArgumentCaptor.forClass(DialogInterface.OnClickListener.class);
- verify(mAlertDialogBuilder).setNegativeButton(eq(R.string.accessibility_shortcut_on),
- captor.capture());
- captor.getValue().onClick(null, DialogInterface.BUTTON_NEGATIVE);
-
- assertThat(Settings.Secure.getString(mContentResolver,
- ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)).isEqualTo(SERVICE_NAME_STRING);
- assertThat(Settings.Secure.getInt(
- mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)).isEqualTo(
- AccessibilityShortcutController.DialogStatus.SHOWN);
- }
-
- @Test
- @EnableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void testTurnOffDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOff()
throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
@@ -601,29 +521,6 @@
}
@Test
- @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
- public void testTurnOffDefaultA11yServiceInDialog_defaultServiceShortcutTurnsOff_old()
- throws Exception {
- configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
- configureDefaultAccessibilityService();
- Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
- AccessibilityShortcutController.DialogStatus.NOT_SHOWN);
- getController().performAccessibilityShortcut();
-
- ArgumentCaptor<DialogInterface.OnClickListener> captor =
- ArgumentCaptor.forClass(DialogInterface.OnClickListener.class);
- verify(mAlertDialogBuilder).setPositiveButton(eq(R.string.accessibility_shortcut_off),
- captor.capture());
- captor.getValue().onClick(null, DialogInterface.BUTTON_POSITIVE);
-
- assertThat(Settings.Secure.getString(mContentResolver,
- ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)).isEmpty();
- assertThat(Settings.Secure.getInt(
- mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN)).isEqualTo(
- AccessibilityShortcutController.DialogStatus.NOT_SHOWN);
- }
-
- @Test
public void testOnAccessibilityShortcut_afterDialogShown_shouldCallServer() throws Exception {
configureShortcutEnabled(ENABLED_EXCEPT_LOCK_SCREEN);
configureValidShortcutService();
@@ -638,9 +535,7 @@
}
@Test
- @EnableFlags({
- Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS,
- Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE})
+ @EnableFlags(Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE)
public void testOnAccessibilityShortcut_settingNull_dialogShown_enablesDefaultShortcut()
throws Exception {
configureDefaultAccessibilityService();
@@ -658,24 +553,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_RESTORE_A11Y_SHORTCUT_TARGET_SERVICE)
- @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
- public void testOnAccessibilityShortcut_settingNull_dialogShown_writesDefaultSetting()
- throws Exception {
- configureDefaultAccessibilityService();
- Settings.Secure.putInt(mContentResolver, ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
- AccessibilityShortcutController.DialogStatus.SHOWN);
- // Setting is only `null` during SUW.
- Settings.Secure.putString(mContentResolver, ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, null);
- getController().performAccessibilityShortcut();
-
- assertThat(Settings.Secure.getString(mContentResolver,
- ACCESSIBILITY_SHORTCUT_TARGET_SERVICE)).isEqualTo(SERVICE_NAME_STRING);
- verify(mAccessibilityManagerService).performAccessibilityShortcut(
- Display.DEFAULT_DISPLAY, HARDWARE, null);
- }
-
- @Test
public void getFrameworkFeatureMap_shouldBeUnmodifiable() {
final Map<ComponentName, AccessibilityShortcutController.FrameworkFeatureInfo>
frameworkFeatureMap =
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java b/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java
index 5339d91..acf7dbc 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/dialog/InvisibleToggleAccessibilityServiceTargetTest.java
@@ -33,12 +33,7 @@
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
-import android.provider.Settings;
import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.Flags;
import android.view.accessibility.IAccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -68,8 +63,6 @@
@RunWith(AndroidJUnit4.class)
public class InvisibleToggleAccessibilityServiceTargetTest {
@Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
- @Rule
public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule();
@Mock
private IAccessibilityManager mAccessibilityManagerService;
@@ -117,7 +110,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void onCheckedChanged_true_callA11yManagerToUpdateShortcuts() throws Exception {
mSut.onCheckedChanged(true);
@@ -130,7 +122,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
public void onCheckedChanged_false_callA11yManagerToUpdateShortcuts() throws Exception {
mSut.onCheckedChanged(false);
verify(mAccessibilityManagerService).enableShortcutsForTargets(
@@ -140,86 +131,4 @@
anyInt());
assertThat(mListCaptor.getValue()).containsExactly(ALWAYS_ON_SERVICE_COMPONENT_NAME);
}
-
- @Test
- @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
- public void onCheckedChanged_turnOnShortcut_hasOtherShortcut_serviceKeepsOn() {
- enableA11yService(/* enable= */ true);
- addShortcutForA11yService(
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, /* add= */ false);
- addShortcutForA11yService(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, /* add= */ true);
-
- mSut.onCheckedChanged(/* isChecked= */ true);
-
- assertA11yServiceState(/* enabled= */ true);
- }
-
- @Test
- @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
- public void onCheckedChanged_turnOnShortcut_noOtherShortcut_shouldTurnOnService() {
- enableA11yService(/* enable= */ false);
- addShortcutForA11yService(
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, /* add= */ false);
- addShortcutForA11yService(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, /* add= */ false);
-
- mSut.onCheckedChanged(/* isChecked= */ true);
-
- assertA11yServiceState(/* enabled= */ true);
- }
-
- @Test
- @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
- public void onCheckedChanged_turnOffShortcut_hasOtherShortcut_serviceKeepsOn() {
- enableA11yService(/* enable= */ true);
- addShortcutForA11yService(
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, /* add= */ true);
- addShortcutForA11yService(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, /* add= */ true);
-
- mSut.onCheckedChanged(/* isChecked= */ false);
-
- assertA11yServiceState(/* enabled= */ true);
- }
-
- @Test
- @DisableFlags(Flags.FLAG_MIGRATE_ENABLE_SHORTCUTS)
- public void onCheckedChanged_turnOffShortcut_noOtherShortcut_shouldTurnOffService() {
- enableA11yService(/* enable= */ true);
- addShortcutForA11yService(
- Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE, /* add= */ true);
- addShortcutForA11yService(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS, /* add= */ false);
-
- mSut.onCheckedChanged(/* isChecked= */ false);
-
- assertA11yServiceState(/* enabled= */ false);
- }
-
- private void enableA11yService(boolean enable) {
- Settings.Secure.putString(
- mContextSpy.getContentResolver(),
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
- enable ? ALWAYS_ON_SERVICE_COMPONENT_NAME : "");
- }
-
- private void addShortcutForA11yService(String shortcutKey, boolean add) {
- Settings.Secure.putString(
- mContextSpy.getContentResolver(),
- shortcutKey,
- add ? ALWAYS_ON_SERVICE_COMPONENT_NAME : "");
- }
-
- private void assertA11yServiceState(boolean enabled) {
- if (enabled) {
- assertThat(
- Settings.Secure.getString(
- mContextSpy.getContentResolver(),
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)
- ).contains(ALWAYS_ON_SERVICE_COMPONENT_NAME);
- } else {
- assertThat(
- Settings.Secure.getString(
- mContextSpy.getContentResolver(),
- Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES)
- ).doesNotContain(ALWAYS_ON_SERVICE_COMPONENT_NAME);
- }
- }
}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 897fc54..dcaab8e 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -607,6 +607,8 @@
<!-- Permission required for CTS test - IntrusionDetectionManagerTest -->
<permission name="android.permission.READ_INTRUSION_DETECTION_STATE" />
<permission name="android.permission.MANAGE_INTRUSION_DETECTION_STATE" />
+ <!-- Permissions required for CTS test - BugreportManagerTest -->
+ <permission name="android.permission.CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
index 3508ece..7507e04 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.os.Handler;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -38,6 +39,7 @@
import com.android.wm.shell.common.pip.SizeSpecSource;
import com.android.wm.shell.dagger.WMShellBaseModule;
import com.android.wm.shell.dagger.WMSingleton;
+import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.pip2.phone.PhonePipMenuController;
import com.android.wm.shell.pip2.phone.PipController;
import com.android.wm.shell.pip2.phone.PipMotionHelper;
@@ -128,8 +130,11 @@
static PipScheduler providePipScheduler(Context context,
PipBoundsState pipBoundsState,
@ShellMainThread ShellExecutor mainExecutor,
- PipTransitionState pipTransitionState) {
- return new PipScheduler(context, pipBoundsState, mainExecutor, pipTransitionState);
+ PipTransitionState pipTransitionState,
+ Optional<DesktopRepository> desktopRepositoryOptional,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
+ return new PipScheduler(context, pipBoundsState, mainExecutor, pipTransitionState,
+ desktopRepositoryOptional, rootTaskDisplayAreaOrganizer);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
index 5438a01..7145e06 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.pip2.phone;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP;
@@ -25,6 +26,7 @@
import android.graphics.Matrix;
import android.graphics.Rect;
import android.view.SurfaceControl;
+import android.window.DisplayAreaInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -33,13 +35,19 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
+import com.android.window.flags.Flags;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
+import java.util.Objects;
+import java.util.Optional;
+
/**
* Scheduler for Shell initiated PiP transitions and animations.
*/
@@ -50,6 +58,8 @@
private final PipBoundsState mPipBoundsState;
private final ShellExecutor mMainExecutor;
private final PipTransitionState mPipTransitionState;
+ private final Optional<DesktopRepository> mDesktopRepositoryOptional;
+ private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
private PipTransitionController mPipTransitionController;
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
@@ -61,11 +71,15 @@
public PipScheduler(Context context,
PipBoundsState pipBoundsState,
ShellExecutor mainExecutor,
- PipTransitionState pipTransitionState) {
+ PipTransitionState pipTransitionState,
+ Optional<DesktopRepository> desktopRepositoryOptional,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
mContext = context;
mPipBoundsState = pipBoundsState;
mMainExecutor = mainExecutor;
mPipTransitionState = pipTransitionState;
+ mDesktopRepositoryOptional = desktopRepositoryOptional;
+ mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mSurfaceControlTransactionFactory =
new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
@@ -87,7 +101,7 @@
wct.setBounds(pipTaskToken, null);
// if we are hitting a multi-activity case
// windowing mode change will reparent to original host task
- wct.setWindowingMode(pipTaskToken, WINDOWING_MODE_UNDEFINED);
+ wct.setWindowingMode(pipTaskToken, getOutPipWindowingMode());
return wct;
}
@@ -241,6 +255,47 @@
maybeUpdateMovementBounds();
}
+ /** Returns whether the display is in freeform windowing mode. */
+ private boolean isDisplayInFreeform() {
+ final DisplayAreaInfo tdaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(
+ Objects.requireNonNull(mPipTransitionState.getPipTaskInfo()).displayId);
+ if (tdaInfo != null) {
+ return tdaInfo.configuration.windowConfiguration.getWindowingMode()
+ == WINDOWING_MODE_FREEFORM;
+ }
+ return false;
+ }
+
+ /** Returns whether PiP is exiting while we're in desktop mode. */
+ private boolean isPipExitingToDesktopMode() {
+ return Flags.enableDesktopWindowingPip() && mDesktopRepositoryOptional.isPresent()
+ && (mDesktopRepositoryOptional.get().getVisibleTaskCount(
+ Objects.requireNonNull(mPipTransitionState.getPipTaskInfo()).displayId) > 0
+ || isDisplayInFreeform());
+ }
+
+ /**
+ * The windowing mode to restore to when resizing out of PIP direction. Defaults to undefined
+ * and can be overridden to restore to an alternate windowing mode.
+ */
+ private int getOutPipWindowingMode() {
+ // If we are exiting PiP while the device is in Desktop mode (the task should expand to
+ // freeform windowing mode):
+ // 1) If the display windowing mode is freeform, set windowing mode to undefined so it will
+ // resolve the windowing mode to the display's windowing mode.
+ // 2) If the display windowing mode is not freeform, set windowing mode to freeform.
+ if (isPipExitingToDesktopMode()) {
+ if (isDisplayInFreeform()) {
+ return WINDOWING_MODE_UNDEFINED;
+ } else {
+ return WINDOWING_MODE_FREEFORM;
+ }
+ }
+
+ // By default, or if the task is going to fullscreen, reset the windowing mode to undefined.
+ return WINDOWING_MODE_UNDEFINED;
+ }
+
@VisibleForTesting
void setSurfaceControlTransactionFactory(
@NonNull PipSurfaceTransactionHelper.SurfaceControlTransactionFactory factory) {
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 265bb25..07c157b 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
@@ -501,6 +501,7 @@
/**
* Deactivates main stage by removing the stage from the top level split root (usually when a
* task underneath gets removed from the stage root).
+ * This function should always be called as part of exiting split screen.
* @param stageToTop stage which we want to put on top
*/
private void deactivateSplit(WindowContainerTransaction wct, @StageType int stageToTop) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
index cab6252..57b6d7f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipSchedulerTest.java
@@ -42,8 +42,10 @@
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.pip.PipBoundsState;
+import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.pip.PipTransitionController;
import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
@@ -56,6 +58,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
/**
* Unit test against {@link PipScheduler}
*/
@@ -79,6 +83,8 @@
@Mock private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mMockFactory;
@Mock private SurfaceControl.Transaction mMockTransaction;
@Mock private PipAlphaAnimator mMockAlphaAnimator;
+ @Mock private Optional<DesktopRepository> mMockOptionalDesktopRepository;
+ @Mock private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
@Captor private ArgumentCaptor<Runnable> mRunnableArgumentCaptor;
@Captor private ArgumentCaptor<WindowContainerTransaction> mWctArgumentCaptor;
@@ -96,7 +102,8 @@
.thenReturn(mMockTransaction);
mPipScheduler = new PipScheduler(mMockContext, mMockPipBoundsState, mMockMainExecutor,
- mMockPipTransitionState);
+ mMockPipTransitionState, mMockOptionalDesktopRepository,
+ mRootTaskDisplayAreaOrganizer);
mPipScheduler.setPipTransitionController(mMockPipTransitionController);
mPipScheduler.setSurfaceControlTransactionFactory(mMockFactory);
mPipScheduler.setPipAlphaAnimatorSupplier((context, leash, tx, direction) ->
diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java
index 3fac74b..7b0bd04 100644
--- a/media/java/android/media/quality/MediaQualityContract.java
+++ b/media/java/android/media/quality/MediaQualityContract.java
@@ -74,6 +74,130 @@
*/
public static final String PARAMETER_SATURATION = "saturation";
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_COLOR = "color";
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_HUE = "hue";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_BACKLIGHT = "backlight";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_COLOR_TUNER_BRIGHTNESS = "color_tuner_brightness";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_COLOR_TUNER_SATURATION = "color_tuner_saturation";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_COLOR_TUNER_HUE = "color_tuner_hue";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_COLOR_TUNER_REDO_FFSET = "color_tuner_red_offset";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_COLOR_TUNER_GREEN_OFFSET = "color_tuner_green_offset";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_COLOR_TUNER_BLUE_OFFSET = "color_tuner_blue_offset";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_COLOR_TUNER_RED_GAIN = "color_tuner_red_gain";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_COLOR_TUNER_GREEN_GAIN = "color_tuner_green_gain";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_COLOR_TUNER_BLUE_GAIN = "color_tuner_blue_gain";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_AI_PQ = "ai_pq";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_AI_SUPER_RESOLUTION = "ai_super_resolution";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_NOISE_REDUCTION = "noise_reduction";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_MPEG_NOISE_REDUCTION = "mpeg_noise_reduction";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_FLESH_TONE = "flesh_tone";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DECONTOUR = "decontour";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DYNAMIC_LUMA_CONTROL = "dynamic_luma_control";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_FILM_MODE = "film_mode";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_BLACK_STRETCH = "black_stretch";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_BLUE_STRETCH = "blue_stretch";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_COLOR_TUNE = "color_tune";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_COLOR_TEMPERATURE = "color_temperature";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_GLOBAL_DIMMING = "global_dimming";
+
private PictureQuality() {
}
}
@@ -105,6 +229,129 @@
*/
public static final String PARAMETER_TREBLE = "treble";
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_SOUND_MODE = "sound_mode";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_SURROUND_SOUND = "surround_sound";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_EQUALIZER_DETAIL = "equalizer_detail";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_SPEAKERS = "speakers";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_SPEAKERS_DELAY = "speakers_delay";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_EARC = "earc";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_AUTO_VOLUME_CONTROL = "auto_volume_control";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DOWN_MIX_MODE = "down_mix_mode";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DTS_DRC = "dts_drc";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DOLBY_AUDIO_PROCESSING = "dolby_audio_processing";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_SOUND_MODE =
+ "dolby_audio_processing_sound_mode";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_VOLUME_LEVELER =
+ "dolby_audio_processing_volume_leveler";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_SURROUND_VIRTUALIZER =
+ "dolby_audio_processing_surround_virtualizer";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_DOLBY_ATMOS =
+ "dolby_audio_processing_dolby_atmos";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DIALOGUE_ENHANCER = "dialogue_enhancer";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DTS_VIRTUAL_X = "dts_virtual_x";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DTS_VIRTUAL_X_TBHDX = "dts_virtual_x_tbhdx";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DTS_VIRTUAL_X_LIMITER = "dts_virtual_x_limiter";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DTS_VIRTUAL_X_TRU_SURROUND_X =
+ "dts_virtual_x_tru_surround_x";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DTS_VIRTUAL_X_TRU_VOLUME_HD =
+ "dts_virtual_x_tru_volume_hd";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DTS_VIRTUAL_X_DIALOG_CLARITY =
+ "dts_virtual_x_dialog_clarity";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DTS_VIRTUAL_X_DEFINITION = "dts_virtual_x_definition";
+
+ /**
+ * @hide
+ */
+ public static final String PARAMETER_DTS_VIRTUAL_X_HEIGHT = "dts_virtual_x_height";
+
+
private SoundQuality() {
}
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0ec5571..e03afec 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -990,6 +990,9 @@
<uses-permission android:name="android.permission.health.READ_SKIN_TEMPERATURE"
android:featureFlag="android.permission.flags.replace_body_sensor_permission_enabled"/>
+ <!-- Permissions required for CTS test - BugreportManagerTest -->
+ <uses-permission android:name="android.permission.CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT" />
+
<application
android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/StatementService/src/com/android/statementservice/StatementServiceApplication.kt b/packages/StatementService/src/com/android/statementservice/StatementServiceApplication.kt
index 021a514..6af8004 100644
--- a/packages/StatementService/src/com/android/statementservice/StatementServiceApplication.kt
+++ b/packages/StatementService/src/com/android/statementservice/StatementServiceApplication.kt
@@ -30,6 +30,7 @@
// WorkManager can only schedule when the user data directories are unencrypted (after
// the user has entered their lock password.
DomainVerificationUtils.schedulePeriodicCheckUnlocked(WorkManager.getInstance(this))
+ DomainVerificationUtils.schedulePeriodicUpdateUnlocked(WorkManager.getInstance(this))
}
}
}
diff --git a/packages/StatementService/src/com/android/statementservice/domain/DomainVerificationUtils.kt b/packages/StatementService/src/com/android/statementservice/domain/DomainVerificationUtils.kt
index 6944248..157a800 100644
--- a/packages/StatementService/src/com/android/statementservice/domain/DomainVerificationUtils.kt
+++ b/packages/StatementService/src/com/android/statementservice/domain/DomainVerificationUtils.kt
@@ -22,6 +22,7 @@
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import com.android.statementservice.domain.worker.RetryRequestWorker
+import com.android.statementservice.domain.worker.UpdateVerifiedDomainsWorker
import java.time.Duration
object DomainVerificationUtils {
@@ -30,6 +31,10 @@
private const val PERIODIC_SHORT_HOURS = 24L
private const val PERIODIC_LONG_ID = "retry_long"
private const val PERIODIC_LONG_HOURS = 72L
+ private const val PERIODIC_UPDATE_ID = "update"
+ private const val PERIODIC_UPDATE_HOURS = 720L
+
+ private const val UPDATE_WORKER_ENABLED = false
/**
* In a majority of cases, the initial requests will be enough to verify domains, since they
@@ -74,4 +79,38 @@
}
}
}
+
+ /**
+ * Schedule a periodic worker to check for any updates to assetlink.json files for domains that
+ * have already been verified.
+ *
+ * Due to the potential for this worker to generate enough traffic across all android devices
+ * to overwhelm websites, this method is hardcoded to be disabled by default. It is highly
+ * recommended to not enable this worker and instead implement a custom worker that pulls
+ * updates from a caching service instead of directly from websites.
+ */
+ fun schedulePeriodicUpdateUnlocked(workManager: WorkManager) {
+ if (UPDATE_WORKER_ENABLED) {
+ workManager.apply {
+ PeriodicWorkRequestBuilder<UpdateVerifiedDomainsWorker>(
+ Duration.ofDays(
+ PERIODIC_UPDATE_HOURS
+ )
+ )
+ .setConstraints(
+ Constraints.Builder()
+ .setRequiredNetworkType(NetworkType.CONNECTED)
+ .setRequiresDeviceIdle(true)
+ .build()
+ )
+ .build()
+ .let {
+ enqueueUniquePeriodicWork(
+ PERIODIC_UPDATE_ID,
+ ExistingPeriodicWorkPolicy.KEEP, it
+ )
+ }
+ }
+ }
+ }
}
diff --git a/packages/StatementService/src/com/android/statementservice/domain/DomainVerifier.kt b/packages/StatementService/src/com/android/statementservice/domain/DomainVerifier.kt
index 6914347..c7f6c18 100644
--- a/packages/StatementService/src/com/android/statementservice/domain/DomainVerifier.kt
+++ b/packages/StatementService/src/com/android/statementservice/domain/DomainVerifier.kt
@@ -64,7 +64,8 @@
private val targetAssetCache = AssetLruCache()
- fun collectHosts(packageNames: Iterable<String>): Iterable<Triple<UUID, String, String>> {
+ fun collectHosts(packageNames: Iterable<String>, statusFilter: (Int) -> Boolean):
+ Iterable<Triple<UUID, String, Iterable<String>>> {
return packageNames.mapNotNull { packageName ->
val (domainSetId, _, hostToStateMap) = try {
manager.getDomainVerificationInfo(packageName)
@@ -74,14 +75,13 @@
} ?: return@mapNotNull null
val hostsToRetry = hostToStateMap
- .filterValues(VerifyStatus::shouldRetry)
+ .filterValues(statusFilter)
.takeIf { it.isNotEmpty() }
?.map { it.key }
?: return@mapNotNull null
- hostsToRetry.map { Triple(domainSetId, packageName, it) }
+ Triple(domainSetId, packageName, hostsToRetry)
}
- .flatten()
}
suspend fun verifyHost(
diff --git a/packages/StatementService/src/com/android/statementservice/domain/VerifyStatus.kt b/packages/StatementService/src/com/android/statementservice/domain/VerifyStatus.kt
index 2193ec5..c771da3 100644
--- a/packages/StatementService/src/com/android/statementservice/domain/VerifyStatus.kt
+++ b/packages/StatementService/src/com/android/statementservice/domain/VerifyStatus.kt
@@ -49,7 +49,7 @@
return false
}
- val status = values().find { it.value == state } ?: return true
+ val status = entries.find { it.value == state } ?: return true
return when (status) {
SUCCESS,
FAILURE_LEGACY_UNSUPPORTED_WILDCARD,
@@ -62,5 +62,20 @@
FAILURE_REDIRECT -> true
}
}
+
+ fun canUpdate(state: Int): Boolean {
+ if (state == DomainVerificationInfo.STATE_UNMODIFIABLE) {
+ return false
+ }
+
+ val status = entries.find { it.value == state }
+ return when (status) {
+ SUCCESS,
+ FAILURE_LEGACY_UNSUPPORTED_WILDCARD,
+ FAILURE_REJECTED_BY_SERVER,
+ UNKNOWN -> true
+ else -> false
+ }
+ }
}
}
diff --git a/packages/StatementService/src/com/android/statementservice/domain/worker/PeriodicUpdateWorker.kt b/packages/StatementService/src/com/android/statementservice/domain/worker/PeriodicUpdateWorker.kt
new file mode 100644
index 0000000..7ec6e6c
--- /dev/null
+++ b/packages/StatementService/src/com/android/statementservice/domain/worker/PeriodicUpdateWorker.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 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.statementservice.domain.worker
+
+import android.content.Context
+import android.content.UriRelativeFilterGroup
+import android.content.pm.verify.domain.DomainVerificationManager
+import androidx.work.ListenableWorker
+import androidx.work.WorkerParameters
+import com.android.statementservice.domain.VerifyStatus
+import com.android.statementservice.utils.AndroidUtils
+import com.android.statementservice.utils.StatementUtils
+import kotlinx.coroutines.async
+import kotlinx.coroutines.awaitAll
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.isActive
+
+abstract class PeriodicUpdateWorker(
+ appContext: Context,
+ params: WorkerParameters
+) : BaseRequestWorker(appContext, params) {
+
+ data class VerifyResult(
+ val host: String,
+ val status: VerifyStatus,
+ val groups: List<UriRelativeFilterGroup>
+ )
+
+ protected suspend fun updateDomainVerificationStatus(verifyStatusFilter: (Int) -> Boolean):
+ ListenableWorker.Result {
+ return coroutineScope {
+ if (!AndroidUtils.isReceiverV2Enabled(appContext)) {
+ return@coroutineScope Result.success()
+ }
+
+ val packageNames = verificationManager.queryValidVerificationPackageNames()
+
+ verifier.collectHosts(packageNames, verifyStatusFilter)
+ .map { (domainSetId, packageName, hosts) ->
+ hosts.map { host ->
+ async {
+ if (isActive && !isStopped) {
+ val (_, status, statement) = verifier.verifyHost(
+ host,
+ packageName,
+ params.network
+ )
+ val groups = statement?.dynamicAppLinkComponents.orEmpty().map {
+ StatementUtils.createUriRelativeFilterGroup(it)
+ }
+ VerifyResult(host, status, groups)
+ } else {
+ // If the job gets cancelled, stop the remaining hosts, but continue the
+ // job to commit the results for hosts that were already requested.
+ null
+ }
+ }
+ }.awaitAll().filterNotNull().groupBy { it.status }
+ .forEach { (status, results) ->
+ val error = verificationManager.setDomainVerificationStatus(
+ domainSetId,
+ results.map { it.host }.toSet(),
+ status.value
+ )
+ if (error == DomainVerificationManager.STATUS_OK
+ && status == VerifyStatus.SUCCESS
+ ) {
+ updateUriRelativeFilterGroups(
+ packageName,
+ results.associateBy({ it.host }, { it.groups })
+ )
+ }
+ }
+ }
+
+ // Succeed regardless of results since this retry is best effort and not required
+ Result.success()
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/StatementService/src/com/android/statementservice/domain/worker/RetryRequestWorker.kt b/packages/StatementService/src/com/android/statementservice/domain/worker/RetryRequestWorker.kt
index f83601a..e8b4df9 100644
--- a/packages/StatementService/src/com/android/statementservice/domain/worker/RetryRequestWorker.kt
+++ b/packages/StatementService/src/com/android/statementservice/domain/worker/RetryRequestWorker.kt
@@ -17,18 +17,9 @@
package com.android.statementservice.domain.worker
import android.content.Context
-import android.content.UriRelativeFilterGroup
-import android.content.pm.verify.domain.DomainVerificationManager
import androidx.work.NetworkType
import androidx.work.WorkerParameters
import com.android.statementservice.domain.VerifyStatus
-import com.android.statementservice.utils.AndroidUtils
-import com.android.statementservice.utils.StatementUtils
-import kotlinx.coroutines.async
-import kotlinx.coroutines.awaitAll
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.isActive
-import java.util.UUID
/**
* Scheduled every 24 hours with [NetworkType.CONNECTED] and every 72 hours without any constraints
@@ -37,63 +28,7 @@
class RetryRequestWorker(
appContext: Context,
params: WorkerParameters
-) : BaseRequestWorker(appContext, params) {
+) : PeriodicUpdateWorker(appContext, params) {
- data class VerifyResult(
- val domainSetId: UUID,
- val host: String,
- val status: VerifyStatus,
- val packageName: String,
- val groups: List<UriRelativeFilterGroup>
- )
-
- override suspend fun doWork() = coroutineScope {
- if (!AndroidUtils.isReceiverV2Enabled(appContext)) {
- return@coroutineScope Result.success()
- }
-
- val packageNames = verificationManager.queryValidVerificationPackageNames()
-
- verifier.collectHosts(packageNames)
- .map { (domainSetId, packageName, host) ->
- async {
- if (isActive && !isStopped) {
- val (_, status, statement) = verifier.verifyHost(host, packageName, params.network)
- val groups = statement?.dynamicAppLinkComponents.orEmpty().map {
- StatementUtils.createUriRelativeFilterGroup(it)
- }
- VerifyResult(domainSetId, host, status, packageName, groups)
- } else {
- // If the job gets cancelled, stop the remaining hosts, but continue the
- // job to commit the results for hosts that were already requested.
- null
- }
- }
- }
- .awaitAll()
- .filterNotNull() // TODO(b/159952358): Fast fail packages which can't be retrieved.
- .groupBy { it.packageName }
- .forEach { (packageName, resultsByName) ->
- val groupUpdates = mutableMapOf<String, List<UriRelativeFilterGroup>>()
- resultsByName.groupBy { it.domainSetId }
- .forEach { (domainSetId, resultsById) ->
- resultsById.groupBy { it.status }
- .forEach { (status, verifyResults) ->
- val error = verificationManager.setDomainVerificationStatus(
- domainSetId,
- verifyResults.map(VerifyResult::host).toSet(),
- status.value
- )
- if (error == DomainVerificationManager.STATUS_OK
- && status == VerifyStatus.SUCCESS) {
- verifyResults.forEach { groupUpdates[it.host] = it.groups }
- }
- }
- }
- updateUriRelativeFilterGroups(packageName, groupUpdates)
- }
-
- // Succeed regardless of results since this retry is best effort and not required
- Result.success()
- }
+ override suspend fun doWork() = updateDomainVerificationStatus(VerifyStatus::shouldRetry)
}
diff --git a/packages/StatementService/src/com/android/statementservice/domain/worker/UpdateVerifiedDomainsWorker.kt b/packages/StatementService/src/com/android/statementservice/domain/worker/UpdateVerifiedDomainsWorker.kt
new file mode 100644
index 0000000..c6f40c8
--- /dev/null
+++ b/packages/StatementService/src/com/android/statementservice/domain/worker/UpdateVerifiedDomainsWorker.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 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.statementservice.domain.worker
+
+import android.content.Context
+import androidx.work.WorkerParameters
+import com.android.statementservice.domain.VerifyStatus
+
+class UpdateVerifiedDomainsWorker(
+ appContext: Context,
+ params: WorkerParameters
+) : PeriodicUpdateWorker(appContext, params) {
+
+ override suspend fun doWork() = updateDomainVerificationStatus(VerifyStatus::canUpdate)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index ae92d259..85f549d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -21,6 +21,7 @@
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.Modifier
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
@@ -71,9 +72,7 @@
}
@Composable
- override fun SceneScope.Content(
- modifier: Modifier,
- ) =
+ override fun SceneScope.Content(modifier: Modifier) =
BouncerScene(
viewModel = rememberViewModel("BouncerScene") { contentViewModelFactory.create() },
dialogFactory = dialogFactory,
@@ -89,6 +88,8 @@
) {
val backgroundColor = MaterialTheme.colorScheme.surface
+ DisposableEffect(Unit) { onDispose { viewModel.onUiDestroyed() } }
+
Box(modifier) {
Canvas(Modifier.element(Bouncer.Elements.Background).fillMaxSize()) {
drawRect(color = backgroundColor)
@@ -101,7 +102,7 @@
dialogFactory,
Modifier.element(Bouncer.Elements.Content)
.sysuiResTag(Bouncer.TestTags.Root)
- .fillMaxSize()
+ .fillMaxSize(),
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt
index 3bf4460..94f6769 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModelTest.kt
@@ -34,6 +34,11 @@
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.shared.model.DismissAction
+import com.android.systemui.keyguard.shared.model.KeyguardDone
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.res.R
@@ -212,6 +217,25 @@
assertThat(isFoldSplitRequired).isTrue()
}
+ @Test
+ fun onUiDestroyed_clearsPendingDismissAction() =
+ kosmos.runTest {
+ val dismissAction by collectLastValue(fakeKeyguardRepository.dismissAction)
+ fakeKeyguardRepository.setDismissAction(
+ DismissAction.RunImmediately(
+ onDismissAction = { KeyguardDone.IMMEDIATE },
+ onCancelAction = {},
+ message = "",
+ willAnimateOnLockscreen = true,
+ )
+ )
+ assertThat(dismissAction).isNotEqualTo(DismissAction.None)
+
+ underTest.onUiDestroyed()
+
+ assertThat(dismissAction).isEqualTo(DismissAction.None)
+ }
+
private fun authMethodsToTest(): List<AuthenticationMethodModel> {
return listOf(None, Pin, Password, Pattern, Sim)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
index e149687..dadcf71 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorTest.kt
@@ -35,10 +35,9 @@
import com.android.systemui.keyguard.shared.model.DismissAction
import com.android.systemui.keyguard.shared.model.KeyguardDone
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
-import com.android.systemui.power.data.repository.fakePowerRepository
-import com.android.systemui.power.shared.model.WakeSleepReason
-import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.scene.data.repository.Idle
import com.android.systemui.scene.data.repository.Transition
import com.android.systemui.scene.data.repository.setSceneTransition
@@ -222,28 +221,6 @@
}
@Test
- fun resetDismissAction() =
- testScope.runTest {
- kosmos.setSceneTransition(Idle(Scenes.Bouncer))
- var wasOnCancelInvoked = false
- startInteractor()
- keyguardRepository.setDismissAction(
- DismissAction.RunAfterKeyguardGone(
- dismissAction = {},
- onCancelAction = { wasOnCancelInvoked = true },
- message = "message",
- willAnimateOnLockscreen = true,
- )
- )
- assertThat(wasOnCancelInvoked).isFalse()
- kosmos.setSceneTransition(Idle(Scenes.Lockscreen))
- runCurrent()
-
- assertThat(wasOnCancelInvoked).isTrue()
- assertThat(keyguardRepository.dismissAction.value).isEqualTo(DismissAction.None)
- }
-
- @Test
fun doNotResetDismissActionOnUnlockedShade() =
testScope.runTest {
kosmos.setSceneTransition(Idle(Scenes.Bouncer))
@@ -272,37 +249,6 @@
}
@Test
- fun resetDismissAction_onBouncer_OnAsleep() =
- testScope.runTest {
- kosmos.setSceneTransition(Idle(Scenes.Bouncer))
- kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
- AuthenticationMethodModel.None
- )
- var wasOnCancelInvoked = false
- startInteractor()
-
- keyguardRepository.setDismissAction(
- DismissAction.RunAfterKeyguardGone(
- dismissAction = {},
- onCancelAction = { wasOnCancelInvoked = true },
- message = "message",
- willAnimateOnLockscreen = true,
- )
- )
- assertThat(wasOnCancelInvoked).isFalse()
- kosmos.fakePowerRepository.updateWakefulness(
- rawState = WakefulnessState.ASLEEP,
- lastWakeReason = WakeSleepReason.POWER_BUTTON,
- lastSleepReason = WakeSleepReason.TIMEOUT,
- powerButtonLaunchGestureTriggered = false,
- )
- runCurrent()
-
- assertThat(wasOnCancelInvoked).isTrue()
- assertThat(keyguardRepository.dismissAction.value).isEqualTo(DismissAction.None)
- }
-
- @Test
fun setDismissAction_callsCancelRunnableOnPreviousDismissAction() =
testScope.runTest {
val dismissAction by collectLastValue(keyguardRepository.dismissAction)
@@ -410,6 +356,25 @@
assertThat(wasCancelActionInvoked).isFalse()
}
+ @Test
+ fun clearDismissAction() =
+ kosmos.runTest {
+ val dismissAction by collectLastValue(fakeKeyguardRepository.dismissAction)
+ fakeKeyguardRepository.setDismissAction(
+ DismissAction.RunImmediately(
+ onDismissAction = { KeyguardDone.IMMEDIATE },
+ onCancelAction = {},
+ message = "",
+ willAnimateOnLockscreen = true,
+ )
+ )
+ assertThat(dismissAction).isNotEqualTo(DismissAction.None)
+
+ underTest.clearDismissAction()
+
+ assertThat(dismissAction).isEqualTo(DismissAction.None)
+ }
+
private fun TestScope.startInteractor() {
testScope.backgroundScope.launchTraced(
"KeyguardDismissActionInteractorTest#startInteractor"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 0eb6203..d174484 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -190,6 +190,8 @@
private ArgumentCaptor<OnBackInvokedCallback> mBackCallbackCaptor;
@Captor
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback;
+ @Mock
+ private KeyguardDismissActionInteractor mKeyguardDismissActionInteractor;
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@@ -236,7 +238,7 @@
mKeyguardTransitionInteractor,
mock(KeyguardDismissTransitionInteractor.class),
StandardTestDispatcher(null, null),
- () -> mock(KeyguardDismissActionInteractor.class),
+ () -> mKeyguardDismissActionInteractor,
mSelectedUserInteractor,
mock(JavaAdapter.class),
() -> mSceneInteractor,
@@ -968,4 +970,33 @@
);
verify(mAlternateBouncerInteractor).hide();
}
+
+ @Test
+ public void hideAlternateBouncer_clearsDismissActionByDefault() {
+ clearInvocations(mKeyguardDismissActionInteractor);
+
+ mStatusBarKeyguardViewManager.hideAlternateBouncer(/* updateScrim= */ true);
+
+ verify(mKeyguardDismissActionInteractor).clearDismissAction();
+ }
+
+ @Test
+ public void hideAlternateBouncer_clearsDismissActionExplicitly() {
+ clearInvocations(mKeyguardDismissActionInteractor);
+
+ mStatusBarKeyguardViewManager.hideAlternateBouncer(
+ /* updateScrim= */ true, /* clearDismissAction= */ true);
+
+ verify(mKeyguardDismissActionInteractor).clearDismissAction();
+ }
+
+ @Test
+ public void hideAlternateBouncer_doNotClearDismissActionExplicitly() {
+ clearInvocations(mKeyguardDismissActionInteractor);
+
+ mStatusBarKeyguardViewManager.hideAlternateBouncer(
+ /* updateScrim= */ true, /* clearDismissAction= */ false);
+
+ verify(mKeyguardDismissActionInteractor, never()).clearDismissAction();
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index 9513c8e..5a9cbce 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -174,9 +174,20 @@
/**
* Stop showing the alternate bouncer, if showing.
+ *
+ * <p>Should be like calling {@link #hideAlternateBouncer(boolean, boolean)} with a {@code true}
+ * {@code clearDismissAction} parameter.
*/
void hideAlternateBouncer(boolean updateScrim);
+ /**
+ * Stop showing the alternate bouncer, if showing.
+ *
+ * @param updateScrim Whether to update the scrim
+ * @param clearDismissAction Whether the pending dismiss action should be cleared
+ */
+ void hideAlternateBouncer(boolean updateScrim, boolean clearDismissAction);
+
// TODO: Deprecate registerStatusBar in KeyguardViewController interface. It is currently
// only used for testing purposes in StatusBarKeyguardViewManager, and it prevents us from
// achieving complete abstraction away from where the Keyguard View is mounted.
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 3794e7b..08d3e17 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -833,9 +833,7 @@
}
// Set the surface of the SurfaceView to black to avoid users seeing the contents below the
// magnifier when the mirrored surface has an alpha less than 1.
- if (Flags.addBlackBackgroundForWindowMagnifier()) {
- mTransaction.setColor(mMirrorSurfaceView.getSurfaceControl(), COLOR_BLACK_ARRAY);
- }
+ mTransaction.setColor(mMirrorSurfaceView.getSurfaceControl(), COLOR_BLACK_ARRAY);
mTransaction.show(mMirrorSurface)
.reparent(mMirrorSurface, mMirrorSurfaceView.getSurfaceControl());
modifyWindowMagnification(false);
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
index 47d91374..5deb751 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerSceneContentViewModel.kt
@@ -23,6 +23,7 @@
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.key.type
import androidx.core.graphics.drawable.toBitmap
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.coroutines.traceCoroutine
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
@@ -35,6 +36,7 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardMediaKeyInteractor
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
@@ -48,7 +50,6 @@
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
-import com.android.app.tracing.coroutines.launchTraced as launch
/** Models UI state for the content of the bouncer scene. */
class BouncerSceneContentViewModel
@@ -67,6 +68,7 @@
private val bouncerHapticPlayer: BouncerHapticPlayer,
private val keyguardMediaKeyInteractor: KeyguardMediaKeyInteractor,
private val bouncerActionButtonInteractor: BouncerActionButtonInteractor,
+ private val keyguardDismissActionInteractor: KeyguardDismissActionInteractor,
) : ExclusiveActivatable() {
private val _selectedUserImage = MutableStateFlow<Bitmap?>(null)
val selectedUserImage: StateFlow<Bitmap?> = _selectedUserImage.asStateFlow()
@@ -421,6 +423,13 @@
}
}
+ /**
+ * Notifies that the bouncer UI has been destroyed (e.g. the composable left the composition).
+ */
+ fun onUiDestroyed() {
+ keyguardDismissActionInteractor.clearDismissAction()
+ }
+
data class DialogViewModel(
val text: String,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
index 21090c1..cc8652c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractor.kt
@@ -18,7 +18,6 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.keyguard.logging.KeyguardLogger
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
import com.android.systemui.dagger.SysUISingleton
@@ -28,15 +27,12 @@
import com.android.systemui.keyguard.shared.model.DismissAction
import com.android.systemui.keyguard.shared.model.KeyguardDone
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
-import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.log.core.LogLevel
-import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
-import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -66,8 +62,6 @@
val dismissInteractor: KeyguardDismissInteractor,
@Application private val applicationScope: CoroutineScope,
deviceUnlockedInteractor: Lazy<DeviceUnlockedInteractor>,
- powerInteractor: PowerInteractor,
- alternateBouncerInteractor: AlternateBouncerInteractor,
shadeInteractor: Lazy<ShadeInteractor>,
keyguardInteractor: Lazy<KeyguardInteractor>,
sceneInteractor: Lazy<SceneInteractor>,
@@ -144,42 +138,6 @@
}
}
- /** Flow that emits whenever we need to reset the dismiss action */
- private val resetDismissAction: Flow<Unit> =
- combine(
- if (SceneContainerFlag.isEnabled) {
- // Using currentScene instead of isFinishedIn because of a race condition that
- // forms between isFinishedIn(Gone) and isOnShadeWhileUnlocked where the latter
- // emits false before the former emits true, causing the evaluation of the
- // combine to come up with true, temporarily, before settling on false, which is
- // a valid final state. That causes an incorrect reset of the dismiss action to
- // occur before it gets executed.
- sceneInteractor
- .get()
- .currentScene
- .map { it == Scenes.Gone }
- .distinctUntilChanged()
- } else {
- transitionInteractor.isFinishedIn(
- scene = Scenes.Gone,
- stateWithoutSceneContainer = GONE,
- )
- },
- transitionInteractor.isFinishedIn(
- scene = Scenes.Bouncer,
- stateWithoutSceneContainer = PRIMARY_BOUNCER,
- ),
- alternateBouncerInteractor.isVisible,
- isOnShadeWhileUnlocked,
- powerInteractor.isAsleep,
- ) { isOnGone, isOnBouncer, isOnAltBouncer, isOnShadeWhileUnlocked, isAsleep ->
- (!isOnGone && !isOnBouncer && !isOnAltBouncer && !isOnShadeWhileUnlocked) ||
- isAsleep
- }
- .filter { it }
- .sampleFilter(dismissAction) { it !is DismissAction.None }
- .map {}
-
fun runDismissAnimationOnKeyguard(): Boolean {
return willAnimateDismissActionOnLockscreen.value
}
@@ -220,19 +178,15 @@
}
}
- launch {
- resetDismissAction.collect {
- log("resetDismissAction")
- repository.dismissAction.value.onCancelAction.run()
- clearDismissAction()
- }
- }
-
launch { repository.dismissAction.collect { log("updatedDismissAction=$it") } }
awaitCancellation()
}
}
+ fun clearDismissAction() {
+ repository.setDismissAction(DismissAction.None)
+ }
+
/** Run the dismiss action and starts the dismiss keyguard transition. */
private suspend fun runDismissAction() {
val dismissAction = repository.dismissAction.value
@@ -249,10 +203,6 @@
}
}
- private fun clearDismissAction() {
- repository.setDismissAction(DismissAction.None)
- }
-
private fun log(message: String) {
keyguardLogger.log(TAG, LogLevel.DEBUG, message)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index aef26de..bd1360f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -818,7 +818,10 @@
* dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)}
*/
public void showPrimaryBouncer(boolean scrimmed) {
- hideAlternateBouncer(false);
+ hideAlternateBouncer(
+ /* updateScrim= */ false,
+ // When the scene framework is on, don't ever clear the pending dismiss action from
+ /* clearDismissAction= */ !SceneContainerFlag.isEnabled());
if (mKeyguardStateController.isShowing() && !isBouncerShowing()) {
if (SceneContainerFlag.isEnabled()) {
mSceneInteractorLazy.get().changeScene(
@@ -1005,6 +1008,15 @@
@Override
public void hideAlternateBouncer(boolean updateScrim) {
+ hideAlternateBouncer(updateScrim, /* clearDismissAction= */ true);
+ }
+
+ @Override
+ public void hideAlternateBouncer(boolean updateScrim, boolean clearDismissAction) {
+ if (clearDismissAction) {
+ mKeyguardDismissActionInteractor.get().clearDismissAction();
+ }
+
updateAlternateBouncerShowing(mAlternateBouncerInteractor.hide() && updateScrim);
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
index e0d1d16..7254154 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelKosmos.kt
@@ -28,6 +28,7 @@
import com.android.systemui.bouncer.ui.helper.BouncerHapticPlayer
import com.android.systemui.haptics.msdl.bouncerHapticPlayer
import com.android.systemui.inputmethod.domain.interactor.inputMethodInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardDismissActionInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardMediaKeyInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -63,6 +64,7 @@
bouncerHapticPlayer = bouncerHapticPlayer,
keyguardMediaKeyInteractor = keyguardMediaKeyInteractor,
bouncerActionButtonInteractor = bouncerActionButtonInteractor,
+ keyguardDismissActionInteractor = keyguardDismissActionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
index bd841ab..09f5fd7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissActionInteractorKosmos.kt
@@ -17,14 +17,12 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.keyguard.logging.KeyguardLogger
-import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.keyguard.data.repository.keyguardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -38,8 +36,6 @@
dismissInteractor = keyguardDismissInteractor,
applicationScope = testScope.backgroundScope,
deviceUnlockedInteractor = { deviceUnlockedInteractor },
- powerInteractor = powerInteractor,
- alternateBouncerInteractor = alternateBouncerInteractor,
shadeInteractor = { shadeInteractor },
keyguardInteractor = { keyguardInteractor },
sceneInteractor = { sceneInteractor },
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 7ef6aac..e1b6c9c 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -409,10 +409,6 @@
final int eventSource = event.getSource();
final int displayId = event.getDisplayId();
if ((policyFlags & WindowManagerPolicy.FLAG_PASS_TO_USER) == 0) {
- if (!Flags.doNotResetKeyEventState()) {
- state.reset();
- clearEventStreamHandler(displayId, eventSource);
- }
if (DEBUG) {
Slog.d(TAG, "Not processing event " + event);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 0ed239e..0cbbf6d 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -240,10 +240,7 @@
}
private void clear(MotionEvent event, int policyFlags) {
- if (mState.isTouchExploring() || Flags.sendHoverEventsBasedOnEventStream()) {
- // If a touch exploration gesture is in progress send events for its end.
- sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
- }
+ sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
mDraggingPointerId = INVALID_POINTER_ID;
// Send exit to any pointers that we have delivered as part of delegating or dragging.
mDispatcher.sendUpForInjectedDownPointers(event, policyFlags);
@@ -562,10 +559,7 @@
// clear any hover events that might have been queued and never sent.
mSendHoverEnterAndMoveDelayed.clear();
mSendHoverExitDelayed.cancel();
- // If a touch exploration gesture is in progress send events for its end.
- if (mState.isTouchExploring() || Flags.sendHoverEventsBasedOnEventStream()) {
- sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
- }
+ sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
if (mState.isClear()) {
if (!mSendHoverEnterAndMoveDelayed.isPending()) {
// Queue a delayed transition to STATE_TOUCH_EXPLORING.
@@ -1599,9 +1593,7 @@
if (mEvents.size() == 0) {
return;
}
- if (Flags.sendHoverEventsBasedOnEventStream()) {
- sendHoverExitAndTouchExplorationGestureEndIfNeeded(mPolicyFlags);
- }
+ sendHoverExitAndTouchExplorationGestureEndIfNeeded(mPolicyFlags);
// Send an accessibility event to announce the touch exploration start.
mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START);
if (isSendMotionEventsEnabled()) {
diff --git a/services/autofill/features.aconfig b/services/autofill/features.aconfig
index b3fe5f2..444844121 100644
--- a/services/autofill/features.aconfig
+++ b/services/autofill/features.aconfig
@@ -37,3 +37,10 @@
description: "Improvements for Fill Dialog, including deprecation of pre-trigger API's"
bug: "336223371"
}
+
+flag {
+ name: "add_last_focused_id_to_fill_event_history"
+ namespace: "autofill"
+ description: "Adds focused id to each event that's part of the fill event history"
+ bug: "334141398"
+}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index b52c6505..cd4ace2 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -900,13 +900,13 @@
* Updates the last fill selection when an authentication was selected.
*/
void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState,
- int uiType) {
+ int uiType, @Nullable AutofillId focusedId) {
synchronized (mLock) {
if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
mEventHistory.addEvent(
new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null,
null, null, null, null, null, null,
- NO_SAVE_UI_REASON_NONE, uiType));
+ NO_SAVE_UI_REASON_NONE, uiType, focusedId));
}
}
}
@@ -915,13 +915,13 @@
* Updates the last fill selection when an dataset authentication was selected.
*/
void logDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId,
- @Nullable Bundle clientState, int uiType) {
+ @Nullable Bundle clientState, int uiType, @Nullable AutofillId focusedId) {
synchronized (mLock) {
if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) {
mEventHistory.addEvent(
new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
clientState, null, null, null, null, null, null, null, null,
- NO_SAVE_UI_REASON_NONE, uiType));
+ NO_SAVE_UI_REASON_NONE, uiType, focusedId));
}
}
}
@@ -933,7 +933,7 @@
synchronized (mLock) {
if (isValidEventLocked("logSaveShown()", sessionId)) {
mEventHistory.addEvent(new Event(Event.TYPE_SAVE_SHOWN, null, clientState, null,
- null, null, null, null, null, null, null));
+ null, null, null, null, null, null, null, /* focusedId= */ null));
}
}
}
@@ -942,13 +942,13 @@
* Updates the last fill response when a dataset was selected.
*/
void logDatasetSelected(@Nullable String selectedDataset, int sessionId,
- @Nullable Bundle clientState, int uiType) {
+ @Nullable Bundle clientState, int uiType, @Nullable AutofillId focusedId) {
synchronized (mLock) {
if (isValidEventLocked("logDatasetSelected()", sessionId)) {
mEventHistory.addEvent(
new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null,
null, null, null, null, null, null, null, NO_SAVE_UI_REASON_NONE,
- uiType));
+ uiType, focusedId));
}
}
}
@@ -956,13 +956,14 @@
/**
* Updates the last fill response when a dataset is shown.
*/
- void logDatasetShown(int sessionId, @Nullable Bundle clientState, int uiType) {
+ void logDatasetShown(int sessionId, @Nullable Bundle clientState, int uiType,
+ @Nullable AutofillId focusedId) {
synchronized (mLock) {
if (isValidEventLocked("logDatasetShown", sessionId)) {
mEventHistory.addEvent(
new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
null, null, null, null, null, NO_SAVE_UI_REASON_NONE,
- uiType));
+ uiType, focusedId));
}
}
}
@@ -970,7 +971,8 @@
/**
* Updates the last fill response when a view was entered.
*/
- void logViewEntered(int sessionId, @Nullable Bundle clientState) {
+ void logViewEntered(int sessionId, @Nullable Bundle clientState,
+ @Nullable AutofillId focusedId) {
synchronized (mLock) {
if (!isValidEventLocked("logViewEntered", sessionId)) {
return;
@@ -988,7 +990,7 @@
mEventHistory.addEvent(
new Event(Event.TYPE_VIEW_REQUESTED_AUTOFILL, null, clientState, null,
- null, null, null, null, null, null, null));
+ null, null, null, null, null, null, null, focusedId));
}
}
@@ -1001,7 +1003,8 @@
}
mAugmentedAutofillEventHistory.addEvent(
new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
- clientState, null, null, null, null, null, null, null, null));
+ clientState, null, null, null, null, null, null, null, null,
+ /* focusedId= */ null));
}
}
@@ -1014,7 +1017,7 @@
}
mAugmentedAutofillEventHistory.addEvent(
new Event(Event.TYPE_DATASET_SELECTED, suggestionId, clientState, null, null,
- null, null, null, null, null, null));
+ null, null, null, null, null, null, /* focusedId= */ null));
}
}
@@ -1029,7 +1032,7 @@
mAugmentedAutofillEventHistory.addEvent(
new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
null, null, null, null, null, NO_SAVE_UI_REASON_NONE,
- UI_TYPE_INLINE));
+ UI_TYPE_INLINE, /* focusedId= */ null));
}
}
@@ -1113,7 +1116,8 @@
clientState, selectedDatasets, ignoredDatasets,
changedFieldIds, changedDatasetIds,
manuallyFilledFieldIds, manuallyFilledDatasetIds,
- detectedFieldsIds, detectedFieldClassifications, saveDialogNotShowReason));
+ detectedFieldsIds, detectedFieldClassifications, saveDialogNotShowReason,
+ /* focusedId= */ null));
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 8f12b1d..6b227d7 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1871,7 +1871,7 @@
if (mLogViewEntered) {
mLogViewEntered = false;
- mService.logViewEntered(id, null);
+ mService.logViewEntered(id, null, mCurrentViewId);
}
}
@@ -2775,9 +2775,9 @@
forceRemoveFromServiceLocked();
return;
}
+ mService.setAuthenticationSelected(id, mClientState, uiType, mCurrentViewId);
}
- mService.setAuthenticationSelected(id, mClientState, uiType);
final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex);
mHandler.sendMessage(
@@ -2850,7 +2850,7 @@
if (!mLoggedInlineDatasetShown) {
// Chip inflation already logged, do not log again.
// This is needed because every chip inflation will call this.
- mService.logDatasetShown(this.id, mClientState, uiType);
+ mService.logDatasetShown(this.id, mClientState, uiType, mCurrentViewId);
Slog.d(TAG, "onShown(): " + uiType + ", " + numDatasetsShown);
}
mLoggedInlineDatasetShown = true;
@@ -2858,7 +2858,7 @@
mPresentationStatsEventLogger.logWhenDatasetShown(numDatasetsShown);
// Explicitly sets maybeSetSuggestionPresentedTimestampMs
mPresentationStatsEventLogger.maybeSetSuggestionPresentedTimestampMs();
- mService.logDatasetShown(this.id, mClientState, uiType);
+ mService.logDatasetShown(this.id, mClientState, uiType, mCurrentViewId);
Slog.d(TAG, "onShown(): " + uiType + ", " + numDatasetsShown);
}
}
@@ -5139,7 +5139,7 @@
// so this calling logViewEntered will be a nop.
// Calling logViewEntered() twice will only log it once
// TODO(271181979): this is broken for multiple partitions
- mService.logViewEntered(this.id, null);
+ mService.logViewEntered(this.id, null, mCurrentViewId);
}
// If this is the first time view is entered for inline, the last
@@ -6657,7 +6657,8 @@
// Autofill it directly...
if (dataset.getAuthentication() == null) {
if (generateEvent) {
- mService.logDatasetSelected(dataset.getId(), id, mClientState, uiType);
+ mService.logDatasetSelected(dataset.getId(), id, mClientState, uiType,
+ mCurrentViewId);
}
if (mCurrentViewId != null) {
mInlineSessionController.hideInlineSuggestionsUiLocked(mCurrentViewId);
@@ -6667,7 +6668,8 @@
}
// ...or handle authentication.
- mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType);
+ mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType,
+ mCurrentViewId);
mPresentationStatsEventLogger.maybeSetAuthenticationType(
AUTHENTICATION_TYPE_DATASET_AUTHENTICATION);
// does not matter the value of isPrimary because null response won't be overridden.
diff --git a/services/core/Android.bp b/services/core/Android.bp
index aea16b0..b9540eb 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -188,7 +188,7 @@
"android.hardware.health-V1.0-java", // HIDL
"android.hardware.health-V2.0-java", // HIDL
"android.hardware.health-V2.1-java", // HIDL
- "android.hardware.health-V3-java", // AIDL
+ "android.hardware.health-V4-java", // AIDL
"android.hardware.health-translate-java",
"android.hardware.light-V1-java",
"android.hardware.security.authgraph-V1-java",
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 0286f7b..36dff89 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -101,6 +101,9 @@
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
+import com.android.server.pm.BackgroundInstallControlService;
+import com.android.server.pm.BackgroundInstallControlCallbackHelper;
+
/**
* @hide
*/
@@ -138,6 +141,10 @@
static final int MBA_STATUS_NEW_INSTALL = 3;
// used for indicating newly installed MBAs that are updated (but unused currently)
static final int MBA_STATUS_UPDATED_NEW_INSTALL = 4;
+ // used for indicating preloaded MBAs that are downgraded
+ static final int MBA_STATUS_DOWNGRADED_PRELOADED = 5;
+ // used for indicating MBAs that are uninstalled
+ static final int MBA_STATUS_UNINSTALLED = 6;
@VisibleForTesting
static final String KEY_ENABLE_BIOMETRIC_PROPERTY_VERIFICATION =
@@ -202,7 +209,9 @@
* @param mbaStatus Assign this value of MBA status to the returned elements.
* @return a @{@code List<IBinaryTransparencyService.AppInfo>}
*/
- private @NonNull List<IBinaryTransparencyService.AppInfo> collectAppInfo(
+ @VisibleForTesting
+ @NonNull
+ List<IBinaryTransparencyService.AppInfo> collectAppInfo(
PackageState packageState, int mbaStatus) {
// compute content digest
if (DEBUG) {
@@ -336,27 +345,28 @@
+ " packages after considering APEXs.");
}
- // proceed with all preloaded apps
- List<IBinaryTransparencyService.AppInfo> allUpdatedPreloadInfo =
- collectAllUpdatedPreloadInfo(packagesMeasured);
- for (IBinaryTransparencyService.AppInfo appInfo : allUpdatedPreloadInfo) {
- packagesMeasured.putBoolean(appInfo.packageName, true);
- writeAppInfoToLog(appInfo);
- }
- if (DEBUG) {
- Slog.d(TAG, "Measured " + packagesMeasured.size()
- + " packages after considering preloads");
- }
-
- if (!android.app.Flags.backgroundInstallControlCallbackApi()
- && CompatChanges.isChangeEnabled(LOG_MBA_INFO)) {
- // lastly measure all newly installed MBAs
- List<IBinaryTransparencyService.AppInfo> allMbaInfo =
- collectAllSilentInstalledMbaInfo(packagesMeasured);
- for (IBinaryTransparencyService.AppInfo appInfo : allMbaInfo) {
+ if (!android.app.Flags.backgroundInstallControlCallbackApi()) {
+ // proceed with all preloaded apps
+ List<IBinaryTransparencyService.AppInfo> allUpdatedPreloadInfo =
+ collectAllUpdatedPreloadInfo(packagesMeasured);
+ for (IBinaryTransparencyService.AppInfo appInfo : allUpdatedPreloadInfo) {
packagesMeasured.putBoolean(appInfo.packageName, true);
writeAppInfoToLog(appInfo);
}
+ if (DEBUG) {
+ Slog.d(TAG, "Measured " + packagesMeasured.size()
+ + " packages after considering preloads");
+ }
+
+ if (CompatChanges.isChangeEnabled(LOG_MBA_INFO)) {
+ // lastly measure all newly installed MBAs
+ List<IBinaryTransparencyService.AppInfo> allMbaInfo =
+ collectAllSilentInstalledMbaInfo(packagesMeasured);
+ for (IBinaryTransparencyService.AppInfo appInfo : allMbaInfo) {
+ packagesMeasured.putBoolean(appInfo.packageName, true);
+ writeAppInfoToLog(appInfo);
+ }
+ }
}
long timeSpentMeasuring = System.currentTimeMillis() - currentTimeMs;
digestAllPackagesLatency.logSample(timeSpentMeasuring);
@@ -466,7 +476,8 @@
apexInfo.signerDigests);
}
- private void writeAppInfoToLog(IBinaryTransparencyService.AppInfo appInfo) {
+ @VisibleForTesting
+ void writeAppInfoToLog(IBinaryTransparencyService.AppInfo appInfo) {
// Must order by the proto's field number.
FrameworkStatsLog.write(FrameworkStatsLog.MOBILE_BUNDLED_APP_INFO_GATHERED,
appInfo.packageName,
@@ -1165,40 +1176,85 @@
* TODO: Add a host test for testing registration and callback of BicCallbackHandler
* b/380002484
*/
+ @VisibleForTesting
static class BicCallbackHandler extends IRemoteCallback.Stub {
- private static final String BIC_CALLBACK_HANDLER_TAG =
- "BTS.BicCallbackHandler";
- private final BinaryTransparencyServiceImpl mServiceImpl;
- static final String FLAGGED_PACKAGE_NAME_KEY = "packageName";
+ private static final String BIC_CALLBACK_HANDLER_TAG = TAG + ".BicCallbackHandler";
- BicCallbackHandler(BinaryTransparencyServiceImpl impl) {
- mServiceImpl = impl;
+ private static final int INSTALL_EVENT_TYPE_UNSET = -1;
+
+ private final IBicAppInfoHelper mBicAppInfoHelper;
+
+ @VisibleForTesting
+ BicCallbackHandler(IBicAppInfoHelper bicAppInfoHelper) {
+ mBicAppInfoHelper = bicAppInfoHelper;
}
@Override
public void sendResult(Bundle data) {
- String packageName = data.getString(FLAGGED_PACKAGE_NAME_KEY);
- if (packageName == null) return;
- if (DEBUG) {
- Slog.d(BIC_CALLBACK_HANDLER_TAG, "background install event detected for "
- + packageName);
+ String packageName = data.getString(
+ BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY);
+ int installType = data.getInt(
+ BackgroundInstallControlCallbackHelper.INSTALL_EVENT_TYPE_KEY,
+ INSTALL_EVENT_TYPE_UNSET);
+ if (packageName == null || installType == INSTALL_EVENT_TYPE_UNSET) {
+ Slog.w(BIC_CALLBACK_HANDLER_TAG, "Package name or install type is "
+ + "unavailable, ignoring event");
+ return;
}
-
- PackageState packageState = LocalServices.getService(PackageManagerInternal.class)
+ Slog.d(BIC_CALLBACK_HANDLER_TAG, "Detected new bic event for: " + packageName);
+ if (installType == BackgroundInstallControlService.INSTALL_EVENT_TYPE_INSTALL) {
+ PackageState packageState = LocalServices.getService(PackageManagerInternal.class)
.getPackageStateInternal(packageName);
- if (packageState == null) {
- Slog.w(TAG, "Package state is unavailable, ignoring the package "
- + packageName);
- return;
+ if (packageState == null) {
+ Slog.w(TAG, "Package state is unavailable, ignoring the package "
+ + packageName);
+ return;
+ }
+ int mbaStatus = MBA_STATUS_NEW_INSTALL;
+ if (packageState.isUpdatedSystemApp()) {
+ mbaStatus = MBA_STATUS_UPDATED_PRELOAD;
+ }
+ List<IBinaryTransparencyService.AppInfo> mbaInfo = mBicAppInfoHelper.collectAppInfo(
+ packageState, mbaStatus);
+ for (IBinaryTransparencyService.AppInfo appInfo : mbaInfo) {
+ mBicAppInfoHelper.writeAppInfoToLog(appInfo);
+ }
+ } else if (installType
+ == BackgroundInstallControlService.INSTALL_EVENT_TYPE_UNINSTALL) {
+ IBinaryTransparencyService.AppInfo appInfo
+ = new IBinaryTransparencyService.AppInfo();
+ // since app is already uninstalled we won't be able to retrieve additional
+ // info on it.
+ appInfo.packageName = packageName;
+ appInfo.mbaStatus = MBA_STATUS_UNINSTALLED;
+ mBicAppInfoHelper.writeAppInfoToLog(appInfo);
+ } else {
+ Slog.w(BIC_CALLBACK_HANDLER_TAG, "Unsupported BIC event: " + installType);
}
- if (packageState.isUpdatedSystemApp()) {
- return;
- }
- List<IBinaryTransparencyService.AppInfo> mbaInfo = mServiceImpl.collectAppInfo(
- packageState, MBA_STATUS_NEW_INSTALL);
- for (IBinaryTransparencyService.AppInfo appInfo : mbaInfo) {
- mServiceImpl.writeAppInfoToLog(appInfo);
- }
+ }
+
+ /**
+ * A wrapper of interface for{@link FrameworkStatsLog and ApkDigests}
+ * for easier testing
+ */
+ @VisibleForTesting
+ public interface IBicAppInfoHelper {
+
+ /**
+ * A wrapper of {@link FrameworkStatsLog}
+ *
+ * @param appInfo The app info of the changed MBA to be logged
+ */
+ public void writeAppInfoToLog(IBinaryTransparencyService.AppInfo appInfo);
+
+ /**
+ * A wrapper of {@link BinaryTransparencyServiceImpl}
+ *
+ * @param packageState The packageState provided retrieved from PackageManagerInternal
+ * @param mbaStatus The MBA status of the package
+ */
+ public List<IBinaryTransparencyService.AppInfo> collectAppInfo(
+ PackageState packageState, int mbaStatus);
}
};
@@ -1596,7 +1652,21 @@
}
try {
iBics.registerBackgroundInstallCallback(
- new BicCallbackHandler(mServiceImpl));
+ new BicCallbackHandler(
+ new BicCallbackHandler.IBicAppInfoHelper() {
+ @Override
+ public void writeAppInfoToLog(
+ IBinaryTransparencyService.AppInfo appInfo) {
+ mServiceImpl.writeAppInfoToLog(appInfo);
+ }
+
+ @Override
+ public List<IBinaryTransparencyService.AppInfo> collectAppInfo(
+ PackageState packageState, int mbaStatus) {
+ return mServiceImpl.collectAppInfo(packageState, mbaStatus);
+ }
+ }
+ ));
} catch (RemoteException e) {
Slog.e(TAG, "Failed to register BackgroundInstallControl callback.");
}
@@ -1643,8 +1713,12 @@
}
String packageName = data.getSchemeSpecificPart();
- // now we've got to check what package is this
- if (isPackagePreloaded(packageName) || isPackageAnApex(packageName)) {
+
+ boolean shouldMeasureMba =
+ !android.app.Flags.backgroundInstallControlCallbackApi()
+ && isPackagePreloaded(packageName);
+
+ if (shouldMeasureMba || isPackageAnApex(packageName)) {
Slog.d(TAG, packageName + " was updated. Scheduling measurement...");
UpdateMeasurementsJobService.scheduleBinaryMeasurements(mContext,
BinaryTransparencyService.this);
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
index c6cb67f..354f281 100644
--- a/services/core/java/com/android/server/am/BroadcastController.java
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -57,6 +57,7 @@
import android.app.ApplicationThreadConstants;
import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
+import android.app.BroadcastStickyCache;
import android.app.IApplicationThread;
import android.app.compat.CompatChanges;
import android.appwidget.AppWidgetManager;
@@ -183,6 +184,13 @@
final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
/**
+ * If {@code false} invalidate the list of {@link android.os.IpcDataCache} present inside the
+ * {@link BroadcastStickyCache} class.
+ * The invalidation is required to start caching of the sticky broadcast in the client side.
+ */
+ private volatile boolean mAreStickyCachesInvalidated = false;
+
+ /**
* Resolver for broadcast intents to registered receivers.
* Holds BroadcastFilter (subclass of IntentFilter).
*/
@@ -288,6 +296,11 @@
IIntentReceiver receiver, IntentFilter filter, String permission,
int userId, int flags) {
mService.enforceNotIsolatedCaller("registerReceiver");
+
+ if (!mAreStickyCachesInvalidated) {
+ BroadcastStickyCache.invalidateAllCaches();
+ mAreStickyCachesInvalidated = true;
+ }
ArrayList<StickyBroadcast> stickyBroadcasts = null;
ProcessRecord callerApp = null;
final boolean visibleToInstantApps =
@@ -700,6 +713,7 @@
String[] excludedPackages, int appOp, Bundle bOptions,
boolean serialized, boolean sticky, int userId) {
mService.enforceNotIsolatedCaller("broadcastIntent");
+ final int result;
synchronized (mService) {
intent = verifyBroadcastLocked(intent);
@@ -722,7 +736,7 @@
final long origId = Binder.clearCallingIdentity();
try {
- return broadcastIntentLocked(callerApp,
+ result = broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
intent, resolvedType, resultToApp, resultTo, resultCode, resultData,
resultExtras, requiredPermissions, excludedPermissions, excludedPackages,
@@ -733,6 +747,11 @@
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
+
+ if (sticky && result == ActivityManager.BROADCAST_SUCCESS) {
+ BroadcastStickyCache.invalidateCache(intent.getAction());
+ }
+ return result;
}
// Not the binder call surface
@@ -743,6 +762,7 @@
boolean serialized, boolean sticky, int userId,
BackgroundStartPrivileges backgroundStartPrivileges,
@Nullable int[] broadcastAllowList) {
+ final int result;
synchronized (mService) {
intent = verifyBroadcastLocked(intent);
@@ -750,7 +770,7 @@
String[] requiredPermissions = requiredPermission == null ? null
: new String[] {requiredPermission};
try {
- return broadcastIntentLocked(null, packageName, featureId, intent, resolvedType,
+ result = broadcastIntentLocked(null, packageName, featureId, intent, resolvedType,
resultToApp, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, null, null, OP_NONE, bOptions, serialized, sticky, -1,
uid, realCallingUid, realCallingPid, userId,
@@ -760,6 +780,11 @@
Binder.restoreCallingIdentity(origId);
}
}
+
+ if (sticky && result == ActivityManager.BROADCAST_SUCCESS) {
+ BroadcastStickyCache.invalidateCache(intent.getAction());
+ }
+ return result;
}
@GuardedBy("mService")
@@ -1458,6 +1483,7 @@
list.add(StickyBroadcast.create(new Intent(intent), deferUntilActive,
callingUid, callerAppProcessState, resolvedType));
}
+ BroadcastStickyCache.invalidateCache(intent.getAction());
}
}
@@ -1724,6 +1750,7 @@
Slog.w(TAG, msg);
throw new SecurityException(msg);
}
+ final ArrayList<String> changedStickyBroadcasts = new ArrayList<>();
synchronized (mStickyBroadcasts) {
ArrayMap<String, ArrayList<StickyBroadcast>> stickies = mStickyBroadcasts.get(userId);
if (stickies != null) {
@@ -1740,12 +1767,16 @@
if (list.size() <= 0) {
stickies.remove(intent.getAction());
}
+ changedStickyBroadcasts.add(intent.getAction());
}
if (stickies.size() <= 0) {
mStickyBroadcasts.remove(userId);
}
}
}
+ for (int i = changedStickyBroadcasts.size() - 1; i >= 0; --i) {
+ BroadcastStickyCache.invalidateCache(changedStickyBroadcasts.get(i));
+ }
}
void finishReceiver(IBinder caller, int resultCode, String resultData,
@@ -2124,9 +2155,18 @@
}
void removeStickyBroadcasts(int userId) {
+ final ArrayList<String> changedStickyBroadcasts = new ArrayList<>();
synchronized (mStickyBroadcasts) {
+ final ArrayMap<String, ArrayList<StickyBroadcast>> stickies =
+ mStickyBroadcasts.get(userId);
+ if (stickies != null) {
+ changedStickyBroadcasts.addAll(stickies.keySet());
+ }
mStickyBroadcasts.remove(userId);
}
+ for (int i = changedStickyBroadcasts.size() - 1; i >= 0; --i) {
+ BroadcastStickyCache.invalidateCache(changedStickyBroadcasts.get(i));
+ }
}
@NeverCompile
diff --git a/services/core/java/com/android/server/incident/PendingReports.java b/services/core/java/com/android/server/incident/PendingReports.java
index 35b3673..9a6c87e 100644
--- a/services/core/java/com/android/server/incident/PendingReports.java
+++ b/services/core/java/com/android/server/incident/PendingReports.java
@@ -324,16 +324,12 @@
// Allow system apps to skip the consent dialog and use their in-built consent mechanism
// instead.
- boolean captureConsentlessBugreportDelegatedConsentGranted = false;
- if ((flags & IncidentManager.FLAG_ALLOW_CONSENTLESS_BUGREPORT) != 0) {
- captureConsentlessBugreportDelegatedConsentGranted =
- mPermissionManager.checkPermissionForDataDelivery(
- Manifest.permission
- .CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT,
- attributionSource,
- /* message= */ null)
- == PERMISSION_GRANTED;
- }
+ boolean captureConsentlessBugreportDelegatedConsentGranted =
+ mPermissionManager.checkPermissionForDataDelivery(
+ Manifest.permission.CAPTURE_CONSENTLESS_BUGREPORT_DELEGATED_CONSENT,
+ attributionSource,
+ /* message= */ null)
+ == PERMISSION_GRANTED;
if (captureConsentlessBugreportOnUserdebugBuildGranted
|| captureConsentlessBugreportDelegatedConsentGranted) {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index da31bf2..ccfa61b 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -492,14 +492,21 @@
/* package */
void onTransactionResponse(int transactionId, boolean success) {
TransactionAcceptConditions conditions =
- transaction -> transaction.getTransactionId() == transactionId;
+ transaction -> {
+ if (transaction.getTransactionId() != transactionId) {
+ Log.w(
+ TAG,
+ "Unexpected transaction: expected "
+ + transactionId
+ + ", received "
+ + transaction.getTransactionId());
+ return false;
+ }
+ return true;
+ };
ContextHubServiceTransaction transaction = getTransactionAndHandleNext(conditions);
if (transaction == null) {
- Log.w(TAG, "Received unexpected transaction response (expected ID = "
- + transactionId
- + ", received ID = "
- + transaction.getTransactionId()
- + ")");
+ Log.w(TAG, "Received unexpected transaction response");
return;
}
@@ -581,7 +588,7 @@
transaction.getTransactionType() == ContextHubTransaction.TYPE_QUERY_NANOAPPS;
ContextHubServiceTransaction transaction = getTransactionAndHandleNext(conditions);
if (transaction == null) {
- Log.w(TAG, "Received unexpected query response (expected " + transaction + ")");
+ Log.w(TAG, "Received unexpected query response");
return;
}
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index e886f3b..2d2d258 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -221,6 +221,11 @@
}
}
+ @NonNull
+ private SystemMediaRoute2Provider getSystemProviderForUser(@NonNull UserHandler userHandler) {
+ return userHandler.mSystemProvider;
+ }
+
// Start of methods that implement MediaRouter2 operations.
@NonNull
@@ -246,7 +251,7 @@
UserRecord userRecord = getOrCreateUserRecordLocked(userId);
if (hasSystemRoutingPermissions) {
MediaRoute2ProviderInfo providerInfo =
- userRecord.mHandler.mSystemProvider.getProviderInfo();
+ getSystemProviderForUser(userRecord.mHandler).getProviderInfo();
if (providerInfo != null) {
systemRoutes = providerInfo.getRoutes();
} else {
@@ -258,7 +263,8 @@
}
} else {
systemRoutes = new ArrayList<>();
- systemRoutes.add(userRecord.mHandler.mSystemProvider.getDefaultRoute());
+ systemRoutes.add(
+ getSystemProviderForUser(userRecord.mHandler).getDefaultRoute());
}
}
return new ArrayList<>(systemRoutes);
@@ -850,10 +856,11 @@
if (setDeviceRouteSelected) {
// Return a fake system session that shows the device route as selected and
// available bluetooth routes as transferable.
- return userRecord.mHandler.mSystemProvider
+ return getSystemProviderForUser(userRecord.mHandler)
.generateDeviceRouteSelectedSessionInfo(targetPackageName);
} else {
- sessionInfos = userRecord.mHandler.mSystemProvider.getSessionInfos();
+ sessionInfos = getSystemProviderForUser(userRecord.mHandler)
+ .getSessionInfos();
if (!sessionInfos.isEmpty()) {
// Return a copy of the current system session with no modification,
// except setting the client package name.
@@ -866,7 +873,8 @@
}
} else {
return new RoutingSessionInfo.Builder(
- userRecord.mHandler.mSystemProvider.getDefaultSessionInfo())
+ getSystemProviderForUser(userRecord.mHandler)
+ .getDefaultSessionInfo())
.setClientPackageName(targetPackageName)
.build();
}
@@ -1374,7 +1382,7 @@
}
manager.mLastSessionCreationRequest = null;
} else {
- String defaultRouteId = userHandler.mSystemProvider.getDefaultRoute().getId();
+ String defaultRouteId = getSystemProviderForUser(userHandler).getDefaultRoute().getId();
if (route.isSystemRoute()
&& !routerRecord.hasSystemRoutingPermission()
&& !TextUtils.equals(route.getId(), defaultRouteId)) {
@@ -1462,7 +1470,7 @@
routerRecord.mPackageName, routerRecord.mRouterId, route.getId()));
UserHandler userHandler = routerRecord.mUserRecord.mHandler;
- String defaultRouteId = userHandler.mSystemProvider.getDefaultRoute().getId();
+ String defaultRouteId = getSystemProviderForUser(userHandler).getDefaultRoute().getId();
if (route.isSystemRoute()
&& !routerRecord.hasSystemRoutingPermission()
&& !TextUtils.equals(route.getId(), defaultRouteId)) {
@@ -2125,11 +2133,12 @@
notifyRoutesUpdated(routesToReport.values().stream().toList());
List<RoutingSessionInfo> sessionInfos =
- mUserRecord.mHandler.mSystemProvider.getSessionInfos();
+ getSystemProviderForUser(mUserRecord.mHandler).getSessionInfos();
RoutingSessionInfo systemSessionToReport =
newSystemRoutingPermissionValue && !sessionInfos.isEmpty()
? sessionInfos.get(0)
- : mUserRecord.mHandler.mSystemProvider.getDefaultSessionInfo();
+ : getSystemProviderForUser(mUserRecord.mHandler)
+ .getDefaultSessionInfo();
notifySessionInfoChanged(systemSessionToReport);
}
}
@@ -2279,7 +2288,7 @@
if (route.isSystemRoute() && !hasSystemRoutingPermission()) {
// The router lacks permission to modify system routing, so we hide system
// route info from them.
- route = mUserRecord.mHandler.mSystemProvider.getDefaultRoute();
+ route = getSystemProviderForUser(mUserRecord.mHandler).getDefaultRoute();
}
mRouter.requestCreateSessionByManager(uniqueRequestId, oldSession, route);
} catch (RemoteException ex) {
@@ -2535,6 +2544,10 @@
private boolean mRunning;
+ private SystemMediaRoute2Provider getSystemProvider() {
+ return mSystemProvider;
+ }
+
// TODO: (In Android S+) Pull out SystemMediaRoute2Provider out of UserHandler.
UserHandler(
@NonNull MediaRouter2ServiceImpl service,
@@ -2549,19 +2562,19 @@
service.mContext, UserHandle.of(userRecord.mUserId), looper)
: new SystemMediaRoute2Provider(
service.mContext, UserHandle.of(userRecord.mUserId), looper);
- mRouteProviders.add(mSystemProvider);
+ mRouteProviders.add(getSystemProvider());
mWatcher = new MediaRoute2ProviderWatcher(service.mContext, this,
this, mUserRecord.mUserId);
}
void init() {
- mSystemProvider.setCallback(this);
+ getSystemProvider().setCallback(this);
}
private void start() {
if (!mRunning) {
mRunning = true;
- mSystemProvider.start();
+ getSystemProvider().start();
mWatcher.start();
}
}
@@ -2570,7 +2583,7 @@
if (mRunning) {
mRunning = false;
mWatcher.stop(); // also stops all providers
- mSystemProvider.stop();
+ getSystemProvider().stop();
}
}
@@ -2662,7 +2675,7 @@
String indent = prefix + " ";
pw.println(indent + "mRunning=" + mRunning);
- mSystemProvider.dump(pw, prefix);
+ getSystemProvider().dump(pw, prefix);
mWatcher.dump(pw, prefix);
}
@@ -2755,7 +2768,7 @@
hasAddedOrModifiedRoutes,
hasRemovedRoutes,
provider.mIsSystemRouteProvider,
- mSystemProvider.getDefaultRoute());
+ getSystemProvider().getDefaultRoute());
}
private static String getPackageNameFromNullableRecord(
@@ -2969,7 +2982,8 @@
}
// Bypass checking router if it's the system session (routerRecord should be null)
- if (TextUtils.equals(getProviderId(uniqueSessionId), mSystemProvider.getUniqueId())) {
+ if (TextUtils.equals(
+ getProviderId(uniqueSessionId), getSystemProvider().getUniqueId())) {
return true;
}
@@ -3100,7 +3114,7 @@
&& !matchingRequest.mRouterRecord.hasSystemRoutingPermission()) {
// The router lacks permission to modify system routing, so we hide system routing
// session info from them.
- sessionInfo = mSystemProvider.getDefaultSessionInfo();
+ sessionInfo = getSystemProvider().getDefaultSessionInfo();
}
matchingRequest.mRouterRecord.notifySessionCreated(
toOriginalRequestId(uniqueRequestId), sessionInfo);
@@ -3114,13 +3128,13 @@
}
// For system provider, notify all routers.
- if (provider == mSystemProvider) {
+ if (provider == getSystemProvider()) {
if (mServiceRef.get() == null) {
return;
}
notifySessionInfoChangedToRouters(getRouterRecords(true), sessionInfo);
notifySessionInfoChangedToRouters(
- getRouterRecords(false), mSystemProvider.getDefaultSessionInfo());
+ getRouterRecords(false), getSystemProvider().getDefaultSessionInfo());
return;
}
@@ -3256,7 +3270,8 @@
MediaRoute2ProviderInfo systemProviderInfo = null;
for (MediaRoute2ProviderInfo providerInfo : mLastProviderInfos) {
// TODO: Create MediaRoute2ProviderInfo#isSystemProvider()
- if (TextUtils.equals(providerInfo.getUniqueId(), mSystemProvider.getUniqueId())) {
+ if (TextUtils.equals(
+ providerInfo.getUniqueId(), getSystemProvider().getUniqueId())) {
// Adding routes from system provider will be handled below, so skip it here.
systemProviderInfo = providerInfo;
continue;
@@ -3272,10 +3287,10 @@
// This shouldn't happen.
Slog.wtf(TAG, "System route provider not found.");
}
- currentSystemSessionInfo = mSystemProvider.getSessionInfos().get(0);
+ currentSystemSessionInfo = getSystemProvider().getSessionInfos().get(0);
} else {
- currentRoutes.add(mSystemProvider.getDefaultRoute());
- currentSystemSessionInfo = mSystemProvider.getDefaultSessionInfo();
+ currentRoutes.add(getSystemProvider().getDefaultRoute());
+ currentSystemSessionInfo = getSystemProvider().getDefaultSessionInfo();
}
if (!currentRoutes.isEmpty()) {
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index c7e00d3..52433a56 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -25,7 +25,7 @@
import android.media.quality.IMediaQualityManager;
import android.media.quality.IPictureProfileCallback;
import android.media.quality.ISoundProfileCallback;
-import android.media.quality.MediaQualityContract;
+import android.media.quality.MediaQualityContract.BaseParameters;
import android.media.quality.ParamCapability;
import android.media.quality.PictureProfile;
import android.media.quality.PictureProfileHandle;
@@ -75,10 +75,10 @@
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
- values.put(MediaQualityContract.BaseParameters.PARAMETER_TYPE, pp.getProfileType());
- values.put(MediaQualityContract.BaseParameters.PARAMETER_NAME, pp.getName());
- values.put(MediaQualityContract.BaseParameters.PARAMETER_PACKAGE, pp.getPackageName());
- values.put(MediaQualityContract.BaseParameters.PARAMETER_INPUT_ID, pp.getInputId());
+ values.put(BaseParameters.PARAMETER_TYPE, pp.getProfileType());
+ values.put(BaseParameters.PARAMETER_NAME, pp.getName());
+ values.put(BaseParameters.PARAMETER_PACKAGE, pp.getPackageName());
+ values.put(BaseParameters.PARAMETER_INPUT_ID, pp.getInputId());
values.put(mMediaQualityDbHelper.SETTINGS, bundleToJson(pp.getParameters()));
// id is auto-generated by SQLite upon successful insertion of row
@@ -99,8 +99,8 @@
public PictureProfile getPictureProfile(int type, String name) {
SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase();
- String selection = MediaQualityContract.BaseParameters.PARAMETER_TYPE + " = ? AND "
- + MediaQualityContract.BaseParameters.PARAMETER_NAME + " = ?";
+ String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
+ + BaseParameters.PARAMETER_NAME + " = ?";
String[] selectionArguments = {Integer.toString(type), name};
try (
@@ -176,26 +176,26 @@
private String[] getAllPictureProfileColumns() {
return new String[]{
- MediaQualityContract.BaseParameters.PARAMETER_ID,
- MediaQualityContract.BaseParameters.PARAMETER_TYPE,
- MediaQualityContract.BaseParameters.PARAMETER_NAME,
- MediaQualityContract.BaseParameters.PARAMETER_INPUT_ID,
- MediaQualityContract.BaseParameters.PARAMETER_PACKAGE,
+ BaseParameters.PARAMETER_ID,
+ BaseParameters.PARAMETER_TYPE,
+ BaseParameters.PARAMETER_NAME,
+ BaseParameters.PARAMETER_INPUT_ID,
+ BaseParameters.PARAMETER_PACKAGE,
mMediaQualityDbHelper.SETTINGS
};
}
private PictureProfile getPictureProfileFromCursor(Cursor cursor) {
String returnId = cursor.getString(cursor.getColumnIndexOrThrow(
- MediaQualityContract.BaseParameters.PARAMETER_ID));
+ BaseParameters.PARAMETER_ID));
int type = cursor.getInt(cursor.getColumnIndexOrThrow(
- MediaQualityContract.BaseParameters.PARAMETER_TYPE));
+ BaseParameters.PARAMETER_TYPE));
String name = cursor.getString(cursor.getColumnIndexOrThrow(
- MediaQualityContract.BaseParameters.PARAMETER_NAME));
+ BaseParameters.PARAMETER_NAME));
String inputId = cursor.getString(cursor.getColumnIndexOrThrow(
- MediaQualityContract.BaseParameters.PARAMETER_INPUT_ID));
+ BaseParameters.PARAMETER_INPUT_ID));
String packageName = cursor.getString(cursor.getColumnIndexOrThrow(
- MediaQualityContract.BaseParameters.PARAMETER_PACKAGE));
+ BaseParameters.PARAMETER_PACKAGE));
String settings = cursor.getString(
cursor.getColumnIndexOrThrow(mMediaQualityDbHelper.SETTINGS));
return new PictureProfile(returnId, type, name, inputId,
@@ -204,7 +204,7 @@
@Override
public List<PictureProfile> getPictureProfilesByPackage(String packageName) {
- String selection = MediaQualityContract.BaseParameters.PARAMETER_PACKAGE + " = ?";
+ String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
String[] selectionArguments = {packageName};
return getPictureProfilesBasedOnConditions(getAllPictureProfileColumns(), selection,
selectionArguments);
@@ -217,7 +217,7 @@
@Override
public List<String> getPictureProfilePackageNames() {
- String [] column = {MediaQualityContract.BaseParameters.PARAMETER_NAME};
+ String [] column = {BaseParameters.PARAMETER_NAME};
List<PictureProfile> pictureProfiles = getPictureProfilesBasedOnConditions(column,
null, null);
List<String> packageNames = new ArrayList<>();
@@ -255,18 +255,32 @@
}
@Override
- public SoundProfile createSoundProfile(SoundProfile pp) {
- // TODO: implement
- return pp;
+ public SoundProfile createSoundProfile(SoundProfile sp) {
+ SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+
+ ContentValues values = new ContentValues();
+ values.put(BaseParameters.PARAMETER_NAME, sp.getName());
+ values.put(BaseParameters.PARAMETER_PACKAGE, sp.getPackageName());
+ values.put(BaseParameters.PARAMETER_INPUT_ID, sp.getInputId());
+ values.put(mMediaQualityDbHelper.SETTINGS, bundleToJson(sp.getParameters()));
+
+ long id = db.insert(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, null, values);
+ return new SoundProfile.Builder(sp).setProfileId(Long.toString(id)).build();
}
+
@Override
- public void updateSoundProfile(String id, SoundProfile pp) {
+ public void updateSoundProfile(String id, SoundProfile sp) {
// TODO: implement
}
+
@Override
public void removeSoundProfile(String id) {
- // TODO: implement
+ SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+ String selection = BaseParameters.PARAMETER_ID + " = ?";
+ String[] selectionArgs = {id};
+ db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection, selectionArgs);
}
+
@Override
public SoundProfile getSoundProfile(int type, String id) {
return null;
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java
index 27c4e9d..bc0fc2b 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlCallbackHelper.java
@@ -33,9 +33,10 @@
public class BackgroundInstallControlCallbackHelper {
- @VisibleForTesting static final String FLAGGED_PACKAGE_NAME_KEY = "packageName";
- @VisibleForTesting static final String FLAGGED_USER_ID_KEY = "userId";
- @VisibleForTesting static final String INSTALL_EVENT_TYPE_KEY = "installEventType";
+ public static final String FLAGGED_PACKAGE_NAME_KEY = "packageName";
+ public static final String FLAGGED_USER_ID_KEY = "userId";
+ public static final String INSTALL_EVENT_TYPE_KEY = "installEventType";
+
private static final String TAG = "BackgroundInstallControlCallbackHelper";
private final Handler mHandler;
diff --git a/services/core/java/com/android/server/power/hint/adpf_flags.aconfig b/services/core/java/com/android/server/power/hint/adpf_flags.aconfig
index 147d76b..97d3483 100644
--- a/services/core/java/com/android/server/power/hint/adpf_flags.aconfig
+++ b/services/core/java/com/android/server/power/hint/adpf_flags.aconfig
@@ -5,3 +5,11 @@
package: "android.adpf"
container: "system"
+flag {
+ name: "adpf_viewrootimpl_action_down_boost"
+ is_exported: true
+ namespace: "game"
+ description: "Guards boosting on touch in ViewRootImpl."
+ is_fixed_read_only: true
+ bug: "360345939"
+}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index d2546e4..ef33ffe 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3212,18 +3212,11 @@
* will be ignored.
*/
boolean isUniversalResizeable() {
- if (info.applicationInfo.category == ApplicationInfo.CATEGORY_GAME) {
- return false;
- }
- final boolean compatEnabled = Flags.universalResizableByDefault()
- && mDisplayContent != null && mDisplayContent.getConfiguration()
- .smallestScreenWidthDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP
- && mDisplayContent.getIgnoreOrientationRequest()
- && info.isChangeEnabled(ActivityInfo.UNIVERSAL_RESIZABLE_BY_DEFAULT);
- if (!compatEnabled && !mWmService.mConstants.mIgnoreActivityOrientationRequest) {
- return false;
- }
- if (mWmService.mConstants.isPackageOptOutIgnoreActivityOrientationRequest(packageName)) {
+ final boolean isLargeScreen = mDisplayContent != null && mDisplayContent.getConfiguration()
+ .smallestScreenWidthDp >= WindowManager.LARGE_SCREEN_SMALLEST_SCREEN_WIDTH_DP
+ && mDisplayContent.getIgnoreOrientationRequest();
+ if (!canBeUniversalResizeable(info.applicationInfo, mWmService, isLargeScreen,
+ true /* forActivity */)) {
return false;
}
if (mAppCompatController.mAllowRestrictedResizability.getAsBoolean()) {
@@ -3234,6 +3227,31 @@
.userPreferenceCompatibleWithNonResizability();
}
+ /**
+ * Returns {@code true} if the fixed orientation, aspect ratio, resizability of the application
+ * can be ignored.
+ */
+ static boolean canBeUniversalResizeable(ApplicationInfo appInfo, WindowManagerService wms,
+ boolean isLargeScreen, boolean forActivity) {
+ if (appInfo.category == ApplicationInfo.CATEGORY_GAME) {
+ return false;
+ }
+ final boolean compatEnabled = isLargeScreen && Flags.universalResizableByDefault()
+ && appInfo.isChangeEnabled(ActivityInfo.UNIVERSAL_RESIZABLE_BY_DEFAULT);
+ if (!compatEnabled && !wms.mConstants.mIgnoreActivityOrientationRequest) {
+ return false;
+ }
+ if (wms.mConstants.isPackageOptOutIgnoreActivityOrientationRequest(appInfo.packageName)) {
+ return false;
+ }
+ if (forActivity) {
+ // The caller will check both application and activity level property.
+ return true;
+ }
+ return !AppCompatController.allowRestrictedResizability(wms.mContext.getPackageManager(),
+ appInfo.packageName);
+ }
+
boolean isResizeable() {
return mAtmService.mForceResizableActivities
|| ActivityInfo.isResizeableMode(info.resizeMode)
@@ -3667,16 +3685,6 @@
pauseKeyDispatchingLocked();
- // We are finishing the top focused activity and its task has nothing to be focused so
- // the next focusable task should be focused.
- if (mayAdjustTop && task.topRunningActivity(true /* focusableOnly */)
- == null) {
- task.adjustFocusToNextFocusableTask("finish-top", false /* allowFocusSelf */,
- shouldAdjustGlobalFocus);
- }
-
- finishActivityResults(resultCode, resultData, resultGrants);
-
final boolean endTask = task.getTopNonFinishingActivity() == null
&& !task.isClearingToReuseTask();
final WindowContainer<?> trigger = endTask ? task : this;
@@ -3687,6 +3695,16 @@
if (transition != null) {
transition.collectClose(trigger);
}
+ // We are finishing the top focused activity and its task has nothing to be focused so
+ // the next focusable task should be focused.
+ if (mayAdjustTop && task.topRunningActivity(true /* focusableOnly */)
+ == null) {
+ task.adjustFocusToNextFocusableTask("finish-top", false /* allowFocusSelf */,
+ shouldAdjustGlobalFocus);
+ }
+
+ finishActivityResults(resultCode, resultData, resultGrants);
+
if (isState(RESUMED)) {
if (endTask) {
mAtmService.getTaskChangeNotificationController().notifyTaskRemovalStarted(
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index 203932d..330283f 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -77,13 +77,8 @@
mAppCompatOverrides);
mAllowRestrictedResizability = AppCompatUtils.asLazy(() -> {
// Application level.
- try {
- if (packageManager.getProperty(PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY,
- mActivityRecord.packageName).getBoolean()) {
- return true;
- }
- } catch (PackageManager.NameNotFoundException e) {
- // Fall through.
+ if (allowRestrictedResizability(packageManager, mActivityRecord.packageName)) {
+ return true;
}
// Activity level.
try {
@@ -98,6 +93,15 @@
});
}
+ static boolean allowRestrictedResizability(PackageManager pm, String packageName) {
+ try {
+ return pm.getProperty(PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY, packageName)
+ .getBoolean();
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
@NonNull
TransparentPolicy getTransparentPolicy() {
return mTransparentPolicy;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index ace6aae..d9332ec0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -47,8 +47,10 @@
import static com.android.server.am.ProcessList.NETWORK_STATE_BLOCK;
import static com.android.server.am.ProcessList.NETWORK_STATE_NO_CHANGE;
import static com.android.server.am.ProcessList.NETWORK_STATE_UNBLOCK;
+
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -105,6 +107,7 @@
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IProgressListener;
+import android.os.IpcDataCache;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
@@ -959,28 +962,37 @@
@Test
@SuppressWarnings("GuardedBy")
public void testBroadcastStickyIntent_verifyTypeNotResolved() throws Exception {
- final Intent intent = new Intent(TEST_ACTION1);
- final Uri uri = new Uri.Builder()
- .scheme(SCHEME_CONTENT)
- .authority(TEST_AUTHORITY)
- .path("green")
- .build();
- intent.setData(uri);
- broadcastIntent(intent, null, true, TEST_MIME_TYPE, USER_ALL);
- assertStickyBroadcasts(mAms.getStickyBroadcastsForTest(TEST_ACTION1, USER_ALL),
- StickyBroadcast.create(intent, false, Process.myUid(), PROCESS_STATE_UNKNOWN,
- TEST_MIME_TYPE));
- when(mContentResolver.getType(uri)).thenReturn(TEST_MIME_TYPE);
+ MockitoSession mockitoSession =
+ ExtendedMockito.mockitoSession().mockStatic(IpcDataCache.class).startMocking();
- addUidRecord(TEST_UID, TEST_PACKAGE);
- final ProcessRecord procRecord = mAms.getProcessRecordLocked(TEST_PACKAGE, TEST_UID);
- final IntentFilter intentFilter = new IntentFilter(TEST_ACTION1);
- intentFilter.addDataType(TEST_MIME_TYPE);
- final Intent resultIntent = mAms.registerReceiverWithFeature(procRecord.getThread(),
- TEST_PACKAGE, null, null, null, intentFilter, null, TEST_USER,
- Context.RECEIVER_EXPORTED);
- assertNotNull(resultIntent);
- verify(mContentResolver, never()).getType(any());
+ try {
+ final Intent intent = new Intent(TEST_ACTION1);
+ final Uri uri = new Uri.Builder()
+ .scheme(SCHEME_CONTENT)
+ .authority(TEST_AUTHORITY)
+ .path("green")
+ .build();
+ intent.setData(uri);
+ broadcastIntent(intent, null, true, TEST_MIME_TYPE, USER_ALL);
+ assertStickyBroadcasts(mAms.getStickyBroadcastsForTest(TEST_ACTION1, USER_ALL),
+ StickyBroadcast.create(intent, false, Process.myUid(), PROCESS_STATE_UNKNOWN,
+ TEST_MIME_TYPE));
+ when(mContentResolver.getType(uri)).thenReturn(TEST_MIME_TYPE);
+ ExtendedMockito.doNothing().when(
+ () -> IpcDataCache.invalidateCache(anyString(), anyString()));
+
+ addUidRecord(TEST_UID, TEST_PACKAGE);
+ final ProcessRecord procRecord = mAms.getProcessRecordLocked(TEST_PACKAGE, TEST_UID);
+ final IntentFilter intentFilter = new IntentFilter(TEST_ACTION1);
+ intentFilter.addDataType(TEST_MIME_TYPE);
+ final Intent resultIntent = mAms.registerReceiverWithFeature(procRecord.getThread(),
+ TEST_PACKAGE, null, null, null, intentFilter, null, TEST_USER,
+ Context.RECEIVER_EXPORTED);
+ assertNotNull(resultIntent);
+ verify(mContentResolver, never()).getType(any());
+ } finally {
+ mockitoSession.finishMocking();
+ }
}
@SuppressWarnings("GuardedBy")
diff --git a/services/tests/security/intrusiondetection/Android.bp b/services/tests/security/intrusiondetection/Android.bp
index 00ac908..8d674b1 100644
--- a/services/tests/security/intrusiondetection/Android.bp
+++ b/services/tests/security/intrusiondetection/Android.bp
@@ -19,15 +19,20 @@
"androidx.test.rules",
"androidx.test.runner",
"compatibility-device-util-axt",
+ "coretests-aidl",
"frameworks-base-testutils",
"junit",
"platform-test-annotations",
+ "servicestests-utils",
"services.core",
"truth",
"Nene",
"Harrier",
"TestApp",
],
+ data: [
+ ":TestIntrusionDetectionApp",
+ ],
platform_apis: true,
diff --git a/services/tests/security/intrusiondetection/AndroidManifest.xml b/services/tests/security/intrusiondetection/AndroidManifest.xml
index 39e41cd..b30710d 100644
--- a/services/tests/security/intrusiondetection/AndroidManifest.xml
+++ b/services/tests/security/intrusiondetection/AndroidManifest.xml
@@ -33,6 +33,10 @@
</receiver>
</application>
+ <queries>
+ <package android:name="com.android.coretests.apps.testapp" />
+ </queries>
+
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.server.security.intrusiondetection.tests"
android:label="Frameworks IntrusionDetection Services Tests"/>
diff --git a/services/tests/security/intrusiondetection/AndroidTest.xml b/services/tests/security/intrusiondetection/AndroidTest.xml
index 42cb9e3..6489dea4a 100644
--- a/services/tests/security/intrusiondetection/AndroidTest.xml
+++ b/services/tests/security/intrusiondetection/AndroidTest.xml
@@ -20,6 +20,7 @@
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
<option name="test-file-name" value="IntrusionDetectionServiceTests.apk"/>
+ <option name="test-file-name" value="TestIntrusionDetectionApp.apk"/>
<option name="install-arg" value="-t" />
</target_preparer>
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
index c185ad5..da5e9f6 100644
--- a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/IntrusionDetectionServiceTest.java
@@ -42,6 +42,10 @@
import android.app.admin.SecurityLog.SecurityEvent;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.Bundle;
import android.os.Looper;
import android.os.PermissionEnforcer;
import android.os.RemoteException;
@@ -50,8 +54,10 @@
import android.security.intrusiondetection.IIntrusionDetectionServiceCommandCallback;
import android.security.intrusiondetection.IIntrusionDetectionServiceStateCallback;
import android.security.intrusiondetection.IntrusionDetectionEvent;
+import android.security.intrusiondetection.IntrusionDetectionEventTransport;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
+import android.util.Log;
import androidx.test.core.app.ApplicationProvider;
@@ -65,6 +71,7 @@
import com.android.bedstead.permissions.CommonPermissions;
import com.android.bedstead.permissions.PermissionContext;
import com.android.bedstead.permissions.annotations.EnsureHasPermission;
+import com.android.internal.infra.AndroidFuture;
import com.android.server.ServiceThread;
import org.junit.Before;
@@ -111,11 +118,16 @@
private IntrusionDetectionEventTransportConnection mIntrusionDetectionEventTransportConnection;
private DataAggregator mDataAggregator;
private IntrusionDetectionService mIntrusionDetectionService;
+ private IBinder mService;
private TestLooper mTestLooper;
private Looper mLooper;
private TestLooper mTestLooperOfDataAggregator;
private Looper mLooperOfDataAggregator;
private FakePermissionEnforcer mPermissionEnforcer;
+ private boolean mBoundToLoggingService = false;
+ private static final String TEST_PKG =
+ "com.android.coretests.apps.testapp";
+ private static final String TEST_SERVICE = TEST_PKG + ".TestLoggingService";
@BeforeClass
public static void setDeviceOwner() {
@@ -141,8 +153,8 @@
@SuppressLint("VisibleForTests")
@Before
- public void setUp() {
- mContext = spy(ApplicationProvider.getApplicationContext());
+ public void setUp() throws Exception {
+ mContext = ApplicationProvider.getApplicationContext();
mPermissionEnforcer = new FakePermissionEnforcer();
mPermissionEnforcer.grant(READ_INTRUSION_DETECTION_STATE);
@@ -565,6 +577,57 @@
}
}
+ @Test
+ public void test_StartIntrusionDetectionEventTransportService() {
+ final String TAG = "test_StartIntrusionDetectionEventTransportService";
+ ServiceConnection serviceConnection = null;
+
+ assertEquals(false, mBoundToLoggingService);
+ try {
+ serviceConnection = startTestService();
+ assertEquals(true, mBoundToLoggingService);
+ assertNotNull(serviceConnection);
+ } catch (SecurityException e) {
+ Log.e(TAG, "SecurityException while starting: ", e);
+ fail("Exception thrown while connecting to service");
+ } catch (InterruptedException e) {
+ Log.e(TAG, "InterruptedException while starting: ", e);
+ fail("Interrupted while connecting to service");
+ } finally {
+ mContext.unbindService(serviceConnection);
+ }
+ }
+
+ private ServiceConnection startTestService() throws SecurityException, InterruptedException {
+ final String TAG = "startTestService";
+ final CountDownLatch latch = new CountDownLatch(1);
+ IntrusionDetectionEventTransport transport = new IntrusionDetectionEventTransport();
+
+ ServiceConnection serviceConnection = new ServiceConnection() {
+ // Called when connection with the service is established.
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ mService = transport.getBinder();
+ mBoundToLoggingService = true;
+ latch.countDown();
+ }
+
+ // Called when the connection with the service disconnects unexpectedly.
+ @Override
+ public void onServiceDisconnected(ComponentName className) {
+ Log.d(TAG, "onServiceDisconnected");
+ mBoundToLoggingService = false;
+ }
+ };
+
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName(TEST_PKG, TEST_SERVICE));
+ mContext.bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
+ latch.await(5, TimeUnit.SECONDS);
+
+ return serviceConnection;
+ }
+
private class MockInjector implements IntrusionDetectionService.Injector {
private final Context mContext;
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/Android.bp b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/Android.bp
new file mode 100644
index 0000000..57d0de4
--- /dev/null
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/Android.bp
@@ -0,0 +1,52 @@
+// Copyright (C) 2017 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 {
+ default_team: "trendy_team_platform_security",
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "TestIntrusionDetectionApp",
+
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ "androidx.test.runner",
+ "compatibility-device-util-axt",
+ "frameworks-base-testutils",
+ "junit",
+ "platform-test-annotations",
+ "services.core",
+ "servicestests-utils",
+ "coretests-aidl",
+ "truth",
+ "Nene",
+ "Harrier",
+ ],
+
+ srcs: ["**/*.java"],
+
+ platform_apis: true,
+ certificate: "platform",
+ dxflags: ["--multi-dex"],
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/AndroidManifest.xml b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/AndroidManifest.xml
new file mode 100644
index 0000000..7cc75ab
--- /dev/null
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.coretests.apps.testapp">
+
+ <application>
+ <service android:name=".TestLoggingService"
+ android:exported="true" />
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java
new file mode 100644
index 0000000..9e38b1c
--- /dev/null
+++ b/services/tests/security/intrusiondetection/src/com/android/server/security/intrusiondetection/TestApp/src/com/android/coretests/apps/testapp/TestLoggingService.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 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.coretests.apps.testapp;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.Process;
+
+import android.security.intrusiondetection.IntrusionDetectionEventTransport;
+
+import com.android.internal.infra.AndroidFuture;
+
+
+public class TestLoggingService extends Service {
+ private static final String TAG = "TestLoggingService";
+ private IntrusionDetectionEventTransport mIntrusionDetectionEventTransport;
+
+ public TestLoggingService() {
+ mIntrusionDetectionEventTransport = new IntrusionDetectionEventTransport();
+ }
+
+ // Binder given to clients.
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mIntrusionDetectionEventTransport.getBinder();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java
index ae78dfe..cc5be7e 100644
--- a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java
@@ -20,6 +20,7 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -40,6 +41,7 @@
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
+import android.os.Bundle;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemProperties;
@@ -50,6 +52,12 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.os.IBinaryTransparencyService;
+import com.android.server.pm.BackgroundInstallControlService;
+import com.android.server.pm.BackgroundInstallControlCallbackHelper;
+import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.AndroidPackageSplit;
+import com.android.server.pm.pkg.PackageStateInternal;
import org.junit.After;
import org.junit.Assert;
@@ -68,6 +76,9 @@
public class BinaryTransparencyServiceTest {
private static final String TAG = "BinaryTransparencyServiceTest";
+ private static final String TEST_PKG_NAME = "testPackageName";
+ private static final long TEST_VERSION_CODE = 1L;
+
private Context mContext;
private BinaryTransparencyService mBinaryTransparencyService;
private BinaryTransparencyService.BinaryTransparencyServiceImpl mTestInterface;
@@ -83,6 +94,8 @@
private PackageManager mPackageManager;
@Mock
private PackageManagerInternal mPackageManagerInternal;
+ @Mock
+ private BinaryTransparencyService.BicCallbackHandler.IBicAppInfoHelper mBicAppInfoHelper;
@Captor
private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback>
@@ -91,6 +104,9 @@
private ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback>
mFaceAuthenticatorsRegisteredCaptor;
+ @Captor
+ private ArgumentCaptor<IBinaryTransparencyService.AppInfo> appInfoCaptor;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -262,4 +278,69 @@
eq("") /* softwareVersion */
);
}
+
+ @Test
+ public void BicCallbackHandler_uploads_mba_metrics() {
+ Bundle data = setupBicCallbackHandlerTest(false,
+ BinaryTransparencyService.MBA_STATUS_NEW_INSTALL);
+
+ BinaryTransparencyService.BicCallbackHandler handler =
+ new BinaryTransparencyService.BicCallbackHandler(mBicAppInfoHelper);
+ handler.sendResult(data);
+
+ verify(mBicAppInfoHelper, times(1)).writeAppInfoToLog(appInfoCaptor.capture());
+ Assert.assertEquals(TEST_PKG_NAME, appInfoCaptor.getValue().packageName);
+ Assert.assertEquals(TEST_VERSION_CODE, appInfoCaptor.getValue().longVersion);
+ }
+
+ @Test
+ public void BicCallbackHandler_uploads_mba_metrics_for_preloads() {
+ Bundle data = setupBicCallbackHandlerTest(true,
+ BinaryTransparencyService.MBA_STATUS_UPDATED_PRELOAD);
+
+ BinaryTransparencyService.BicCallbackHandler handler =
+ new BinaryTransparencyService.BicCallbackHandler(mBicAppInfoHelper);
+ handler.sendResult(data);
+
+ verify(mBicAppInfoHelper, times(1)).writeAppInfoToLog(appInfoCaptor.capture());
+ Assert.assertEquals(TEST_PKG_NAME, appInfoCaptor.getValue().packageName);
+ Assert.assertEquals(TEST_VERSION_CODE, appInfoCaptor.getValue().longVersion);
+ }
+
+ @Test
+ public void BicCallbackHandler_uploads_mba_metrics_for_uninstalls() {
+ Bundle data = new Bundle();
+ data.putString(BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY,
+ TEST_PKG_NAME);
+ data.putInt(BackgroundInstallControlCallbackHelper.INSTALL_EVENT_TYPE_KEY,
+ BackgroundInstallControlService.INSTALL_EVENT_TYPE_UNINSTALL);
+
+ BinaryTransparencyService.BicCallbackHandler handler =
+ new BinaryTransparencyService.BicCallbackHandler(mBicAppInfoHelper);
+ handler.sendResult(data);
+
+ verify(mBicAppInfoHelper, times(1)).writeAppInfoToLog(appInfoCaptor.capture());
+ Assert.assertEquals(TEST_PKG_NAME ,appInfoCaptor.getValue().packageName);
+ Assert.assertEquals(BinaryTransparencyService.MBA_STATUS_UNINSTALLED,
+ appInfoCaptor.getValue().mbaStatus);
+ }
+
+ private Bundle setupBicCallbackHandlerTest(boolean isUpdatedSystemApp,
+ int expectedBtsMbaStatus) {
+ Bundle data = new Bundle();
+ data.putString(BackgroundInstallControlCallbackHelper.FLAGGED_PACKAGE_NAME_KEY,
+ TEST_PKG_NAME);
+ data.putInt(BackgroundInstallControlCallbackHelper.INSTALL_EVENT_TYPE_KEY,
+ BackgroundInstallControlService.INSTALL_EVENT_TYPE_INSTALL);
+ PackageStateInternal mockPackageState = mock(PackageStateInternal.class);
+ when(mPackageManagerInternal.getPackageStateInternal(TEST_PKG_NAME))
+ .thenReturn(mockPackageState);
+ when(mockPackageState.isUpdatedSystemApp()).thenReturn(isUpdatedSystemApp);
+ IBinaryTransparencyService.AppInfo appInfo = new IBinaryTransparencyService.AppInfo();
+ appInfo.packageName = TEST_PKG_NAME;
+ appInfo.longVersion = TEST_VERSION_CODE;
+ when(mBicAppInfoHelper.collectAppInfo(mockPackageState, expectedBtsMbaStatus))
+ .thenReturn(List.of(appInfo));
+ return data;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 584c4c9..87db6c0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -4899,6 +4899,8 @@
prepareLimitedBounds(mActivity, maxAspect, minAspect,
ActivityInfo.SCREEN_ORIENTATION_NOSENSOR, true /* isUnresizable */);
+ assertTrue(ActivityRecord.canBeUniversalResizeable(mActivity.info.applicationInfo,
+ mWm, true /* isLargeScreen */, false /* forActivity */));
assertTrue(mActivity.isUniversalResizeable());
assertTrue(mActivity.isResizeable());
assertFalse(mActivity.shouldCreateAppCompatDisplayInsets());
@@ -4953,6 +4955,8 @@
.setComponent(getUniqueComponentName(mContext.getPackageName()))
.setTask(mTask).build();
assertFalse(optOutAppActivity.isUniversalResizeable());
+ assertFalse(ActivityRecord.canBeUniversalResizeable(mActivity.info.applicationInfo,
+ mWm, true /* isLargeScreen */, false /* forActivity */));
}
diff --git a/tests/BinaryTransparencyHostTest/Android.bp b/tests/BinaryTransparencyHostTest/Android.bp
index e14e5fe..1c8386a 100644
--- a/tests/BinaryTransparencyHostTest/Android.bp
+++ b/tests/BinaryTransparencyHostTest/Android.bp
@@ -31,6 +31,8 @@
],
static_libs: [
"truth",
+ "flag-junit-host",
+ "android.app.flags-aconfig-java-host",
],
device_common_data: [
":BinaryTransparencyTestApp",
diff --git a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
index 6e5f08a..6d8dbcb 100644
--- a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
+++ b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
@@ -24,6 +24,9 @@
import android.platform.test.annotations.LargeTest;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.host.HostFlagsValueProvider;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil.CLog;
@@ -34,6 +37,7 @@
import com.android.tradefed.util.CommandStatus;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -49,6 +53,10 @@
/** Waiting time for the job to be scheduled */
private static final int JOB_CREATION_MAX_SECONDS = 30;
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ HostFlagsValueProvider.createCheckFlagsRule(this::getDevice);
+
@Before
public void setUp() throws Exception {
cancelPendingJob();
@@ -123,6 +131,7 @@
}
}
+ @RequiresFlagsDisabled(android.app.Flags.FLAG_BACKGROUND_INSTALL_CONTROL_CALLBACK_API)
@Test
public void testPreloadUpdateTriggersJobScheduling() throws Exception {
try {
diff --git a/tests/broadcasts/unit/Android.bp b/tests/broadcasts/unit/Android.bp
new file mode 100644
index 0000000..9e15ac4
--- /dev/null
+++ b/tests/broadcasts/unit/Android.bp
@@ -0,0 +1,44 @@
+// Copyright (C) 2024 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+ default_team: "trendy_team_framework_backstage_power",
+}
+
+android_test {
+ name: "BroadcastUnitTests",
+ srcs: ["src/**/*.java"],
+ defaults: [
+ "modules-utils-extended-mockito-rule-defaults",
+ ],
+ static_libs: [
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "mockito-target-extended-minus-junit4",
+ "truth",
+ "flag-junit",
+ "android.app.flags-aconfig-java",
+ "junit-params",
+ ],
+ certificate: "platform",
+ platform_apis: true,
+ test_suites: ["device-tests"],
+}
diff --git a/tests/broadcasts/unit/AndroidManifest.xml b/tests/broadcasts/unit/AndroidManifest.xml
new file mode 100644
index 0000000..61eb230
--- /dev/null
+++ b/tests/broadcasts/unit/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.broadcasts.unit" >
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.broadcasts.unit"
+ android:label="Broadcasts Unit Tests"/>
+</manifest>
\ No newline at end of file
diff --git a/tests/broadcasts/unit/AndroidTest.xml b/tests/broadcasts/unit/AndroidTest.xml
new file mode 100644
index 0000000..b91e4783
--- /dev/null
+++ b/tests/broadcasts/unit/AndroidTest.xml
@@ -0,0 +1,29 @@
+<!-- Copyright (C) 2024 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.
+-->
+<configuration description="Runs Broadcasts tests">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-tag" value="BroadcastUnitTests" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="BroadcastUnitTests.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.broadcasts.unit" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/broadcasts/unit/OWNERS b/tests/broadcasts/unit/OWNERS
new file mode 100644
index 0000000..f1e450b
--- /dev/null
+++ b/tests/broadcasts/unit/OWNERS
@@ -0,0 +1,2 @@
+# Bug component: 316181
+include platform/frameworks/base:/BROADCASTS_OWNERS
\ No newline at end of file
diff --git a/tests/broadcasts/unit/TEST_MAPPING b/tests/broadcasts/unit/TEST_MAPPING
new file mode 100644
index 0000000..b920e25
--- /dev/null
+++ b/tests/broadcasts/unit/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "BroadcastUnitTests"
+ }
+ ]
+}
diff --git a/tests/broadcasts/unit/src/android/app/BroadcastStickyCacheTest.java b/tests/broadcasts/unit/src/android/app/BroadcastStickyCacheTest.java
new file mode 100644
index 0000000..ad032fb
--- /dev/null
+++ b/tests/broadcasts/unit/src/android/app/BroadcastStickyCacheTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2024 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.app;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+
+import static org.junit.Assert.assertFalse;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.os.IpcDataCache;
+import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.internal.annotations.Keep;
+import com.android.modules.utils.testing.ExtendedMockitoRule;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+@RunWith(JUnitParamsRunner.class)
+public class BroadcastStickyCacheTest {
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule
+ public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
+ .mockStatic(IpcDataCache.class)
+ .mockStatic(ActivityManager.class)
+ .build();
+
+ @Mock
+ private IActivityManager mActivityManagerMock;
+
+ @Mock
+ private IApplicationThread mIApplicationThreadMock;
+
+ @Keep
+ private static Object stickyBroadcastList() {
+ return BroadcastStickyCache.STICKY_BROADCAST_ACTIONS;
+ }
+
+ @Before
+ public void setUp() {
+ BroadcastStickyCache.clearCacheForTest();
+
+ doNothing().when(() -> IpcDataCache.invalidateCache(anyString(), anyString()));
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_USE_STICKY_BCAST_CACHE)
+ public void useCache_flagDisabled_returnsFalse() {
+ assertFalse(BroadcastStickyCache.useCache(new IntentFilter(Intent.ACTION_BATTERY_CHANGED)));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_USE_STICKY_BCAST_CACHE)
+ public void useCache_nullFilter_returnsFalse() {
+ assertFalse(BroadcastStickyCache.useCache(null));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_USE_STICKY_BCAST_CACHE)
+ public void useCache_filterWithoutAction_returnsFalse() {
+ assertFalse(BroadcastStickyCache.useCache(new IntentFilter()));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_USE_STICKY_BCAST_CACHE)
+ public void useCache_filterWithoutStickyBroadcastAction_returnsFalse() {
+ assertFalse(BroadcastStickyCache.useCache(new IntentFilter(Intent.ACTION_BOOT_COMPLETED)));
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_USE_STICKY_BCAST_CACHE)
+ public void invalidateCache_flagDisabled_cacheNotInvalidated() {
+ final String apiName = BroadcastStickyCache.sActionApiNameMap.get(
+ AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
+
+ BroadcastStickyCache.invalidateCache(
+ AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
+
+ ExtendedMockito.verify(
+ () -> IpcDataCache.invalidateCache(eq(IpcDataCache.MODULE_SYSTEM), eq(apiName)),
+ times(0));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_USE_STICKY_BCAST_CACHE)
+ public void invalidateCache_broadcastNotSticky_cacheNotInvalidated() {
+ BroadcastStickyCache.invalidateCache(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+
+ ExtendedMockito.verify(
+ () -> IpcDataCache.invalidateCache(eq(IpcDataCache.MODULE_SYSTEM), anyString()),
+ times(0));
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_USE_STICKY_BCAST_CACHE)
+ public void invalidateCache_withStickyBroadcast_cacheInvalidated() {
+ final String apiName = BroadcastStickyCache.sActionApiNameMap.get(
+ Intent.ACTION_BATTERY_CHANGED);
+
+ BroadcastStickyCache.invalidateCache(Intent.ACTION_BATTERY_CHANGED);
+
+ ExtendedMockito.verify(
+ () -> IpcDataCache.invalidateCache(eq(IpcDataCache.MODULE_SYSTEM), eq(apiName)),
+ times(1));
+ }
+
+ @Test
+ public void invalidateAllCaches_cacheInvalidated() {
+ BroadcastStickyCache.invalidateAllCaches();
+
+ for (int i = BroadcastStickyCache.sActionApiNameMap.size() - 1; i > -1; i--) {
+ final String apiName = BroadcastStickyCache.sActionApiNameMap.valueAt(i);
+ ExtendedMockito.verify(() -> IpcDataCache.invalidateCache(anyString(),
+ eq(apiName)), times(1));
+ }
+ }
+
+ @Test
+ @Parameters(method = "stickyBroadcastList")
+ public void getIntent_createNewCache_verifyRegisterReceiverIsCalled(String action)
+ throws RemoteException {
+ setActivityManagerMock(action);
+ final IntentFilter filter = new IntentFilter(action);
+ final Intent intent = queryIntent(filter);
+
+ assertNotNull(intent);
+ assertEquals(intent.getAction(), action);
+ verify(mActivityManagerMock, times(1)).registerReceiverWithFeature(
+ eq(mIApplicationThreadMock), anyString(), anyString(), anyString(), any(),
+ eq(filter), anyString(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void getIntent_querySameValueTwice_verifyRegisterReceiverIsCalledOnce()
+ throws RemoteException {
+ setActivityManagerMock(Intent.ACTION_DEVICE_STORAGE_LOW);
+ final Intent intent = queryIntent(new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW));
+ final Intent cachedIntent = queryIntent(new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW));
+
+ assertNotNull(intent);
+ assertEquals(intent.getAction(), Intent.ACTION_DEVICE_STORAGE_LOW);
+ assertNotNull(cachedIntent);
+ assertEquals(cachedIntent.getAction(), Intent.ACTION_DEVICE_STORAGE_LOW);
+
+ verify(mActivityManagerMock, times(1)).registerReceiverWithFeature(
+ eq(mIApplicationThreadMock), anyString(), anyString(), anyString(), any(),
+ any(), anyString(), anyInt(), anyInt());
+ }
+
+ @Test
+ public void getIntent_querySameActionWithDifferentFilter_verifyRegisterReceiverCalledTwice()
+ throws RemoteException {
+ setActivityManagerMock(Intent.ACTION_DEVICE_STORAGE_LOW);
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
+ final Intent intent = queryIntent(filter);
+
+ final IntentFilter newFilter = new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW);
+ newFilter.addDataScheme("file");
+ final Intent newIntent = queryIntent(newFilter);
+
+ assertNotNull(intent);
+ assertEquals(intent.getAction(), Intent.ACTION_DEVICE_STORAGE_LOW);
+ assertNotNull(newIntent);
+ assertEquals(newIntent.getAction(), Intent.ACTION_DEVICE_STORAGE_LOW);
+
+ verify(mActivityManagerMock, times(1)).registerReceiverWithFeature(
+ eq(mIApplicationThreadMock), anyString(), anyString(), anyString(), any(),
+ eq(filter), anyString(), anyInt(), anyInt());
+
+ verify(mActivityManagerMock, times(1)).registerReceiverWithFeature(
+ eq(mIApplicationThreadMock), anyString(), anyString(), anyString(), any(),
+ eq(newFilter), anyString(), anyInt(), anyInt());
+ }
+
+ private Intent queryIntent(IntentFilter filter) {
+ return BroadcastStickyCache.getIntent(
+ mIApplicationThreadMock,
+ "android",
+ "android",
+ filter,
+ "system",
+ 0,
+ 0
+ );
+ }
+
+ private void setActivityManagerMock(String action) throws RemoteException {
+ when(ActivityManager.getService()).thenReturn(mActivityManagerMock);
+ when(mActivityManagerMock.registerReceiverWithFeature(any(), anyString(),
+ anyString(), anyString(), any(), any(), anyString(), anyInt(),
+ anyInt())).thenReturn(new Intent(action));
+ }
+}