summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/AppOpsManager.java3
-rw-r--r--core/java/android/app/Service.java9
-rw-r--r--core/java/android/provider/Settings.java13
-rw-r--r--core/java/android/service/voice/VisualQueryDetector.java13
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java5
-rw-r--r--core/java/android/view/View.java16
-rw-r--r--core/proto/android/providers/settings/secure.proto1
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/coretests/src/android/view/ViewFrameRateTest.java47
-rw-r--r--core/tests/coretests/src/android/view/ViewRootImplTest.java23
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_resize_veil.xml7
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java160
-rw-r--r--nfc/java/android/nfc/INfcAdapter.aidl4
-rw-r--r--nfc/java/android/nfc/NfcAdapter.java12
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java3
-rw-r--r--packages/SystemUI/res/drawable/biometric_prompt_content_view_background.xml (renamed from libs/WindowManager/Shell/res/drawable/desktop_mode_resize_veil_background.xml)15
-rw-r--r--packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml27
-rw-r--r--packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml27
-rw-r--r--packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml20
-rw-r--r--packages/SystemUI/res/layout/biometric_prompt_content_layout.xml11
-rw-r--r--packages/SystemUI/res/layout/biometric_prompt_content_row_item_text_view.xml2
-rw-r--r--packages/SystemUI/res/layout/biometric_prompt_content_row_layout.xml2
-rw-r--r--packages/SystemUI/res/layout/biometric_prompt_layout.xml10
-rw-r--r--packages/SystemUI/res/values/colors.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml18
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/res/values/styles.xml55
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt139
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt8
-rw-r--r--ravenwood/README.md4
-rw-r--r--ravenwood/api-maintainers.md2
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java24
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java19
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java22
-rw-r--r--services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java23
-rw-r--r--wifi/java/src/android/net/wifi/WifiBlobStore.java39
-rw-r--r--wifi/java/src/android/net/wifi/WifiKeystore.java64
43 files changed, 588 insertions, 286 deletions
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index ff713d071a05..0ed25eb3125a 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2685,8 +2685,7 @@ public class AppOpsManager {
.setDefaultMode(getSystemAlertWindowDefault()).build(),
new AppOpInfo.Builder(OP_ACCESS_NOTIFICATIONS, OPSTR_ACCESS_NOTIFICATIONS,
"ACCESS_NOTIFICATIONS")
- .setPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS)
- .setDefaultMode(AppOpsManager.MODE_ALLOWED).build(),
+ .setPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS).build(),
new AppOpInfo.Builder(OP_CAMERA, OPSTR_CAMERA, "CAMERA")
.setPermission(android.Manifest.permission.CAMERA)
.setRestriction(UserManager.DISALLOW_CAMERA)
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index fe8655c13562..f092945a5d28 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -1135,6 +1135,9 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
} catch (RemoteException ex) {
}
onTimeout(startId);
+ if (Flags.introduceNewServiceOntimeoutCallback()) {
+ onTimeout(startId, ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE);
+ }
}
/**
@@ -1146,6 +1149,12 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
* doesn't finish even after it's timed out,
* the app will be declared an ANR after a short grace period of several seconds.
*
+ * <p>Starting from Android version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM},
+ * {@link #onTimeout(int, int)} will also be called when a foreground service of type
+ * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SHORT_SERVICE} times out.
+ * Developers do not need to implement both of the callbacks on
+ * {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM} and onwards.
+ *
* <p>Note, even though
* {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_SHORT_SERVICE}
* was added
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d0593e7398fc..5bb490336f5d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8502,6 +8502,19 @@ public final class Settings {
public static final String ACCESSIBILITY_BUTTON_TARGETS = "accessibility_button_targets";
/**
+ * Setting specifying the accessibility services, shortcut targets or features
+ * to be toggled via the floating accessibility menu
+ *
+ * <p> This is a colon-separated string list which contains the flattened
+ * {@link ComponentName} and the class name of a system class
+ * implementing a supported accessibility feature.
+ * @hide
+ */
+ @Readable
+ public static final String ACCESSIBILITY_FLOATING_MENU_TARGETS =
+ "accessibility_floating_menu_targets";
+
+ /**
* Setting specifying the accessibility services, accessibility shortcut targets,
* or features to be toggled via a tile in the quick settings panel.
*
diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java
index bf8de06fd244..11858e841a8f 100644
--- a/core/java/android/service/voice/VisualQueryDetector.java
+++ b/core/java/android/service/voice/VisualQueryDetector.java
@@ -301,8 +301,15 @@ public class VisualQueryDetector {
}
/**
- * A class that lets a VoiceInteractionService implementation interact with
- * visual query detection APIs.
+ * A class that lets a VoiceInteractionService implementation interact with visual query
+ * detection APIs.
+ *
+ * Note that methods in this callbacks are not thread-safe so the invocation of each
+ * methods will have different order from how they are called in the
+ * {@link VisualQueryDetectionService}. It is expected to pass a single thread executor or a
+ * serial executor as the callback executor when creating the {@link VisualQueryDetector}
+ * with {@link VoiceInteractionService#createVisualQueryDetector(
+ * PersistableBundle, SharedMemory, Executor, Callback)}.
*/
public interface Callback {
@@ -456,7 +463,7 @@ public class VisualQueryDetector {
Slog.v(TAG, "BinderCallback#onResultDetected");
Binder.withCleanCallingIdentity(() -> {
synchronized (mLock) {
- mCallback.onQueryDetected(partialResult);
+ mExecutor.execute(()->mCallback.onQueryDetected(partialResult));
}
});
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 306410c9a98b..2f2a6709f50b 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -932,7 +932,10 @@ public class VoiceInteractionService extends Service {
* @param sharedMemory The unrestricted data blob to be provided to the
* {@link VisualQueryDetectionService}. Use this to provide models or other such data to the
* sandboxed process.
- * @param callback The callback to notify of detection events.
+ * @param callback The callback to notify of detection events. Single threaded or sequential
+ * executors are recommended for the callback are not guaranteed to be executed
+ * in the order of how they were called from the
+ * {@link VisualQueryDetectionService}.
* @return An instanece of {@link VisualQueryDetector}.
* @throws IllegalStateException when there is an existing {@link VisualQueryDetector}, or when
* there is a non-trusted hotword detector running.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0a9ac2f4bea7..736e8159c8c6 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -40,6 +40,8 @@ import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
import static android.view.flags.Flags.enableUseMeasureCacheDuringForceLayout;
import static android.view.flags.Flags.sensitiveContentAppProtection;
+import static android.view.flags.Flags.toolkitFrameRateBySizeReadOnly;
+import static android.view.flags.Flags.toolkitFrameRateDefaultNormalReadOnly;
import static android.view.flags.Flags.toolkitMetricsForFrameRateDecision;
import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
import static android.view.flags.Flags.viewVelocityApi;
@@ -33796,9 +33798,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|| heightDp <= FRAME_RATE_NARROW_THRESHOLD
|| (widthDp <= FRAME_RATE_SMALL_SIZE_THRESHOLD
&& heightDp <= FRAME_RATE_SMALL_SIZE_THRESHOLD)) {
- return FRAME_RATE_CATEGORY_NORMAL | FRAME_RATE_CATEGORY_REASON_SMALL;
+ int category = toolkitFrameRateBySizeReadOnly()
+ ? FRAME_RATE_CATEGORY_LOW : FRAME_RATE_CATEGORY_NORMAL;
+ return category | FRAME_RATE_CATEGORY_REASON_SMALL;
} else {
- return FRAME_RATE_CATEGORY_HIGH | FRAME_RATE_CATEGORY_REASON_LARGE;
+ int category = toolkitFrameRateDefaultNormalReadOnly()
+ ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
+ return category | FRAME_RATE_CATEGORY_REASON_LARGE;
}
}
@@ -33846,8 +33852,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
frameRateCategory = FRAME_RATE_CATEGORY_HIGH
| FRAME_RATE_CATEGORY_REASON_REQUESTED;
} else {
- // invalid frame rate, default to HIGH
- frameRateCategory = FRAME_RATE_CATEGORY_HIGH
+ // invalid frame rate, use default
+ int category = toolkitFrameRateDefaultNormalReadOnly()
+ ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
+ frameRateCategory = category
| FRAME_RATE_CATEGORY_REASON_INVALID;
}
} else {
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 6b0c2d28b776..fcc85b7ec90f 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -102,6 +102,7 @@ message SecureSettingsProto {
optional SettingProto qs_targets = 54 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto accessibility_pinch_to_zoom_anywhere_enabled = 55 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto accessibility_single_finger_panning_enabled = 56 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto accessibility_floating_menu_targets = 57 [ (android.privacy).dest = DEST_AUTOMATIC ];
}
optional Accessibility accessibility = 2;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 89ac81ebce56..1d6b151e2278 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -6984,4 +6984,7 @@
<!-- Whether WM DisplayContent supports high performance transitions
(lower-end devices may want to disable) -->
<bool name="config_deviceSupportsHighPerfTransitions">true</bool>
+
+ <!-- Wear devices: An intent action that is used for remote intent. -->
+ <string name="config_wearRemoteIntentAction" translatable="false" />
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2e029b23f6af..4322b55b3f35 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5385,4 +5385,6 @@
<!-- Whether WM DisplayContent supports high performance transitions -->
<java-symbol type="bool" name="config_deviceSupportsHighPerfTransitions" />
+
+ <java-symbol type="string" name="config_wearRemoteIntentAction" />
</resources>
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index 90a8c5c57fc2..226629e2019e 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -16,7 +16,14 @@
package android.view;
+import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
+import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
+import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
+import static android.view.flags.Flags.FLAG_TOOLKIT_FRAME_RATE_DEFAULT_NORMAL_READ_ONLY;
+import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
+import static android.view.flags.Flags.toolkitFrameRateBySizeReadOnly;
+import static android.view.flags.Flags.toolkitFrameRateDefaultNormalReadOnly;
import static junit.framework.Assert.assertEquals;
@@ -124,6 +131,7 @@ public class ViewFrameRateTest {
}
@Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
public void noVelocityUsesCategorySmall() throws Throwable {
final CountDownLatch drawLatch1 = new CountDownLatch(1);
mActivityRule.runOnUiThread(() -> {
@@ -141,12 +149,14 @@ public class ViewFrameRateTest {
// Now that it is small, any invalidation should have a normal category
mActivityRule.runOnUiThread(() -> {
mMovingView.invalidate();
- assertEquals(Surface.FRAME_RATE_CATEGORY_NORMAL,
- mViewRoot.getPreferredFrameRateCategory());
+ int expected = toolkitFrameRateBySizeReadOnly()
+ ? FRAME_RATE_CATEGORY_LOW : FRAME_RATE_CATEGORY_NORMAL;
+ assertEquals(expected, mViewRoot.getPreferredFrameRateCategory());
});
}
@Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
public void noVelocityUsesCategoryNarrowWidth() throws Throwable {
final CountDownLatch drawLatch1 = new CountDownLatch(1);
mActivityRule.runOnUiThread(() -> {
@@ -164,12 +174,14 @@ public class ViewFrameRateTest {
// Now that it is small, any invalidation should have a normal category
mActivityRule.runOnUiThread(() -> {
mMovingView.invalidate();
- assertEquals(Surface.FRAME_RATE_CATEGORY_NORMAL,
- mViewRoot.getPreferredFrameRateCategory());
+ int expected = toolkitFrameRateBySizeReadOnly()
+ ? FRAME_RATE_CATEGORY_LOW : FRAME_RATE_CATEGORY_NORMAL;
+ assertEquals(expected, mViewRoot.getPreferredFrameRateCategory());
});
}
@Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
public void noVelocityUsesCategoryNarrowHeight() throws Throwable {
final CountDownLatch drawLatch1 = new CountDownLatch(1);
mActivityRule.runOnUiThread(() -> {
@@ -187,12 +199,14 @@ public class ViewFrameRateTest {
// Now that it is small, any invalidation should have a normal category
mActivityRule.runOnUiThread(() -> {
mMovingView.invalidate();
- assertEquals(Surface.FRAME_RATE_CATEGORY_NORMAL,
- mViewRoot.getPreferredFrameRateCategory());
+ int expected = toolkitFrameRateBySizeReadOnly()
+ ? FRAME_RATE_CATEGORY_LOW : FRAME_RATE_CATEGORY_NORMAL;
+ assertEquals(expected, mViewRoot.getPreferredFrameRateCategory());
});
}
@Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
public void noVelocityUsesCategoryLargeWidth() throws Throwable {
final CountDownLatch drawLatch1 = new CountDownLatch(1);
mActivityRule.runOnUiThread(() -> {
@@ -210,12 +224,14 @@ public class ViewFrameRateTest {
// Now that it is small, any invalidation should have a high category
mActivityRule.runOnUiThread(() -> {
mMovingView.invalidate();
- assertEquals(Surface.FRAME_RATE_CATEGORY_HIGH,
- mViewRoot.getPreferredFrameRateCategory());
+ int expected = toolkitFrameRateDefaultNormalReadOnly()
+ ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
+ assertEquals(expected, mViewRoot.getPreferredFrameRateCategory());
});
}
@Test
+ @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
public void noVelocityUsesCategoryLargeHeight() throws Throwable {
final CountDownLatch drawLatch1 = new CountDownLatch(1);
mActivityRule.runOnUiThread(() -> {
@@ -233,7 +249,20 @@ public class ViewFrameRateTest {
// Now that it is small, any invalidation should have a high category
mActivityRule.runOnUiThread(() -> {
mMovingView.invalidate();
- assertEquals(Surface.FRAME_RATE_CATEGORY_HIGH,
+ int expected = toolkitFrameRateDefaultNormalReadOnly()
+ ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
+ assertEquals(expected, mViewRoot.getPreferredFrameRateCategory());
+ });
+ }
+
+ @Test
+ @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+ FLAG_TOOLKIT_FRAME_RATE_DEFAULT_NORMAL_READ_ONLY})
+ public void defaultNormal() throws Throwable {
+ waitForFrameRateCategoryToSettle();
+ mActivityRule.runOnUiThread(() -> {
+ mMovingView.invalidate();
+ assertEquals(FRAME_RATE_CATEGORY_NORMAL,
mViewRoot.getPreferredFrameRateCategory());
});
}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index f1e56d9d5259..fa364e06a705 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -41,6 +41,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
+import static android.view.flags.Flags.toolkitFrameRateDefaultNormalReadOnly;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -593,8 +594,9 @@ public class ViewRootImplTest {
sInstrumentation.runOnMainSync(() -> {
view.setVisibility(View.VISIBLE);
view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_HIGH);
+ int expected = toolkitFrameRateDefaultNormalReadOnly()
+ ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
+ assertEquals(expected, viewRootImpl.getPreferredFrameRateCategory());
});
sInstrumentation.waitForIdleSync();
@@ -658,7 +660,9 @@ public class ViewRootImplTest {
ViewRootImpl viewRootImpl = view.getViewRootImpl();
sInstrumentation.runOnMainSync(() -> {
view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+ int expected = toolkitFrameRateDefaultNormalReadOnly()
+ ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
+ assertEquals(expected, viewRootImpl.getPreferredFrameRateCategory());
});
}
@@ -1017,11 +1021,13 @@ public class ViewRootImplTest {
ViewRootImpl viewRootImpl = view.getViewRootImpl();
- // In transistion from frequent update to infrequent update
+ // In transition from frequent update to infrequent update
Thread.sleep(delay);
sInstrumentation.runOnMainSync(() -> {
view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
+ int expected = toolkitFrameRateDefaultNormalReadOnly()
+ ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
+ assertEquals(expected, viewRootImpl.getPreferredFrameRateCategory());
});
// reset the frame rate category counts
@@ -1033,7 +1039,7 @@ public class ViewRootImplTest {
sInstrumentation.waitForIdleSync();
}
- // In transistion from frequent update to infrequent update
+ // In transition from frequent update to infrequent update
Thread.sleep(delay);
sInstrumentation.runOnMainSync(() -> {
view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
@@ -1107,8 +1113,9 @@ public class ViewRootImplTest {
assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
FRAME_RATE_CATEGORY_NO_PREFERENCE);
view.invalidate();
- assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
- FRAME_RATE_CATEGORY_HIGH);
+ int expected = toolkitFrameRateDefaultNormalReadOnly()
+ ? FRAME_RATE_CATEGORY_NORMAL : FRAME_RATE_CATEGORY_HIGH;
+ assertEquals(expected, viewRootImpl.getPreferredFrameRateCategory());
});
// reset the frame rate category counts
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_resize_veil.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_resize_veil.xml
index a4bbd8998cc5..147f99144b1d 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_resize_veil.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_resize_veil.xml
@@ -16,13 +16,12 @@
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/desktop_mode_resize_veil_background">
+ android:layout_height="match_parent">
<ImageView
android:id="@+id/veil_application_icon"
- android:layout_width="96dp"
- android:layout_height="96dp"
+ android:layout_width="@dimen/desktop_mode_resize_veil_icon_size"
+ android:layout_height="@dimen/desktop_mode_resize_veil_icon_size"
android:layout_gravity="center"
android:contentDescription="@string/app_icon_text" />
</FrameLayout> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index c032a8106c94..70371f6b18fc 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -506,6 +506,9 @@
<!-- The radius of the caption menu shadow. -->
<dimen name="desktop_mode_handle_menu_shadow_radius">2dp</dimen>
+ <!-- The size of the icon shown in the resize veil. -->
+ <dimen name="desktop_mode_resize_veil_icon_size">96dp</dimen>
+
<dimen name="freeform_resize_handle">15dp</dimen>
<dimen name="freeform_resize_corner">44dp</dimen>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 4c9e17155625..ad290c6aeaa3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -451,7 +451,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
* until a resize event calls showResizeVeil below.
*/
void createResizeVeil() {
- mResizeVeil = new ResizeVeil(mContext, mAppIconDrawable, mTaskInfo,
+ mResizeVeil = new ResizeVeil(mContext, mAppIconDrawable, mTaskInfo, mTaskSurface,
mSurfaceControlBuilderSupplier, mDisplay, mSurfaceControlTransactionSupplier);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
index b0d3b5090ef0..d072f8cec194 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
@@ -23,13 +23,16 @@ import android.annotation.ColorRes;
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.res.Configuration;
+import android.graphics.Color;
import android.graphics.PixelFormat;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
@@ -37,6 +40,7 @@ import android.widget.ImageView;
import android.window.TaskConstants;
import com.android.wm.shell.R;
+import com.android.wm.shell.common.SurfaceUtils;
import java.util.function.Supplier;
@@ -45,19 +49,36 @@ import java.util.function.Supplier;
*/
public class ResizeVeil {
private static final int RESIZE_ALPHA_DURATION = 100;
+
+ private static final int VEIL_CONTAINER_LAYER = TaskConstants.TASK_CHILD_LAYER_RESIZE_VEIL;
+ /** The background is a child of the veil container layer and goes at the bottom. */
+ private static final int VEIL_BACKGROUND_LAYER = 0;
+ /** The icon is a child of the veil container layer and goes in front of the background. */
+ private static final int VEIL_ICON_LAYER = 1;
+
private final Context mContext;
private final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier;
private final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
+ private final SurfaceSession mSurfaceSession = new SurfaceSession();
private final Drawable mAppIcon;
private ImageView mIconView;
+ private int mIconSize;
private SurfaceControl mParentSurface;
+
+ /** A container surface to host the veil background and icon child surfaces. */
private SurfaceControl mVeilSurface;
+ /** A color surface for the veil background. */
+ private SurfaceControl mBackgroundSurface;
+ /** A surface that hosts a windowless window with the app icon. */
+ private SurfaceControl mIconSurface;
+
private final RunningTaskInfo mTaskInfo;
private SurfaceControlViewHost mViewHost;
private final Display mDisplay;
private ValueAnimator mVeilAnimator;
public ResizeVeil(Context context, Drawable appIcon, RunningTaskInfo taskInfo,
+ SurfaceControl taskSurface,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, Display display,
Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier) {
mContext = context;
@@ -65,6 +86,7 @@ public class ResizeVeil {
mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier;
mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
mTaskInfo = taskInfo;
+ mParentSurface = taskSurface;
mDisplay = display;
setupResizeVeil();
}
@@ -73,34 +95,44 @@ public class ResizeVeil {
* Create the veil in its default invisible state.
*/
private void setupResizeVeil() {
- SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
- final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get();
- mVeilSurface = builder
- .setName("Resize veil of Task= " + mTaskInfo.taskId)
+ mVeilSurface = mSurfaceControlBuilderSupplier.get()
+ .setContainerLayer()
+ .setName("Resize veil of Task=" + mTaskInfo.taskId)
+ .setHidden(true)
+ .setParent(mParentSurface)
+ .setCallsite("ResizeVeil#setupResizeVeil")
+ .build();
+ mBackgroundSurface = SurfaceUtils.makeColorLayer(mVeilSurface,
+ "Resize veil background of Task=" + mTaskInfo.taskId, mSurfaceSession);
+ mIconSurface = mSurfaceControlBuilderSupplier.get()
+ .setName("Resize veil icon of Task= " + mTaskInfo.taskId)
.setContainerLayer()
+ .setParent(mVeilSurface)
+ .setHidden(true)
+ .setCallsite("ResizeVeil#setupResizeVeil")
.build();
- View v = LayoutInflater.from(mContext)
- .inflate(R.layout.desktop_mode_resize_veil, null);
- t.setPosition(mVeilSurface, 0, 0)
- .setLayer(mVeilSurface, TaskConstants.TASK_CHILD_LAYER_RESIZE_VEIL)
- .apply();
- Rect taskBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
+ mIconSize = mContext.getResources()
+ .getDimensionPixelSize(R.dimen.desktop_mode_resize_veil_icon_size);
+ final View root = LayoutInflater.from(mContext)
+ .inflate(R.layout.desktop_mode_resize_veil, null /* root */);
+ mIconView = root.findViewById(R.id.veil_application_icon);
+ mIconView.setImageDrawable(mAppIcon);
+
final WindowManager.LayoutParams lp =
- new WindowManager.LayoutParams(taskBounds.width(),
- taskBounds.height(),
+ new WindowManager.LayoutParams(
+ mIconSize,
+ mIconSize,
WindowManager.LayoutParams.TYPE_APPLICATION,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSPARENT);
- lp.setTitle("Resize veil of Task=" + mTaskInfo.taskId);
+ lp.setTitle("Resize veil icon window of Task=" + mTaskInfo.taskId);
lp.setTrustedOverlay();
- WindowlessWindowManager windowManager = new WindowlessWindowManager(mTaskInfo.configuration,
- mVeilSurface, null /* hostInputToken */);
- mViewHost = new SurfaceControlViewHost(mContext, mDisplay, windowManager, "ResizeVeil");
- mViewHost.setView(v, lp);
- mIconView = mViewHost.getView().findViewById(R.id.veil_application_icon);
- mIconView.setImageDrawable(mAppIcon);
+ final WindowlessWindowManager wwm = new WindowlessWindowManager(mTaskInfo.configuration,
+ mIconSurface, null /* hostInputToken */);
+ mViewHost = new SurfaceControlViewHost(mContext, mDisplay, wwm, "ResizeVeil");
+ mViewHost.setView(root, lp);
}
/**
@@ -120,46 +152,74 @@ public class ResizeVeil {
mParentSurface = parentSurface;
}
- int backgroundColorId = getBackgroundColorId();
- mViewHost.getView().setBackgroundColor(mContext.getColor(backgroundColorId));
+ t.show(mVeilSurface);
+ t.setLayer(mVeilSurface, VEIL_CONTAINER_LAYER);
+ t.setLayer(mIconSurface, VEIL_ICON_LAYER);
+ t.setLayer(mBackgroundSurface, VEIL_BACKGROUND_LAYER);
+ t.setColor(mBackgroundSurface,
+ Color.valueOf(mContext.getColor(getBackgroundColorId())).getComponents());
relayout(taskBounds, t);
if (fadeIn) {
cancelAnimation();
+ final SurfaceControl.Transaction veilAnimT = mSurfaceControlTransactionSupplier.get();
mVeilAnimator = new ValueAnimator();
mVeilAnimator.setFloatValues(0f, 1f);
mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION);
mVeilAnimator.addUpdateListener(animation -> {
- t.setAlpha(mVeilSurface, mVeilAnimator.getAnimatedFraction());
- t.apply();
+ veilAnimT.setAlpha(mBackgroundSurface, mVeilAnimator.getAnimatedFraction());
+ veilAnimT.apply();
});
mVeilAnimator.addListener(new AnimatorListenerAdapter() {
@Override
+ public void onAnimationStart(Animator animation) {
+ veilAnimT.show(mBackgroundSurface)
+ .setAlpha(mBackgroundSurface, 0)
+ .apply();
+ }
+
+ @Override
public void onAnimationEnd(Animator animation) {
- t.setAlpha(mVeilSurface, 1);
- t.apply();
+ veilAnimT.setAlpha(mBackgroundSurface, 1).apply();
}
});
+ final SurfaceControl.Transaction iconAnimT = mSurfaceControlTransactionSupplier.get();
final ValueAnimator iconAnimator = new ValueAnimator();
iconAnimator.setFloatValues(0f, 1f);
iconAnimator.setDuration(RESIZE_ALPHA_DURATION);
iconAnimator.addUpdateListener(animation -> {
- mIconView.setAlpha(animation.getAnimatedFraction());
+ iconAnimT.setAlpha(mIconSurface, animation.getAnimatedFraction());
+ iconAnimT.apply();
});
+ iconAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ iconAnimT.show(mIconSurface)
+ .setAlpha(mIconSurface, 0)
+ .apply();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ iconAnimT.setAlpha(mIconSurface, 1).apply();
+ }
+ });
+ // Let the animators show it with the correct alpha value once the animation starts.
+ t.hide(mIconSurface);
+ t.hide(mBackgroundSurface);
+ t.apply();
- t.show(mVeilSurface)
- .addTransactionCommittedListener(
- mContext.getMainExecutor(), () -> {
- mVeilAnimator.start();
- iconAnimator.start();
- })
- .setAlpha(mVeilSurface, 0);
+ mVeilAnimator.start();
+ iconAnimator.start();
} else {
- // Show the veil immediately at full opacity.
- t.show(mVeilSurface).setAlpha(mVeilSurface, 1);
+ // Show the veil immediately.
+ t.show(mIconSurface);
+ t.show(mBackgroundSurface);
+ t.setAlpha(mIconSurface, 1);
+ t.setAlpha(mBackgroundSurface, 1);
+ t.apply();
}
- mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t);
}
/**
@@ -175,8 +235,9 @@ public class ResizeVeil {
* @param newBounds bounds to update veil to.
*/
private void relayout(Rect newBounds, SurfaceControl.Transaction t) {
- mViewHost.relayout(newBounds.width(), newBounds.height());
t.setWindowCrop(mVeilSurface, newBounds.width(), newBounds.height());
+ final PointF iconPosition = calculateAppIconPosition(newBounds);
+ t.setPosition(mIconSurface, iconPosition.x, iconPosition.y);
t.setPosition(mParentSurface, newBounds.left, newBounds.top);
t.setWindowCrop(mParentSurface, newBounds.width(), newBounds.height());
}
@@ -204,7 +265,7 @@ public class ResizeVeil {
mVeilAnimator.end();
}
relayout(newBounds, t);
- mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t);
+ t.apply();
}
/**
@@ -217,14 +278,16 @@ public class ResizeVeil {
mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION);
mVeilAnimator.addUpdateListener(animation -> {
SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
- t.setAlpha(mVeilSurface, 1 - mVeilAnimator.getAnimatedFraction());
+ t.setAlpha(mBackgroundSurface, 1 - mVeilAnimator.getAnimatedFraction());
+ t.setAlpha(mIconSurface, 1 - mVeilAnimator.getAnimatedFraction());
t.apply();
});
mVeilAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
- t.hide(mVeilSurface);
+ t.hide(mBackgroundSurface);
+ t.hide(mIconSurface);
t.apply();
}
});
@@ -242,6 +305,11 @@ public class ResizeVeil {
}
}
+ private PointF calculateAppIconPosition(Rect parentBounds) {
+ return new PointF((float) parentBounds.width() / 2 - (float) mIconSize / 2,
+ (float) parentBounds.height() / 2 - (float) mIconSize / 2);
+ }
+
private void cancelAnimation() {
if (mVeilAnimator != null) {
mVeilAnimator.removeAllUpdateListeners();
@@ -260,11 +328,19 @@ public class ResizeVeil {
mViewHost.release();
mViewHost = null;
}
+ final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
+ if (mBackgroundSurface != null) {
+ t.remove(mBackgroundSurface);
+ mBackgroundSurface = null;
+ }
+ if (mIconSurface != null) {
+ t.remove(mIconSurface);
+ mIconSurface = null;
+ }
if (mVeilSurface != null) {
- final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
t.remove(mVeilSurface);
mVeilSurface = null;
- t.apply();
}
+ t.apply();
}
}
diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl
index c444740a5b1b..7a78f3d17e1e 100644
--- a/nfc/java/android/nfc/INfcAdapter.aidl
+++ b/nfc/java/android/nfc/INfcAdapter.aidl
@@ -47,8 +47,8 @@ interface INfcAdapter
INfcAdapterExtras getNfcAdapterExtrasInterface(in String pkg);
INfcDta getNfcDtaInterface(in String pkg);
int getState();
- boolean disable(boolean saveState);
- boolean enable();
+ boolean disable(boolean saveState, in String pkg);
+ boolean enable(in String pkg);
void pausePolling(int timeoutInMs);
void resumePolling();
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index 0ebc3f5178e0..7a7db31fd417 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -1117,7 +1117,7 @@ public final class NfcAdapter {
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
public boolean enable() {
try {
- return sService.enable();
+ return sService.enable(mContext.getPackageName());
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
// Try one more time
@@ -1126,7 +1126,7 @@ public final class NfcAdapter {
return false;
}
try {
- return sService.enable();
+ return sService.enable(mContext.getPackageName());
} catch (RemoteException ee) {
Log.e(TAG, "Failed to recover NFC Service.");
}
@@ -1156,7 +1156,7 @@ public final class NfcAdapter {
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
public boolean disable() {
try {
- return sService.disable(true);
+ return sService.disable(true, mContext.getPackageName());
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
// Try one more time
@@ -1165,7 +1165,7 @@ public final class NfcAdapter {
return false;
}
try {
- return sService.disable(true);
+ return sService.disable(true, mContext.getPackageName());
} catch (RemoteException ee) {
Log.e(TAG, "Failed to recover NFC Service.");
}
@@ -1181,7 +1181,7 @@ public final class NfcAdapter {
@RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
public boolean disable(boolean persist) {
try {
- return sService.disable(persist);
+ return sService.disable(persist, mContext.getPackageName());
} catch (RemoteException e) {
attemptDeadServiceRecovery(e);
// Try one more time
@@ -1190,7 +1190,7 @@ public final class NfcAdapter {
return false;
}
try {
- return sService.disable(persist);
+ return sService.disable(persist, mContext.getPackageName());
} catch (RemoteException ee) {
Log.e(TAG, "Failed to recover NFC Service.");
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 5629a7bf7b21..87a7f823edfe 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -269,6 +269,7 @@ public class SecureSettings {
Settings.Secure.CAMERA_EXTENSIONS_FALLBACK,
Settings.Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED,
Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS,
- Settings.Secure.AUDIO_DEVICE_INVENTORY
+ Settings.Secure.AUDIO_DEVICE_INVENTORY,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_TARGETS
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index b8d95eb5329d..edef286b6ac0 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -326,6 +326,9 @@ public class SecureSettingsValidators {
Secure.ACCESSIBILITY_BUTTON_TARGETS,
ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
VALIDATORS.put(
+ Secure.ACCESSIBILITY_FLOATING_MENU_TARGETS,
+ ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
+ VALIDATORS.put(
Secure.ACCESSIBILITY_QS_TARGETS,
ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR);
VALIDATORS.put(Secure.ACCESSIBILITY_FORCE_INVERT_COLOR_ENABLED, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index dba3bac4a4b8..4603b43b0ab5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1838,6 +1838,9 @@ class SettingsProtoDumpUtil {
Settings.Secure.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED,
SecureSettingsProto.Accessibility.ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED);
dumpSetting(s, p,
+ Settings.Secure.ACCESSIBILITY_FLOATING_MENU_TARGETS,
+ SecureSettingsProto.Accessibility.ACCESSIBILITY_FLOATING_MENU_TARGETS);
+ dumpSetting(s, p,
Settings.Secure.ODI_CAPTIONS_VOLUME_UI_ENABLED,
SecureSettingsProto.Accessibility.ODI_CAPTIONS_VOLUME_UI_ENABLED);
dumpSetting(s, p,
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_resize_veil_background.xml b/packages/SystemUI/res/drawable/biometric_prompt_content_view_background.xml
index 1f3e3a4c5b22..fdafe6d8e335 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_mode_resize_veil_background.xml
+++ b/packages/SystemUI/res/drawable/biometric_prompt_content_view_background.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2023 The Android Open Source Project
+<?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.
@@ -14,7 +13,11 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<shape android:shape="rectangle"
- xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="@android:color/white" />
+
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh"/>
+ <corners android:radius="@dimen/biometric_prompt_content_corner_radius"/>
</shape>
diff --git a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
index 2d63c8da54f9..2d5ec3e5110a 100644
--- a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
@@ -2,6 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
+xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -19,7 +20,7 @@ android:layout_height="match_parent">
android:id="@+id/panel"
android:layout_width="0dp"
android:layout_height="0dp"
- android:background="?android:attr/colorBackgroundFloating"
+ android:background="?androidprv:attr/materialColorSurfaceBright"
android:clickable="true"
android:clipToOutline="true"
android:importantForAccessibility="no"
@@ -74,8 +75,9 @@ android:layout_height="match_parent">
<ImageView
android:id="@+id/logo"
- android:layout_width="@dimen/biometric_auth_icon_size"
- android:layout_height="@dimen/biometric_auth_icon_size"
+ android:contentDescription="@string/biometric_dialog_logo"
+ android:layout_width="@dimen/biometric_prompt_logo_size"
+ android:layout_height="@dimen/biometric_prompt_logo_size"
android:layout_gravity="center"
android:scaleType="fitXY"
android:visibility="visible"
@@ -84,12 +86,9 @@ android:layout_height="match_parent">
<TextView
android:id="@+id/logo_description"
+ style="@style/TextAppearance.AuthCredential.LogoDescription"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:ellipsize="marquee"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:marqueeRepeatLimit="1"
- android:singleLine="true"
android:textAlignment="viewStart"
android:paddingLeft="8dp"
app:layout_constraintBottom_toBottomOf="@+id/logo"
@@ -97,12 +96,6 @@ android:layout_height="match_parent">
app:layout_constraintStart_toEndOf="@+id/logo"
app:layout_constraintTop_toTopOf="@+id/logo" />
- <Space
- android:id="@+id/space_above_content"
- android:layout_width="match_parent"
- android:layout_height="@dimen/biometric_prompt_space_above_content"
- android:visibility="gone" />
-
<TextView
android:id="@+id/title"
style="@style/TextAppearance.AuthCredential.Title"
@@ -137,11 +130,10 @@ android:layout_height="match_parent">
<LinearLayout
android:id="@+id/customized_view_container"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="vertical"
- android:paddingHorizontal="0dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
@@ -165,7 +157,6 @@ android:layout_height="match_parent">
app:layout_constraintTop_toBottomOf="@+id/subtitle"
app:layout_constraintVertical_bias="0.0" />
-
<androidx.constraintlayout.widget.Barrier
android:id="@+id/contentBarrier"
android:layout_width="wrap_content"
@@ -179,16 +170,14 @@ android:layout_height="match_parent">
<TextView
android:id="@+id/indicator"
+ style="@style/TextAppearance.AuthCredential.Indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:accessibilityLiveRegion="polite"
android:fadingEdge="horizontal"
android:gravity="center_horizontal"
- android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
- android:textColor="@color/biometric_dialog_gray"
- android:textSize="12sp"
app:layout_constraintBottom_toTopOf="@+id/buttonBarrier"
app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
app:layout_constraintStart_toStartOf="@+id/biometric_icon"
diff --git a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
index 329fc466d378..4d24c58d3937 100644
--- a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
@@ -2,6 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -19,7 +20,7 @@
android:id="@+id/panel"
android:layout_width="0dp"
android:layout_height="0dp"
- android:background="?android:attr/colorBackgroundFloating"
+ android:background="?androidprv:attr/materialColorSurfaceBright"
android:clickable="true"
android:clipToOutline="true"
android:importantForAccessibility="no"
@@ -61,8 +62,9 @@
<ImageView
android:id="@+id/logo"
- android:layout_width="@dimen/biometric_auth_icon_size"
- android:layout_height="@dimen/biometric_auth_icon_size"
+ android:contentDescription="@string/biometric_dialog_logo"
+ android:layout_width="@dimen/biometric_prompt_logo_size"
+ android:layout_height="@dimen/biometric_prompt_logo_size"
android:layout_gravity="center"
android:scaleType="fitXY"
android:visibility="visible"
@@ -73,24 +75,14 @@
<TextView
android:id="@+id/logo_description"
+ style="@style/TextAppearance.AuthCredential.LogoDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:ellipsize="marquee"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:marqueeRepeatLimit="1"
- android:singleLine="true"
- android:paddingTop="16dp"
app:layout_constraintBottom_toTopOf="@+id/title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/logo" />
- <Space
- android:id="@+id/space_above_content"
- android:layout_width="match_parent"
- android:layout_height="@dimen/biometric_prompt_space_above_content"
- android:visibility="gone" />
-
<TextView
android:id="@+id/title"
style="@style/TextAppearance.AuthCredential.Title"
@@ -117,11 +109,10 @@
<LinearLayout
android:id="@+id/customized_view_container"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="vertical"
- android:paddingHorizontal="@dimen/biometric_prompt_content_container_padding_horizontal"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
@@ -153,16 +144,14 @@
<!-- Cancel Button, replaces negative button when biometric is accepted -->
<TextView
android:id="@+id/indicator"
+ style="@style/TextAppearance.AuthCredential.Indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:accessibilityLiveRegion="polite"
android:fadingEdge="horizontal"
android:gravity="center_horizontal"
- android:marqueeRepeatLimit="marquee_forever"
android:scrollHorizontally="true"
- android:textColor="@color/biometric_dialog_gray"
- android:textSize="12sp"
app:layout_constraintBottom_toTopOf="@+id/buttonBarrier"
app:layout_constraintEnd_toEndOf="@+id/panel"
app:layout_constraintStart_toStartOf="@+id/panel"
diff --git a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
index 6391813754d0..56469daee326 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
@@ -2,6 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -19,7 +20,7 @@
android:id="@+id/panel"
android:layout_width="0dp"
android:layout_height="0dp"
- android:background="?android:attr/colorBackgroundFloating"
+ android:background="?androidprv:attr/materialColorSurfaceBright"
android:clickable="true"
android:clipToOutline="true"
android:importantForAccessibility="no"
@@ -61,8 +62,9 @@
<ImageView
android:id="@+id/logo"
- android:layout_width="@dimen/biometric_auth_icon_size"
- android:layout_height="@dimen/biometric_auth_icon_size"
+ android:contentDescription="@string/biometric_dialog_logo"
+ android:layout_width="@dimen/biometric_prompt_logo_size"
+ android:layout_height="@dimen/biometric_prompt_logo_size"
android:layout_gravity="center"
android:scaleType="fitXY"
app:layout_constraintBottom_toTopOf="@+id/logo_description"
@@ -73,21 +75,14 @@
<TextView
android:id="@+id/logo_description"
+ style="@style/TextAppearance.AuthCredential.LogoDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="@integer/biometric_dialog_text_gravity"
- android:singleLine="true"
app:layout_constraintBottom_toTopOf="@+id/title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/logo" />
- <Space
- android:id="@+id/space_above_content"
- android:layout_width="match_parent"
- android:layout_height="@dimen/biometric_prompt_space_above_content"
- android:visibility="gone" />
-
<TextView
android:id="@+id/title"
style="@style/TextAppearance.AuthCredential.Title"
@@ -119,7 +114,6 @@
android:gravity="center_vertical"
android:orientation="vertical"
android:visibility="gone"
- android:paddingTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
@@ -150,6 +144,7 @@
<TextView
android:id="@+id/indicator"
+ style="@style/TextAppearance.AuthCredential.Indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
@@ -229,5 +224,4 @@
app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
app:layout_constraintStart_toStartOf="@+id/biometric_icon"
app:layout_constraintTop_toTopOf="@+id/biometric_icon" />
-
</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/biometric_prompt_content_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_content_layout.xml
index 390875702cfe..0cc5e718e6d2 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_content_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_content_layout.xml
@@ -17,16 +17,11 @@
android:id="@+id/customized_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:orientation="vertical"
style="@style/AuthCredentialContentLayoutStyle">
<TextView
- android:id="@+id/customized_view_title"
- style="@style/TextAppearance.AuthCredential.ContentViewTitle"
+ android:id="@+id/customized_view_description"
+ style="@style/TextAppearance.AuthCredential.ContentViewDescription"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:ellipsize="marquee"
- android:marqueeRepeatLimit="1"
- android:singleLine="true" />
+ android:layout_height="wrap_content" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/biometric_prompt_content_row_item_text_view.xml b/packages/SystemUI/res/layout/biometric_prompt_content_row_item_text_view.xml
index e39f60f349bc..bc827081292e 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_content_row_item_text_view.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_content_row_item_text_view.xml
@@ -17,5 +17,5 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/TextAppearance.AuthCredential.ContentViewListItem"
android:layout_width="0dp"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_weight="1.0" /> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/biometric_prompt_content_row_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_content_row_layout.xml
index 6c867365e92c..f0125b60c6d8 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_content_row_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_content_row_layout.xml
@@ -16,6 +16,6 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="@dimen/biometric_prompt_content_list_row_height"
+ android:layout_height="wrap_content"
android:gravity="center_vertical|start"
android:orientation="horizontal" />
diff --git a/packages/SystemUI/res/layout/biometric_prompt_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_layout.xml
index 984210906e68..ff89ed9e6e7a 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_layout.xml
@@ -44,7 +44,7 @@
android:singleLine="true"
android:marqueeRepeatLimit="1"
android:ellipsize="marquee"
- style="@style/TextAppearance.AuthCredential.Title"/>
+ style="@style/TextAppearance.AuthCredential.OldTitle"/>
<TextView
android:id="@+id/subtitle"
@@ -54,20 +54,21 @@
android:singleLine="true"
android:marqueeRepeatLimit="1"
android:ellipsize="marquee"
- style="@style/TextAppearance.AuthCredential.Subtitle"/>
+ style="@style/TextAppearance.AuthCredential.OldSubtitle"/>
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:gravity="@integer/biometric_dialog_text_gravity"
android:scrollbars ="vertical"
android:importantForAccessibility="no"
- style="@style/TextAppearance.AuthCredential.Description"/>
+ style="@style/TextAppearance.AuthCredential.OldDescription"/>
<Space
android:id="@+id/space_above_content"
android:layout_width="match_parent"
- android:layout_height="@dimen/biometric_prompt_space_above_content"
+ android:layout_height="24dp"
android:visibility="gone" />
<LinearLayout
@@ -77,7 +78,6 @@
android:fadeScrollbars="false"
android:gravity="center_vertical"
android:orientation="vertical"
- android:paddingHorizontal="@dimen/biometric_prompt_content_container_padding_horizontal"
android:scrollbars="vertical"
android:visibility="gone" />
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 307a6192a570..590dc682564e 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -140,9 +140,6 @@
<color name="biometric_dialog_gray">#ff757575</color>
<color name="biometric_dialog_accent">@color/material_dynamic_primary40</color>
<color name="biometric_dialog_error">#ffd93025</color> <!-- red 600 -->
- <!-- Color for biometric prompt content view -->
- <color name="biometric_prompt_content_background_color">#8AB4F8</color>
- <color name="biometric_prompt_content_list_item_bullet_color">#1d873b</color>
<!-- SFPS colors -->
<color name="sfps_chevron_fill">@color/material_dynamic_primary90</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2285550d94c7..e004ee9fa157 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1101,15 +1101,15 @@
<dimen name="biometric_dialog_width">240dp</dimen>
<dimen name="biometric_dialog_height">240dp</dimen>
- <!-- Dimensions for biometric prompt content view. -->
- <dimen name="biometric_prompt_space_above_content">48dp</dimen>
- <dimen name="biometric_prompt_content_container_padding_horizontal">24dp</dimen>
- <dimen name="biometric_prompt_content_padding_horizontal">10dp</dimen>
- <dimen name="biometric_prompt_content_list_row_height">24dp</dimen>
- <dimen name="biometric_prompt_content_list_item_padding_horizontal">10dp</dimen>
- <dimen name="biometric_prompt_content_list_item_text_size">14sp</dimen>
- <dimen name="biometric_prompt_content_list_item_bullet_gap_width">10dp</dimen>
- <dimen name="biometric_prompt_content_list_item_bullet_radius">5dp</dimen>
+ <!-- Dimensions for biometric prompt custom content view. -->
+ <dimen name="biometric_prompt_logo_size">32dp</dimen>
+ <dimen name="biometric_prompt_content_corner_radius">28dp</dimen>
+ <dimen name="biometric_prompt_content_padding_horizontal">24dp</dimen>
+ <dimen name="biometric_prompt_content_padding_vertical">16dp</dimen>
+ <dimen name="biometric_prompt_content_space_width_between_items">16dp</dimen>
+ <dimen name="biometric_prompt_content_list_item_padding_top">12dp</dimen>
+ <dimen name="biometric_prompt_content_list_item_bullet_gap_width">8.5dp</dimen>
+ <dimen name="biometric_prompt_content_list_item_bullet_radius">1.5dp</dimen>
<!-- Biometric Auth Credential values -->
<dimen name="biometric_auth_icon_size">48dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3029888c7e54..5e9f811ce386 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -380,6 +380,8 @@
<!-- Button name for "Cancel". [CHAR LIMIT=NONE] -->
<string name="cancel">Cancel</string>
+ <!-- Content description for the app logo icon on biometric prompt. [CHAR LIMIT=NONE] -->
+ <string name="biometric_dialog_logo">App logo</string>
<!-- Message shown when a biometric is authenticated, asking the user to confirm authentication [CHAR LIMIT=30] -->
<string name="biometric_dialog_confirm">Confirm</string>
<!-- Button name on BiometricPrompt shown when a biometric is detected but not authenticated. Tapping the button resumes authentication [CHAR LIMIT=30] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 0483a0734a83..30de6475ea42 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -174,43 +174,59 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
- <style name="TextAppearance.AuthCredential.Title">
+ <style name="TextAppearance.AuthCredential.OldTitle">
<item name="android:fontFamily">google-sans</item>
<item name="android:paddingTop">12dp</item>
<item name="android:paddingHorizontal">24dp</item>
<item name="android:textSize">24sp</item>
</style>
- <style name="TextAppearance.AuthCredential.Subtitle">
+ <style name="TextAppearance.AuthCredential.OldSubtitle">
<item name="android:fontFamily">google-sans</item>
<item name="android:paddingTop">8dp</item>
<item name="android:paddingHorizontal">24dp</item>
<item name="android:textSize">16sp</item>
</style>
- <style name="TextAppearance.AuthCredential.Description">
+ <style name="TextAppearance.AuthCredential.OldDescription">
<item name="android:fontFamily">google-sans</item>
<item name="android:paddingTop">8dp</item>
<item name="android:paddingHorizontal">24dp</item>
<item name="android:textSize">14sp</item>
</style>
- <style name="TextAppearance.AuthCredential.ContentViewTitle">
- <item name="android:fontFamily">google-sans</item>
- <item name="android:paddingTop">8dp</item>
- <item name="android:paddingHorizontal">24dp</item>
- <item name="android:textSize">14sp</item>
- <item name="android:gravity">start</item>
+ <style name="TextAppearance.AuthCredential.LogoDescription" parent="TextAppearance.Material3.LabelLarge" >
+ <item name="android:ellipsize">marquee</item>
+ <item name="android:gravity">@integer/biometric_dialog_text_gravity</item>
+ <item name="android:marqueeRepeatLimit">1</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
</style>
- <style name="TextAppearance.AuthCredential.ContentViewListItem">
- <item name="android:fontFamily">google-sans</item>
- <item name="android:paddingTop">8dp</item>
- <item name="android:paddingHorizontal">
- @dimen/biometric_prompt_content_list_item_padding_horizontal
- </item>
- <item name="android:textSize">@dimen/biometric_prompt_content_list_item_text_size</item>
- <item name="android:gravity">start</item>
+ <style name="TextAppearance.AuthCredential.Title" parent="TextAppearance.Material3.HeadlineSmall" >
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ </style>
+
+ <style name="TextAppearance.AuthCredential.Subtitle" parent="TextAppearance.Material3.BodyMedium" >
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ </style>
+
+ <style name="TextAppearance.AuthCredential.Description" parent="TextAppearance.Material3.BodyMedium" >
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ </style>
+
+ <style name="TextAppearance.AuthCredential.ContentViewDescription" parent="TextAppearance.Material3.TitleSmall">
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ </style>
+
+ <style name="TextAppearance.AuthCredential.ContentViewListItem" parent="TextAppearance.Material3.BodySmall">
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
+ <item name="android:paddingTop">@dimen/biometric_prompt_content_list_item_padding_top</item>
+ </style>
+
+ <style name="TextAppearance.AuthCredential.Indicator" parent="TextAppearance.Material3.BodyMedium">
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
+ <item name="android:marqueeRepeatLimit">marquee_forever</item>
</style>
<style name="TextAppearance.AuthCredential.Error">
@@ -313,8 +329,11 @@
</style>
<style name="AuthCredentialContentLayoutStyle">
- <item name="android:background">@color/biometric_prompt_content_background_color</item>
+ <item name="android:gravity">center_vertical</item>
+ <item name="android:orientation">vertical</item>
+ <item name="android:background">@drawable/biometric_prompt_content_view_background</item>
<item name="android:paddingHorizontal">@dimen/biometric_prompt_content_padding_horizontal</item>
+ <item name="android:paddingVertical">@dimen/biometric_prompt_content_padding_vertical</item>
</style>
<style name="DeviceManagementDialogTitle">
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
index e58c8ff92c03..769e290c711e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricCustomizedViewBinder.kt
@@ -18,8 +18,6 @@ package com.android.systemui.biometrics.ui.binder
import android.content.Context
import android.content.res.Resources
-import android.content.res.Resources.Theme
-import android.graphics.Paint
import android.hardware.biometrics.PromptContentItem
import android.hardware.biometrics.PromptContentItemBulletedText
import android.hardware.biometrics.PromptContentItemPlainText
@@ -27,14 +25,17 @@ import android.hardware.biometrics.PromptContentView
import android.hardware.biometrics.PromptVerticalListContentView
import android.text.SpannableString
import android.text.Spanned
+import android.text.TextPaint
import android.text.style.BulletSpan
import android.view.LayoutInflater
import android.view.View
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewTreeObserver
import android.widget.LinearLayout
import android.widget.Space
import android.widget.TextView
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.repeatOnLifecycle
+import androidx.lifecycle.lifecycleScope
+import com.android.settingslib.Utils
import com.android.systemui.biometrics.ui.BiometricPromptLayout
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -45,36 +46,38 @@ import kotlinx.coroutines.launch
/** Sub-binder for [BiometricPromptLayout.customized_view_container]. */
object BiometricCustomizedViewBinder {
- fun bind(customizedViewContainer: LinearLayout, spaceAbove: Space, viewModel: PromptViewModel) {
- customizedViewContainer.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch {
- val contentView: PromptContentView? = viewModel.contentView.first()
-
- if (contentView != null) {
- val context = customizedViewContainer.context
- customizedViewContainer.addView(contentView.toView(context))
- customizedViewContainer.visibility = View.VISIBLE
- spaceAbove.visibility = View.VISIBLE
- } else {
- customizedViewContainer.visibility = View.GONE
- spaceAbove.visibility = View.GONE
+ fun bind(customizedViewContainer: LinearLayout, viewModel: PromptViewModel) {
+ customizedViewContainer.repeatWhenAttached { containerView ->
+ lifecycleScope.launch {
+ val contentView: PromptContentView? = viewModel.contentView.first()
+ if (contentView == null) {
+ containerView.visibility = View.GONE
+ return@launch
+ }
+
+ containerView.width { containerWidth ->
+ if (containerWidth == 0) {
+ return@width
}
+ (containerView as LinearLayout).addView(
+ contentView.toView(containerView.context, containerWidth)
+ )
+ containerView.visibility = View.VISIBLE
}
}
}
}
}
-private fun PromptContentView.toView(context: Context): View {
- val resources = context.resources
+private fun PromptContentView.toView(context: Context, containerViewWidth: Int): View {
val inflater = LayoutInflater.from(context)
when (this) {
is PromptVerticalListContentView -> {
val contentView =
inflater.inflate(R.layout.biometric_prompt_content_layout, null) as LinearLayout
- val descriptionView = contentView.requireViewById<TextView>(R.id.customized_view_title)
+ val descriptionView =
+ contentView.requireViewById<TextView>(R.id.customized_view_description)
if (!description.isNullOrEmpty()) {
descriptionView.text = description
} else {
@@ -83,13 +86,36 @@ private fun PromptContentView.toView(context: Context): View {
// Show two column by default, once there is an item exceeding max lines, show single
// item instead.
- val showTwoColumn = listItems.all { !it.doesExceedMaxLinesIfTwoColumn(resources) }
+ val showTwoColumn =
+ listItems.all { !it.doesExceedMaxLinesIfTwoColumn(context, containerViewWidth) }
var currRowView = createNewRowLayout(inflater)
for (item in listItems) {
- val itemView = item.toView(resources, inflater, context.theme)
+ val itemView = item.toView(context, inflater)
+ // If this item will be in the first row (contentView only has description view) and
+ // description is empty, remove top padding of this item.
+ if (contentView.childCount == 1 && description.isNullOrEmpty()) {
+ itemView.setPadding(
+ itemView.paddingLeft,
+ 0,
+ itemView.paddingRight,
+ itemView.paddingBottom
+ )
+ }
currRowView.addView(itemView)
- if (!showTwoColumn || currRowView.childCount == 2) {
+ // If this is the first item in the current row, add space behind it.
+ if (currRowView.childCount == 1 && showTwoColumn) {
+ currRowView.addSpaceView(
+ context.resources.getDimensionPixelSize(
+ R.dimen.biometric_prompt_content_space_width_between_items
+ ),
+ MATCH_PARENT
+ )
+ }
+
+ // If there are already two items (plus the space view) in the current row, or it
+ // should be one column, start a new row
+ if (currRowView.childCount == 3 || !showTwoColumn) {
contentView.addView(currRowView)
currRowView = createNewRowLayout(inflater)
}
@@ -110,9 +136,15 @@ private fun createNewRowLayout(inflater: LayoutInflater): LinearLayout {
return inflater.inflate(R.layout.biometric_prompt_content_row_layout, null) as LinearLayout
}
+private fun LinearLayout.addSpaceView(width: Int, height: Int) {
+ addView(Space(context), LinearLayout.LayoutParams(width, height))
+}
+
private fun PromptContentItem.doesExceedMaxLinesIfTwoColumn(
- resources: Resources,
+ context: Context,
+ containerViewWidth: Int,
): Boolean {
+ val resources = context.resources
val passedInText: String =
when (this) {
is PromptContentItemPlainText -> text
@@ -125,32 +157,26 @@ private fun PromptContentItem.doesExceedMaxLinesIfTwoColumn(
when (this) {
is PromptContentItemPlainText,
is PromptContentItemBulletedText -> {
- val dialogMargin =
- resources.getDimensionPixelSize(R.dimen.biometric_dialog_border_padding)
- val halfDialogWidth =
- Resources.getSystem().displayMetrics.widthPixels / 2 - dialogMargin
- val containerPadding =
- resources.getDimensionPixelSize(
- R.dimen.biometric_prompt_content_container_padding_horizontal
- )
- val contentPadding =
+ val contentViewPadding =
resources.getDimensionPixelSize(R.dimen.biometric_prompt_content_padding_horizontal)
val listItemPadding = getListItemPadding(resources)
- val maxWidth = halfDialogWidth - containerPadding - contentPadding - listItemPadding
+ val maxWidth = containerViewWidth / 2 - contentViewPadding - listItemPadding
- val text = "$passedInText"
- val textSize =
- resources.getDimensionPixelSize(
- R.dimen.biometric_prompt_content_list_item_text_size
+ val paint = TextPaint()
+ val attributes =
+ context.obtainStyledAttributes(
+ R.style.TextAppearance_AuthCredential_ContentViewListItem,
+ intArrayOf(android.R.attr.textSize)
)
- val paint = Paint()
- paint.textSize = textSize.toFloat()
+ paint.textSize = attributes.getDimensionPixelSize(0, 0).toFloat()
+ val textWidth = paint.measureText(passedInText)
+ attributes.recycle()
val maxLines =
resources.getInteger(
R.integer.biometric_prompt_content_list_item_max_lines_if_two_column
)
- val numLines = ceil(paint.measureText(text).toDouble() / maxWidth).toInt()
+ val numLines = ceil(textWidth / maxWidth).toInt()
return numLines > maxLines
}
else -> {
@@ -160,10 +186,10 @@ private fun PromptContentItem.doesExceedMaxLinesIfTwoColumn(
}
private fun PromptContentItem.toView(
- resources: Resources,
+ context: Context,
inflater: LayoutInflater,
- theme: Theme,
): TextView {
+ val resources = context.resources
val textView =
inflater.inflate(R.layout.biometric_prompt_content_row_item_text_view, null) as TextView
val lp = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.MATCH_PARENT, 1f)
@@ -178,7 +204,7 @@ private fun PromptContentItem.toView(
val span =
BulletSpan(
getListItemBulletGapWidth(resources),
- getListItemBulletColor(resources, theme),
+ getListItemBulletColor(context),
getListItemBulletRadius(resources)
)
bulletedText.setSpan(span, 0 /* start */, text.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
@@ -194,8 +220,8 @@ private fun PromptContentItem.toView(
private fun PromptContentItem.getListItemPadding(resources: Resources): Int {
var listItemPadding =
resources.getDimensionPixelSize(
- R.dimen.biometric_prompt_content_list_item_padding_horizontal
- ) * 2
+ R.dimen.biometric_prompt_content_space_width_between_items
+ ) / 2
when (this) {
is PromptContentItemPlainText -> {}
is PromptContentItemBulletedText -> {
@@ -215,5 +241,20 @@ private fun getListItemBulletRadius(resources: Resources): Int =
private fun getListItemBulletGapWidth(resources: Resources): Int =
resources.getDimensionPixelSize(R.dimen.biometric_prompt_content_list_item_bullet_gap_width)
-private fun getListItemBulletColor(resources: Resources, theme: Theme): Int =
- resources.getColor(R.color.biometric_prompt_content_list_item_bullet_color, theme)
+private fun getListItemBulletColor(context: Context): Int =
+ Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.materialColorOnSurface)
+
+private fun <T : View> T.width(function: (Int) -> Unit) {
+ if (width == 0)
+ viewTreeObserver.addOnGlobalLayoutListener(
+ object : ViewTreeObserver.OnGlobalLayoutListener {
+ override fun onGlobalLayout() {
+ if (measuredWidth > 0) {
+ viewTreeObserver.removeOnGlobalLayoutListener(this)
+ }
+ function(measuredWidth)
+ }
+ }
+ )
+ else function(measuredWidth)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 7bb75bf5ca9b..37d234888144 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -169,11 +169,7 @@ object BiometricViewBinder {
descriptionView.text = viewModel.description.first()
if (Flags.customBiometricPrompt() && constraintBp()) {
- BiometricCustomizedViewBinder.bind(
- customizedViewContainer,
- view.requireViewById(R.id.space_above_content),
- viewModel
- )
+ BiometricCustomizedViewBinder.bind(customizedViewContainer, viewModel)
}
// set button listeners
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index 1dfd2e5f9cc9..e3c0cba42e2d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -121,10 +121,6 @@ object BiometricViewSizeBinder {
val largeConstraintSet = ConstraintSet()
largeConstraintSet.clone(mediumConstraintSet)
- largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
- largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
- largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
- largeConstraintSet.setVisibility(R.id.scrollView, View.GONE)
// TODO: Investigate better way to handle 180 rotations
val flipConstraintSet = ConstraintSet()
@@ -286,6 +282,10 @@ object BiometricViewSizeBinder {
fun setVisibilities(size: PromptSize) {
viewsToHideWhenSmall.forEach { it.showContentOrHide(forceHide = size.isSmall) }
+ largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+ largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
+ largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
+ largeConstraintSet.setVisibility(R.id.scrollView, View.GONE)
if (viewModel.showBpWithoutIconForCredential.value) {
smallConstraintSet.setVisibility(iconHolderView.id, View.GONE)
diff --git a/ravenwood/README.md b/ravenwood/README.md
index 8cafb433736f..9c4fda7a50a6 100644
--- a/ravenwood/README.md
+++ b/ravenwood/README.md
@@ -1,11 +1,9 @@
# Ravenwood
-Ravenwood is a lightweight unit testing environment for Android platform code that runs on the host.
+Ravenwood is an officially-supported lightweight unit testing environment for Android platform code that runs on the host.
Ravenwood’s focus on Android platform use-cases, improved maintainability, and device consistency distinguishes it from Robolectric, which remains a popular choice for app testing.
-> **Note:** Active development of Ravenwood has been paused as of March 2024. Existing Ravenwood tests will continue running, but support has moved to a self-service model.
-
## Background
Executing tests on a typical Android device has substantial overhead, such as flashing the build, waiting for the boot to complete, and retrying tests that fail due to general flakiness.
diff --git a/ravenwood/api-maintainers.md b/ravenwood/api-maintainers.md
index c059cabd14e2..4b2f96804c97 100644
--- a/ravenwood/api-maintainers.md
+++ b/ravenwood/api-maintainers.md
@@ -4,7 +4,7 @@ By default, Android APIs aren’t opted-in to Ravenwood, and they default to thr
To opt-in to supporting an API under Ravenwood, you can use the inline annotations documented below to customize your API behavior when running under Ravenwood. Because these annotations are inline in the relevant platform source code, they serve as valuable reminders to future API maintainers of Ravenwood support expectations.
-> **Note:** Active development of Ravenwood has been paused as of March 2024. Currently supported APIs will continue working, but the addition of new APIs is not currently being supported. There is an allowlist that restricts where Ravenwood-specific annotations can be used, and that allowlist is not being expanded while development is paused.
+> **Note:** to ensure that API teams are well-supported during early Ravenwood onboarding, the Ravenwood team is manually maintaining an allow-list of classes that are able to use Ravenwood annotations. Please reach out to ravenwood@ so we can offer design advice and allow-list your APIs.
These Ravenwood-specific annotations have no bearing on the status of an API being public, `@SystemApi`, `@TestApi`, `@hide`, etc. Ravenwood annotations are an orthogonal concept that are only consumed by the internal `hoststubgen` tool during a post-processing step that generates the Ravenwood runtime environment. Teams that own APIs can continue to refactor opted-in `@hide` implementation details, as long as the test-visible behavior continues passing.
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 133a77df3573..04dd2f3fa288 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -8154,7 +8154,7 @@ public final class ActiveServices {
BackgroundStartPrivileges.NONE);
@ReasonCode int allowStartFgs = shouldAllowFgsStartForegroundNoBindingCheckLocked(
allowWhileInUse, callingPid, callingUid, callingPackage, null /* targetService */,
- BackgroundStartPrivileges.NONE);
+ BackgroundStartPrivileges.NONE, null);
if (allowStartFgs == REASON_DENIED) {
if (canBindingClientStartFgsLocked(callingUid) != null) {
@@ -8410,7 +8410,8 @@ public final class ActiveServices {
allowWhileInUse2,
clientPid, clientUid, clientPackageName,
null /* targetService */,
- BackgroundStartPrivileges.NONE);
+ BackgroundStartPrivileges.NONE,
+ pr);
if (allowStartFgs != REASON_DENIED) {
return new Pair<>(allowStartFgs, clientPackageName);
} else {
@@ -8447,7 +8448,7 @@ public final class ActiveServices {
ActivityManagerService.FgsTempAllowListItem tempAllowListReason =
r.mInfoTempFgsAllowListReason = mAm.isAllowlistedForFgsStartLOSP(callingUid);
int ret = shouldAllowFgsStartForegroundNoBindingCheckLocked(allowWhileInUse, callingPid,
- callingUid, callingPackage, r, backgroundStartPrivileges);
+ callingUid, callingPackage, r, backgroundStartPrivileges, null);
// If an app (App 1) is bound by another app (App 2) that could start an FGS, then App 1
// is also allowed to start an FGS. We check all the binding
@@ -8503,7 +8504,8 @@ public final class ActiveServices {
private @ReasonCode int shouldAllowFgsStartForegroundNoBindingCheckLocked(
@ReasonCode int allowWhileInUse, int callingPid, int callingUid, String callingPackage,
@Nullable ServiceRecord targetService,
- BackgroundStartPrivileges backgroundStartPrivileges) {
+ BackgroundStartPrivileges backgroundStartPrivileges,
+ @Nullable ProcessRecord targetRecord) {
int ret = allowWhileInUse;
if (ret == REASON_DENIED) {
@@ -8565,13 +8567,15 @@ public final class ActiveServices {
if (ret == REASON_DENIED) {
// Flag check: are we disabling SAW FGS background starts?
final boolean shouldDisableSaw = Flags.fgsDisableSaw()
- && CompatChanges.isChangeEnabled(FGS_BOOT_COMPLETED_RESTRICTIONS, callingUid);
+ && CompatChanges.isChangeEnabled(FGS_SAW_RESTRICTIONS, callingUid);
if (shouldDisableSaw) {
- final ProcessRecord processRecord = mAm
- .getProcessRecordLocked(targetService.processName,
- targetService.appInfo.uid);
- if (processRecord != null) {
- if (processRecord.mState.hasOverlayUi()) {
+ if (targetRecord == null) {
+ synchronized (mAm.mPidsSelfLocked) {
+ targetRecord = mAm.mPidsSelfLocked.get(callingPid);
+ }
+ }
+ if (targetRecord != null) {
+ if (targetRecord.mState.hasOverlayUi()) {
if (mAm.mAtmInternal.hasSystemAlertWindowPermission(callingUid, callingPid,
callingPackage)) {
ret = REASON_SYSTEM_ALERT_WINDOW_PERMISSION;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4364f16ff0e1..4f1a35c3fbd4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2656,6 +2656,11 @@ public class ActivityManagerService extends IActivityManager.Stub
return mBackgroundLaunchBroadcasts;
}
+ private String getWearRemoteIntentAction() {
+ return mContext.getResources().getString(
+ com.android.internal.R.string.config_wearRemoteIntentAction);
+ }
+
/**
* Ensures that the given package name has an explicit set of allowed associations.
* If it does not, give it an empty set.
@@ -15213,6 +15218,18 @@ public class ActivityManagerService extends IActivityManager.Stub
intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
}
+ // TODO: b/329211459 - Remove this after background remote intent is fixed.
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
+ && getWearRemoteIntentAction().equals(action)) {
+ final int callerProcState = callerApp != null
+ ? callerApp.getCurProcState()
+ : ActivityManager.PROCESS_STATE_NONEXISTENT;
+ if (ActivityManager.RunningAppProcessInfo.procStateToImportance(callerProcState)
+ > ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
+ return ActivityManager.START_CANCELED;
+ }
+ }
+
switch (action) {
case Intent.ACTION_MEDIA_SCANNER_SCAN_FILE:
UserManagerInternal umInternal = LocalServices.getService(
@@ -19630,7 +19647,7 @@ public class ActivityManagerService extends IActivityManager.Stub
record.procStateSeqWaitingForNetwork = 0;
final long totalTime = SystemClock.uptimeMillis() - startTime;
if (totalTime >= mConstants.mNetworkAccessTimeoutMs || DEBUG_NETWORK) {
- Slog.w(TAG_NETWORK, "Total time waited for network rules to get updated: "
+ Slog.wtf(TAG_NETWORK, "Total time waited for network rules to get updated: "
+ totalTime + ". Uid: " + callingUid + " procStateSeq: "
+ procStateSeq + " UidRec: " + record
+ " validateUidRec: "
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 25095edda5d8..22f5332e150c 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -1214,16 +1214,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
return false;
}
final int previousProcState = previousInfo.procState;
- if (mBackgroundNetworkRestricted && (previousProcState >= BACKGROUND_THRESHOLD_STATE)
- != (newProcState >= BACKGROUND_THRESHOLD_STATE)) {
- // Proc-state change crossed BACKGROUND_THRESHOLD_STATE: Network rules for the
- // BACKGROUND chain may change.
- return true;
- }
if ((previousProcState <= TOP_THRESHOLD_STATE)
- != (newProcState <= TOP_THRESHOLD_STATE)) {
- // Proc-state change crossed TOP_THRESHOLD_STATE: Network rules for the
- // LOW_POWER_STANDBY chain may change.
+ || (newProcState <= TOP_THRESHOLD_STATE)) {
+ // If the proc-state change crossed TOP_THRESHOLD_STATE, network rules for the
+ // LOW_POWER_STANDBY chain may change, so we need to evaluate the transition.
+ // In addition, we always process changes when the new process state is
+ // TOP_THRESHOLD_STATE or below, to avoid situations where the TOP app ends up
+ // waiting for NPMS to finish processing newProcStateSeq, even when it was
+ // redundant (b/327303931).
return true;
}
if ((previousProcState <= FOREGROUND_THRESHOLD_STATE)
@@ -1232,6 +1230,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// different chains may change.
return true;
}
+ if (mBackgroundNetworkRestricted && (previousProcState >= BACKGROUND_THRESHOLD_STATE)
+ != (newProcState >= BACKGROUND_THRESHOLD_STATE)) {
+ // Proc-state change crossed BACKGROUND_THRESHOLD_STATE: Network rules for the
+ // BACKGROUND chain may change.
+ return true;
+ }
final int networkCapabilities = PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK
| PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK;
if ((previousInfo.capability & networkCapabilities)
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 124970758fa5..3cab75b5d320 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -2315,10 +2315,11 @@ public class NetworkPolicyManagerServiceTest {
}
waitForUidEventHandlerIdle();
try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
- // Doesn't cross any other threshold.
+ // Doesn't cross any threshold, but changes below TOP_THRESHOLD_STATE should always
+ // be processed
callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE - 1, testProcStateSeq++,
PROCESS_CAPABILITY_NONE);
- assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
}
waitForUidEventHandlerIdle();
}
@@ -2349,21 +2350,21 @@ public class NetworkPolicyManagerServiceTest {
int testProcStateSeq = 0;
try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
// First callback for uid.
- callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++,
+ callOnUidStatechanged(UID_A, FOREGROUND_THRESHOLD_STATE, testProcStateSeq++,
PROCESS_CAPABILITY_NONE);
assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
}
waitForUidEventHandlerIdle();
try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
// The same process-state with one network capability added.
- callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++,
+ callOnUidStatechanged(UID_A, FOREGROUND_THRESHOLD_STATE, testProcStateSeq++,
PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK);
assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
}
waitForUidEventHandlerIdle();
try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
// The same process-state with another network capability added.
- callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++,
+ callOnUidStatechanged(UID_A, FOREGROUND_THRESHOLD_STATE, testProcStateSeq++,
PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK
| PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK);
assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
@@ -2371,11 +2372,21 @@ public class NetworkPolicyManagerServiceTest {
waitForUidEventHandlerIdle();
try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
// The same process-state with all capabilities, but no change in network capabilities.
- callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++,
+ callOnUidStatechanged(UID_A, FOREGROUND_THRESHOLD_STATE, testProcStateSeq++,
PROCESS_CAPABILITY_ALL);
assertFalse(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
}
waitForUidEventHandlerIdle();
+
+ callAndWaitOnUidStateChanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++,
+ PROCESS_CAPABILITY_ALL);
+ try (SyncBarrier b = new SyncBarrier(mService.mUidEventHandler)) {
+ // No change in capabilities, but TOP_THRESHOLD_STATE change should always be processed.
+ callOnUidStatechanged(UID_A, TOP_THRESHOLD_STATE, testProcStateSeq++,
+ PROCESS_CAPABILITY_ALL);
+ assertTrue(mService.mUidEventHandler.hasMessages(UID_MSG_STATE_CHANGED));
+ }
+ waitForUidEventHandlerIdle();
}
@Test
diff --git a/wifi/java/src/android/net/wifi/WifiBlobStore.java b/wifi/java/src/android/net/wifi/WifiBlobStore.java
new file mode 100644
index 000000000000..8bfaae72f932
--- /dev/null
+++ b/wifi/java/src/android/net/wifi/WifiBlobStore.java
@@ -0,0 +1,39 @@
+/*
+ * 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.net.wifi;
+
+import com.android.internal.net.ConnectivityBlobStore;
+
+/**
+ * Database blob store for Wifi.
+ * @hide
+ */
+public class WifiBlobStore extends ConnectivityBlobStore {
+ private static final String DB_NAME = "WifiBlobStore.db";
+ private static WifiBlobStore sInstance;
+ private WifiBlobStore() {
+ super(DB_NAME);
+ }
+
+ /** Returns an instance of WifiBlobStore. */
+ public static WifiBlobStore getInstance() {
+ if (sInstance == null) {
+ sInstance = new WifiBlobStore();
+ }
+ return sInstance;
+ }
+}
diff --git a/wifi/java/src/android/net/wifi/WifiKeystore.java b/wifi/java/src/android/net/wifi/WifiKeystore.java
index 1cda0326bf6c..a06d0eeade72 100644
--- a/wifi/java/src/android/net/wifi/WifiKeystore.java
+++ b/wifi/java/src/android/net/wifi/WifiKeystore.java
@@ -18,12 +18,17 @@ package android.net.wifi;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.os.Binder;
import android.os.Process;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.security.legacykeystore.ILegacyKeystore;
import android.util.Log;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
/**
* This class allows the storage and retrieval of non-standard Wifi certificate blobs.
* @hide
@@ -34,7 +39,7 @@ public final class WifiKeystore {
private static final String TAG = "WifiKeystore";
private static final String LEGACY_KEYSTORE_SERVICE_NAME = "android.security.legacykeystore";
- private static ILegacyKeystore getService() {
+ private static ILegacyKeystore getLegacyKeystore() {
return ILegacyKeystore.Stub.asInterface(
ServiceManager.checkService(LEGACY_KEYSTORE_SERVICE_NAME));
}
@@ -54,13 +59,18 @@ public final class WifiKeystore {
@SystemApi
@SuppressLint("UnflaggedApi")
public static boolean put(@NonNull String alias, @NonNull byte[] blob) {
+ // ConnectivityBlobStore uses the calling uid as a key into the DB.
+ // Clear identity to ensure that callers from system apps and the Wifi framework
+ // are able to access the same values.
+ final long identity = Binder.clearCallingIdentity();
try {
Log.i(TAG, "put blob. alias " + alias);
- getService().put(alias, Process.WIFI_UID, blob);
- return true;
+ return WifiBlobStore.getInstance().put(alias, blob);
} catch (Exception e) {
Log.e(TAG, "Failed to put blob.", e);
return false;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
@@ -69,23 +79,31 @@ public final class WifiKeystore {
* @param alias Name of the blob to retrieve.
* @return The unstructured blob, that is the blob that was stored using
* {@link android.net.wifi.WifiKeystore#put}.
- * Returns null if no blob was found.
+ * Returns empty byte[] if no blob was found.
* @hide
*/
@SystemApi
@SuppressLint("UnflaggedApi")
public static @NonNull byte[] get(@NonNull String alias) {
+ final long identity = Binder.clearCallingIdentity();
try {
Log.i(TAG, "get blob. alias " + alias);
- return getService().get(alias, Process.WIFI_UID);
+ byte[] blob = WifiBlobStore.getInstance().get(alias);
+ if (blob != null) {
+ return blob;
+ }
+ Log.i(TAG, "Searching for blob in Legacy Keystore");
+ return getLegacyKeystore().get(alias, Process.WIFI_UID);
} catch (ServiceSpecificException e) {
if (e.errorCode != ILegacyKeystore.ERROR_ENTRY_NOT_FOUND) {
Log.e(TAG, "Failed to get blob.", e);
}
} catch (Exception e) {
Log.e(TAG, "Failed to get blob.", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- return null;
+ return new byte[0];
}
/**
@@ -97,17 +115,27 @@ public final class WifiKeystore {
@SystemApi
@SuppressLint("UnflaggedApi")
public static boolean remove(@NonNull String alias) {
+ boolean blobStoreSuccess = false;
+ boolean legacyKsSuccess = false;
+ final long identity = Binder.clearCallingIdentity();
try {
- getService().remove(alias, Process.WIFI_UID);
- return true;
+ Log.i(TAG, "remove blob. alias " + alias);
+ blobStoreSuccess = WifiBlobStore.getInstance().remove(alias);
+ // Legacy Keystore will throw an exception if the alias is not found.
+ getLegacyKeystore().remove(alias, Process.WIFI_UID);
+ legacyKsSuccess = true;
} catch (ServiceSpecificException e) {
if (e.errorCode != ILegacyKeystore.ERROR_ENTRY_NOT_FOUND) {
Log.e(TAG, "Failed to remove blob.", e);
}
} catch (Exception e) {
Log.e(TAG, "Failed to remove blob.", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
- return false;
+ Log.i(TAG, "Removal status: wifiBlobStore=" + blobStoreSuccess
+ + ", legacyKeystore=" + legacyKsSuccess);
+ return blobStoreSuccess || legacyKsSuccess;
}
/**
@@ -119,14 +147,24 @@ public final class WifiKeystore {
@SystemApi
@SuppressLint("UnflaggedApi")
public static @NonNull String[] list(@NonNull String prefix) {
+ final long identity = Binder.clearCallingIdentity();
try {
- final String[] aliases = getService().list(prefix, Process.WIFI_UID);
- for (int i = 0; i < aliases.length; ++i) {
- aliases[i] = aliases[i].substring(prefix.length());
+ // Aliases from WifiBlobStore will be pre-trimmed.
+ final String[] blobStoreAliases = WifiBlobStore.getInstance().list(prefix);
+ final String[] legacyAliases = getLegacyKeystore().list(prefix, Process.WIFI_UID);
+ for (int i = 0; i < legacyAliases.length; ++i) {
+ legacyAliases[i] = legacyAliases[i].substring(prefix.length());
}
- return aliases;
+ // Deduplicate aliases before returning.
+ Set<String> uniqueAliases = new HashSet<>();
+ uniqueAliases.addAll(Arrays.asList(blobStoreAliases));
+ uniqueAliases.addAll(Arrays.asList(legacyAliases));
+ String[] uniqueAliasArray = new String[uniqueAliases.size()];
+ return uniqueAliases.toArray(uniqueAliasArray);
} catch (Exception e) {
Log.e(TAG, "Failed to list blobs.", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
return new String[0];
}