Merge "Add aconfig flag for media refactor." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 2047168..eed248b 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -310,6 +310,8 @@
aconfig_declarations {
name: "android.os.flags-aconfig",
package: "android.os",
+ exportable: true,
+ container: "system",
srcs: ["core/java/android/os/*.aconfig"],
}
@@ -326,6 +328,13 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.os.flags-aconfig-java-export",
+ aconfig_declarations: "android.os.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+ mode: "exported",
+}
+
// VirtualDeviceManager
cc_aconfig_library {
name: "android.companion.virtualdevice.flags-aconfig-cc",
diff --git a/apex/jobscheduler/service/aconfig/alarm.aconfig b/apex/jobscheduler/service/aconfig/alarm.aconfig
index 3b9b4e7..bb0f3cb 100644
--- a/apex/jobscheduler/service/aconfig/alarm.aconfig
+++ b/apex/jobscheduler/service/aconfig/alarm.aconfig
@@ -9,3 +9,10 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "start_user_before_scheduled_alarms"
+ namespace: "multiuser"
+ description: "Persist list of users with alarms scheduled and wakeup stopped users before alarms are due"
+ bug: "314907186"
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index e728a2c..d0a1b02 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -183,6 +183,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.zone.ZoneOffsetTransition;
+import java.time.zone.ZoneRules;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -194,6 +197,7 @@
import java.util.TimeZone;
import java.util.TreeSet;
import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
/**
@@ -234,8 +238,12 @@
private static final long TEMPORARY_QUOTA_DURATION = INTERVAL_DAY;
- // System property read on some device configurations to initialize time properly.
+ // System properties read on some device configurations to initialize time properly and
+ // perform DST transitions at the bootloader level.
private static final String TIMEOFFSET_PROPERTY = "persist.sys.time.offset";
+ private static final String DST_TRANSITION_PROPERTY = "persist.sys.time.dst_transition";
+ private static final String DST_OFFSET_PROPERTY = "persist.sys.time.dst_offset";
+
private final Intent mBackgroundIntent
= new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
@@ -2200,6 +2208,19 @@
final int gmtOffset = newZone.getOffset(mInjector.getCurrentTimeMillis());
SystemProperties.set(TIMEOFFSET_PROPERTY, String.valueOf(gmtOffset));
+
+ final ZoneRules rules = newZone.toZoneId().getRules();
+ final ZoneOffsetTransition transition = rules.nextTransition(Instant.now());
+ if (null != transition) {
+ // Get the offset between the time after the DST transition and before.
+ final long transitionOffset = TimeUnit.SECONDS.toMillis((
+ transition.getOffsetAfter().getTotalSeconds()
+ - transition.getOffsetBefore().getTotalSeconds()));
+ // Time when the next DST transition is programmed.
+ final long nextTransition = TimeUnit.SECONDS.toMillis(transition.toEpochSecond());
+ SystemProperties.set(DST_TRANSITION_PROPERTY, String.valueOf(nextTransition));
+ SystemProperties.set(DST_OFFSET_PROPERTY, String.valueOf(transitionOffset));
+ }
}
// Clear the default time zone in the system server process. This forces the next call
diff --git a/api/StubLibraries.bp b/api/StubLibraries.bp
index 1f5b83c..c1add03 100644
--- a/api/StubLibraries.bp
+++ b/api/StubLibraries.bp
@@ -458,13 +458,21 @@
libs: ["stub-annotations"],
}
+java_defaults {
+ name: "android-non-updatable_everything_from_text_defaults",
+ defaults: [
+ "android-non-updatable_from_text_defaults",
+ ],
+ stubs_type: "everything",
+}
+
java_api_library {
name: "android-non-updatable.stubs.from-text",
api_surface: "public",
api_contributions: [
"api-stubs-docs-non-updatable.api.contribution",
],
- defaults: ["android-non-updatable_from_text_defaults"],
+ defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_stubs_current.from-text",
}
@@ -475,7 +483,7 @@
"api-stubs-docs-non-updatable.api.contribution",
"system-api-stubs-docs-non-updatable.api.contribution",
],
- defaults: ["android-non-updatable_from_text_defaults"],
+ defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_system_stubs_current.from-text",
}
@@ -487,7 +495,7 @@
"system-api-stubs-docs-non-updatable.api.contribution",
"test-api-stubs-docs-non-updatable.api.contribution",
],
- defaults: ["android-non-updatable_from_text_defaults"],
+ defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_test_stubs_current.from-text",
}
@@ -499,7 +507,7 @@
"system-api-stubs-docs-non-updatable.api.contribution",
"module-lib-api-stubs-docs-non-updatable.api.contribution",
],
- defaults: ["android-non-updatable_from_text_defaults"],
+ defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_module_lib_stubs_current_full.from-text",
}
@@ -515,7 +523,7 @@
"test-api-stubs-docs-non-updatable.api.contribution",
"module-lib-api-stubs-docs-non-updatable.api.contribution",
],
- defaults: ["android-non-updatable_from_text_defaults"],
+ defaults: ["android-non-updatable_everything_from_text_defaults"],
full_api_surface_stub: "android_test_module_lib_stubs_current.from-text",
// This module is only used for hiddenapi, and other modules should not
@@ -836,6 +844,7 @@
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -852,6 +861,7 @@
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -870,6 +880,7 @@
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -888,6 +899,7 @@
"system-api-stubs-docs-non-updatable.api.contribution",
],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -908,6 +920,7 @@
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -922,6 +935,7 @@
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -947,6 +961,7 @@
"//visibility:private",
],
enable_validation: false,
+ stubs_type: "everything",
}
java_api_library {
@@ -964,6 +979,7 @@
],
visibility: ["//visibility:public"],
enable_validation: false,
+ stubs_type: "everything",
}
////////////////////////////////////////////////////////////////////////
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index caddf5a..af40c3d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2031,41 +2031,6 @@
method public android.media.PlaybackParams setAudioStretchMode(int);
}
- @FlaggedApi("android.os.vibrator.haptics_customization_enabled") public final class RingtoneSelection {
- method @NonNull public static android.media.RingtoneSelection fromUri(@Nullable android.net.Uri, int);
- method public int getSoundSource();
- method @Nullable public android.net.Uri getSoundUri();
- method public int getVibrationSource();
- method @Nullable public android.net.Uri getVibrationUri();
- method public static boolean isRingtoneSelectionUri(@Nullable android.net.Uri);
- method @NonNull public android.net.Uri toUri();
- field public static final String DEFAULT_SELECTION_URI_STRING = "content://media/ringtone";
- field public static final int FROM_URI_RINGTONE_SELECTION_ONLY = 3; // 0x3
- field public static final int FROM_URI_RINGTONE_SELECTION_OR_SOUND = 1; // 0x1
- field public static final int FROM_URI_RINGTONE_SELECTION_OR_VIBRATION = 2; // 0x2
- field public static final int SOUND_SOURCE_OFF = 1; // 0x1
- field public static final int SOUND_SOURCE_SYSTEM_DEFAULT = 3; // 0x3
- field public static final int SOUND_SOURCE_UNSPECIFIED = 0; // 0x0
- field public static final int SOUND_SOURCE_URI = 2; // 0x2
- field public static final int VIBRATION_SOURCE_APPLICATION_DEFAULT = 4; // 0x4
- field public static final int VIBRATION_SOURCE_AUDIO_CHANNEL = 10; // 0xa
- field public static final int VIBRATION_SOURCE_HAPTIC_GENERATOR = 11; // 0xb
- field public static final int VIBRATION_SOURCE_OFF = 1; // 0x1
- field public static final int VIBRATION_SOURCE_SYSTEM_DEFAULT = 3; // 0x3
- field public static final int VIBRATION_SOURCE_UNSPECIFIED = 0; // 0x0
- field public static final int VIBRATION_SOURCE_URI = 2; // 0x2
- }
-
- @FlaggedApi("android.os.vibrator.haptics_customization_enabled") public static final class RingtoneSelection.Builder {
- ctor public RingtoneSelection.Builder();
- ctor public RingtoneSelection.Builder(@NonNull android.media.RingtoneSelection);
- method @NonNull public android.media.RingtoneSelection build();
- method @NonNull public android.media.RingtoneSelection.Builder setSoundSource(int);
- method @NonNull public android.media.RingtoneSelection.Builder setSoundSource(@NonNull android.net.Uri);
- method @NonNull public android.media.RingtoneSelection.Builder setVibrationSource(int);
- method @NonNull public android.media.RingtoneSelection.Builder setVibrationSource(@NonNull android.net.Uri);
- }
-
public static final class VolumeShaper.Configuration.Builder {
method @NonNull public android.media.VolumeShaper.Configuration.Builder setOptionFlags(int);
}
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 658ddbf..685ea63 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -2055,14 +2055,8 @@
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_DEFAULT
UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_OFF:
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_OFF
-UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_SYSTEM_DEFAULT:
- New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_SYSTEM_DEFAULT
-UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_UNSPECIFIED:
- New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_UNSPECIFIED
UnflaggedApi: android.media.RingtoneSelection#SOUND_SOURCE_URI:
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.SOUND_SOURCE_URI
-UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_DEFAULT:
- New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_DEFAULT
UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_APPLICATION_PROVIDED:
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_APPLICATION_PROVIDED
UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_AUDIO_CHANNEL:
@@ -2073,10 +2067,6 @@
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_HAPTIC_GENERATOR
UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_OFF:
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_OFF
-UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_SYSTEM_DEFAULT:
- New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_SYSTEM_DEFAULT
-UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_UNSPECIFIED:
- New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_UNSPECIFIED
UnflaggedApi: android.media.RingtoneSelection#VIBRATION_SOURCE_URI:
New API must be flagged with @FlaggedApi: field android.media.RingtoneSelection.VIBRATION_SOURCE_URI
UnflaggedApi: android.media.RingtoneSelection#fromUri(android.net.Uri, int):
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 57c67be..63cafdc 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -9601,9 +9601,9 @@
* Specifies whether the activities below this one in the task can also start other activities
* or finish the task.
* <p>
- * Starting from Target SDK Level {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, apps
- * are blocked from starting new activities or finishing their task unless the top activity of
- * such task belong to the same UID for security reasons.
+ * Starting from Target SDK Level {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, apps
+ * may be blocked from starting new activities or finishing their task unless the top activity
+ * of such task belong to the same UID for security reasons.
* <p>
* Setting this flag to {@code true} will allow the launching app to ignore the restriction if
* this activity is on top. Apps matching the UID of this activity are always exempt.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 083705b..b25ebf6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -14071,7 +14071,7 @@
public void setAuditLogEnabled(boolean enabled) {
throwIfParentInstance("setAuditLogEnabled");
try {
- mService.setAuditLogEnabled(mContext.getPackageName(), true);
+ mService.setAuditLogEnabled(mContext.getPackageName(), enabled);
} catch (RemoteException re) {
re.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index 48ea846..6317725 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -40,6 +40,7 @@
public class ActivityConfigurationChangeItem extends ActivityTransactionItem {
private Configuration mConfiguration;
+ private ActivityWindowInfo mActivityWindowInfo;
@Override
public void preExecute(@NonNull ClientTransactionHandler client) {
@@ -55,8 +56,7 @@
// TODO(lifecycler): detect if PIP or multi-window mode changed and report it here.
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityConfigChanged");
client.handleActivityConfigurationChanged(r, mConfiguration, INVALID_DISPLAY,
- // TODO(b/287582673): add ActivityWindowInfo
- new ActivityWindowInfo());
+ mActivityWindowInfo);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -73,7 +73,7 @@
/** Obtain an instance initialized with provided params. */
@NonNull
public static ActivityConfigurationChangeItem obtain(@NonNull IBinder activityToken,
- @NonNull Configuration config) {
+ @NonNull Configuration config, @NonNull ActivityWindowInfo activityWindowInfo) {
ActivityConfigurationChangeItem instance =
ObjectPool.obtain(ActivityConfigurationChangeItem.class);
if (instance == null) {
@@ -81,6 +81,7 @@
}
instance.setActivityToken(activityToken);
instance.mConfiguration = new Configuration(config);
+ instance.mActivityWindowInfo = new ActivityWindowInfo(activityWindowInfo);
return instance;
}
@@ -89,6 +90,7 @@
public void recycle() {
super.recycle();
mConfiguration = null;
+ mActivityWindowInfo = null;
ObjectPool.recycle(this);
}
@@ -100,12 +102,14 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeTypedObject(mConfiguration, flags);
+ dest.writeTypedObject(mActivityWindowInfo, flags);
}
/** Read from Parcel. */
private ActivityConfigurationChangeItem(@NonNull Parcel in) {
super(in);
mConfiguration = in.readTypedObject(Configuration.CREATOR);
+ mActivityWindowInfo = in.readTypedObject(ActivityWindowInfo.CREATOR);
}
public static final @NonNull Creator<ActivityConfigurationChangeItem> CREATOR =
@@ -128,7 +132,8 @@
return false;
}
final ActivityConfigurationChangeItem other = (ActivityConfigurationChangeItem) o;
- return Objects.equals(mConfiguration, other.mConfiguration);
+ return Objects.equals(mConfiguration, other.mConfiguration)
+ && Objects.equals(mActivityWindowInfo, other.mActivityWindowInfo);
}
@Override
@@ -136,12 +141,14 @@
int result = 17;
result = 31 * result + super.hashCode();
result = 31 * result + Objects.hashCode(mConfiguration);
+ result = 31 * result + Objects.hashCode(mActivityWindowInfo);
return result;
}
@Override
public String toString() {
return "ActivityConfigurationChange{" + super.toString()
- + ",config=" + mConfiguration + "}";
+ + ",config=" + mConfiguration
+ + ",activityWindowInfo=" + mActivityWindowInfo + "}";
}
}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index ecffe9e..faa2c70 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -392,8 +392,6 @@
return;
}
- Log.i(TAG, walFile.getAbsolutePath() + " " + size + " bytes: Bigger than "
- + threshold + "; truncating");
try {
executeForString("PRAGMA wal_checkpoint(TRUNCATE)", null, null);
mConfiguration.shouldTruncateWalFile = false;
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index 5e523c0..78c8954 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -377,8 +377,7 @@
if (writable) {
throw ex;
}
- Log.e(TAG, "Couldn't open " + mName
- + " for writing (will try read-only):", ex);
+ Log.e(TAG, "Couldn't open database for writing (will try read-only):", ex);
params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
db = SQLiteDatabase.openDatabase(filePath, params);
}
@@ -425,11 +424,6 @@
}
onOpen(db);
-
- if (db.isReadOnly()) {
- Log.w(TAG, "Opened " + mName + " in read-only mode");
- }
-
mDatabase = db;
return db;
} finally {
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 04e3dab..7119730 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -772,6 +772,16 @@
"CameraDeviceSetup is not supported for Camera ID: " + cameraId);
}
+ return getCameraDeviceSetupUnsafe(cameraId);
+
+ }
+
+ /**
+ * Creates and returns a {@link CameraDeviceSetup} instance without any error checking. To
+ * be used (carefully) by callers who are sure that CameraDeviceSetup instance can be legally
+ * created and don't want to pay the latency cost of calling {@link #getCameraDeviceSetup}.
+ */
+ private CameraDevice.CameraDeviceSetup getCameraDeviceSetupUnsafe(@NonNull String cameraId) {
return new CameraDeviceSetupImpl(cameraId, /*cameraManager=*/ this, mContext);
}
@@ -857,8 +867,9 @@
ICameraDeviceUser cameraUser = null;
CameraDevice.CameraDeviceSetup cameraDeviceSetup = null;
- if (Flags.cameraDeviceSetup() && isCameraDeviceSetupSupported(cameraId)) {
- cameraDeviceSetup = getCameraDeviceSetup(cameraId);
+ if (Flags.cameraDeviceSetup()
+ && CameraDeviceSetupImpl.isCameraDeviceSetupSupported(characteristics)) {
+ cameraDeviceSetup = getCameraDeviceSetupUnsafe(cameraId);
}
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl =
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 95526a8..8aacd5e 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -1379,7 +1379,6 @@
mSurfaceType != other.mSurfaceType ||
mIsDeferredConfig != other.mIsDeferredConfig ||
mIsShared != other.mIsShared ||
- mConfiguredFormat != other.mConfiguredFormat ||
mConfiguredDataspace != other.mConfiguredDataspace ||
mConfiguredGenerationId != other.mConfiguredGenerationId ||
!Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId) ||
diff --git a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
index 63ae28f..2a11835 100644
--- a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
+++ b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig
@@ -20,3 +20,10 @@
description: "Enable reporting USB data compliance warnings from HAL when set"
bug: "296119135"
}
+
+flag {
+ name: "enable_usb_data_signal_staking"
+ namespace: "preload_safety"
+ description: "Enables signal API with staking"
+ bug: "296119135"
+}
\ No newline at end of file
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 3950c25..2fe115f 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -623,14 +623,14 @@
// Message is to be inserted at tail or middle of queue. Usually we don't have to
// wake up the event queue unless there is a barrier at the head of the queue and
// the message is the earliest asynchronous message in the queue.
- //
+ needWake = mBlocked && p.target == null && msg.isAsynchronous();
+
// For readability, we split this portion of the function into two blocks based on
// whether tail tracking is enabled. This has a minor implication for the case
// where tail tracking is disabled. See the comment below.
if (Flags.messageQueueTailTracking()) {
- needWake = mBlocked && p.target == null && msg.isAsynchronous()
- && mAsyncMessageCount == 0;
if (when >= mLast.when) {
+ needWake = needWake && mAsyncMessageCount == 0;
msg.next = null;
mLast.next = msg;
mLast = msg;
@@ -643,6 +643,9 @@
if (p == null || when < p.when) {
break;
}
+ if (needWake && p.isAsynchronous()) {
+ needWake = false;
+ }
}
if (p == null) {
/* Inserting at tail of queue */
@@ -652,7 +655,6 @@
prev.next = msg;
}
} else {
- needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index abfa4e3..d9400ac 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -1,4 +1,5 @@
package: "android.os"
+container: "system"
flag {
name: "android_os_build_vanilla_ice_cream"
@@ -40,6 +41,7 @@
namespace: "profile_experiences"
description: "Guards a new Private Profile type in UserManager - everything from its setup to config to deletion."
bug: "299069460"
+ is_exported: true
}
flag {
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 8495f37..fa9f03d 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -247,8 +247,6 @@
private final LegacyPermissionManager mLegacyPermissionManager;
- private final VirtualDeviceManager mVirtualDeviceManager;
-
private final ArrayMap<PackageManager.OnPermissionsChangedListener,
IOnPermissionsChangeListener> mPermissionListeners = new ArrayMap<>();
private PermissionUsageHelper mUsageHelper;
@@ -269,7 +267,6 @@
mPermissionManager = IPermissionManager.Stub.asInterface(ServiceManager.getServiceOrThrow(
"permissionmgr"));
mLegacyPermissionManager = context.getSystemService(LegacyPermissionManager.class);
- mVirtualDeviceManager = context.getSystemService(VirtualDeviceManager.class);
}
/**
@@ -1918,14 +1915,18 @@
if (deviceId == Context.DEVICE_ID_DEFAULT) {
persistentDeviceId = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
} else if (android.companion.virtual.flags.Flags.vdmPublicApis()) {
- VirtualDevice virtualDevice = mVirtualDeviceManager.getVirtualDevice(deviceId);
- if (virtualDevice == null) {
- Slog.e(LOG_TAG, "Virtual device is not found with device Id " + deviceId);
- return null;
- }
- persistentDeviceId = virtualDevice.getPersistentDeviceId();
- if (persistentDeviceId == null) {
- Slog.e(LOG_TAG, "Cannot find persistent device Id for " + deviceId);
+ VirtualDeviceManager virtualDeviceManager = mContext.getSystemService(
+ VirtualDeviceManager.class);
+ if (virtualDeviceManager != null) {
+ VirtualDevice virtualDevice = virtualDeviceManager.getVirtualDevice(deviceId);
+ if (virtualDevice == null) {
+ Slog.e(LOG_TAG, "Virtual device is not found with device Id " + deviceId);
+ return null;
+ }
+ persistentDeviceId = virtualDevice.getPersistentDeviceId();
+ if (persistentDeviceId == null) {
+ Slog.e(LOG_TAG, "Cannot find persistent device Id for " + deviceId);
+ }
}
} else {
Slog.e(LOG_TAG, "vdmPublicApis flag is not enabled when device Id " + deviceId
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index c13dd36..c03dc71 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -406,6 +406,7 @@
* Builder for the add-call parameters.
*/
public static final class AddCallParametersBuilder {
+ public static final int MAX_NUMBER_OF_CHARACTERS = 256;
private CallerInfo mCallerInfo;
private String mNumber;
private String mPostDialDigits;
@@ -431,7 +432,7 @@
private Uri mPictureUri;
private int mIsPhoneAccountMigrationPending;
private boolean mIsBusinessCall;
- private String mBusinessName;
+ private String mAssertedDisplayName;
/**
* @param callerInfo the CallerInfo object to get the target contact from.
@@ -659,11 +660,18 @@
}
/**
- * @param businessName should be set if the caller is a business call
+ * @param assertedDisplayName the asserted display name associated with the business
+ * call
+ * @throws IllegalArgumentException if the assertedDisplayName is over 256 characters
*/
@FlaggedApi(Flags.FLAG_BUSINESS_CALL_COMPOSER)
- public @NonNull AddCallParametersBuilder setBusinessName(String businessName) {
- mBusinessName = businessName;
+ public @NonNull AddCallParametersBuilder setAssertedDisplayName(
+ String assertedDisplayName) {
+ if (assertedDisplayName.length() > MAX_NUMBER_OF_CHARACTERS) {
+ throw new IllegalArgumentException("assertedDisplayName exceeds the character"
+ + " limit of " + MAX_NUMBER_OF_CHARACTERS + ".");
+ }
+ mAssertedDisplayName = assertedDisplayName;
return this;
}
@@ -678,7 +686,7 @@
mCallBlockReason,
mCallScreeningAppName, mCallScreeningComponentName, mMissedReason,
mPriority, mSubject, mLatitude, mLongitude, mPictureUri,
- mIsPhoneAccountMigrationPending, mIsBusinessCall, mBusinessName);
+ mIsPhoneAccountMigrationPending, mIsBusinessCall, mAssertedDisplayName);
} else {
return new AddCallParams(mCallerInfo, mNumber, mPostDialDigits, mViaNumber,
mPresentation, mCallType, mFeatures, mAccountHandle, mStart, mDuration,
@@ -716,7 +724,7 @@
private Uri mPictureUri;
private int mIsPhoneAccountMigrationPending;
private boolean mIsBusinessCall;
- private String mBusinessName;
+ private String mAssertedDisplayName;
private AddCallParams(CallerInfo callerInfo, String number, String postDialDigits,
String viaNumber, int presentation, int callType, int features,
@@ -761,7 +769,8 @@
CharSequence callScreeningAppName, String callScreeningComponentName,
long missedReason,
int priority, String subject, double latitude, double longitude, Uri pictureUri,
- int isPhoneAccountMigrationPending, boolean isBusinessCall, String businessName) {
+ int isPhoneAccountMigrationPending, boolean isBusinessCall,
+ String assertedDisplayName) {
mCallerInfo = callerInfo;
mNumber = number;
mPostDialDigits = postDialDigits;
@@ -787,7 +796,7 @@
mPictureUri = pictureUri;
mIsPhoneAccountMigrationPending = isPhoneAccountMigrationPending;
mIsBusinessCall = isBusinessCall;
- mBusinessName = businessName;
+ mAssertedDisplayName = assertedDisplayName;
}
}
@@ -1833,7 +1842,7 @@
values.put(IS_PHONE_ACCOUNT_MIGRATION_PENDING, params.mIsPhoneAccountMigrationPending);
if (Flags.businessCallComposer()) {
values.put(IS_BUSINESS_CALL, Integer.valueOf(params.mIsBusinessCall ? 1 : 0));
- values.put(ASSERTED_DISPLAY_NAME, params.mBusinessName);
+ values.put(ASSERTED_DISPLAY_NAME, params.mAssertedDisplayName);
}
if ((params.mCallerInfo != null) && (params.mCallerInfo.getContactId() > 0)) {
// Update usage information for the number associated with the contact ID.
diff --git a/core/java/android/security/ConfirmationPrompt.java b/core/java/android/security/ConfirmationPrompt.java
index d8c44ad..f626149 100644
--- a/core/java/android/security/ConfirmationPrompt.java
+++ b/core/java/android/security/ConfirmationPrompt.java
@@ -92,7 +92,6 @@
private Executor mExecutor;
private Context mContext;
- private final KeyStore mKeyStore = KeyStore.getInstance();
private AndroidProtectedConfirmation mProtectedConfirmation;
private AndroidProtectedConfirmation getService() {
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index f1054ec..c171c1b 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -26,7 +26,6 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
-import android.security.KeyStore;
import android.security.KeyStore2;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore2.AndroidKeyStoreProvider;
@@ -272,11 +271,9 @@
public static final int ERROR_KEY_NOT_FOUND = 30;
private final ILockSettings mBinder;
- private final KeyStore mKeyStore;
- private RecoveryController(ILockSettings binder, KeyStore keystore) {
+ private RecoveryController(ILockSettings binder) {
mBinder = binder;
- mKeyStore = keystore;
}
/**
@@ -296,7 +293,7 @@
// lockSettings may be null.
ILockSettings lockSettings =
ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
- return new RecoveryController(lockSettings, KeyStore.getInstance());
+ return new RecoveryController(lockSettings);
}
/**
diff --git a/core/java/android/tracing/flags.aconfig b/core/java/android/tracing/flags.aconfig
index b1bca96..cedba85 100644
--- a/core/java/android/tracing/flags.aconfig
+++ b/core/java/android/tracing/flags.aconfig
@@ -8,7 +8,7 @@
}
flag {
- name: "perfetto_protolog"
+ name: "perfetto_protolog_tracing"
namespace: "windowing_tools"
description: "Migrate protolog to Perfetto"
bug: "276432490"
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index 00657ea..66655fc 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -188,8 +188,7 @@
// check whether the stylus we are tracking goes up.
if (mState != null) {
mState.mShouldInitHandwriting = false;
- if (!mState.mHasInitiatedHandwriting
- && !mState.mHasPreparedHandwritingDelegation) {
+ if (!mState.mHandled) {
// The user just did a click, long click or another stylus gesture,
// show hover icon again for the connected view.
mShowHoverIconForConnectedView = true;
@@ -204,16 +203,14 @@
// Either we've already tried to initiate handwriting, or the ongoing MotionEvent
// sequence is considered to be tap, long-click or other gestures.
if (!mState.mShouldInitHandwriting || mState.mExceedHandwritingSlop) {
- return mState.mHasInitiatedHandwriting
- || mState.mHasPreparedHandwritingDelegation;
+ return mState.mHandled;
}
final long timeElapsed =
motionEvent.getEventTime() - mState.mStylusDownTimeInMillis;
if (timeElapsed > mHandwritingTimeoutInMillis) {
mState.mShouldInitHandwriting = false;
- return mState.mHasInitiatedHandwriting
- || mState.mHasPreparedHandwritingDelegation;
+ return mState.mHandled;
}
final int pointerIndex = motionEvent.findPointerIndex(mState.mStylusPointerId);
@@ -223,7 +220,7 @@
mState.mExceedHandwritingSlop = true;
View candidateView = findBestCandidateView(mState.mStylusDownX,
mState.mStylusDownY, /* isHover */ false);
- if (candidateView != null) {
+ if (candidateView != null && candidateView.isEnabled()) {
if (candidateView == getConnectedOrFocusedView()) {
if (!mInitiateWithoutConnection && !candidateView.hasFocus()) {
requestFocusWithoutReveal(candidateView);
@@ -246,7 +243,7 @@
}
}
}
- return mState.mHasInitiatedHandwriting || mState.mHasPreparedHandwritingDelegation;
+ return mState.mHandled;
}
return false;
}
@@ -382,7 +379,7 @@
@VisibleForTesting
public void startHandwriting(@NonNull View view) {
mImm.startStylusHandwriting(view);
- mState.mHasInitiatedHandwriting = true;
+ mState.mHandled = true;
mState.mShouldInitHandwriting = false;
mShowHoverIconForConnectedView = false;
if (view instanceof TextView) {
@@ -402,13 +399,12 @@
mImm.startConnectionlessStylusHandwritingForDelegation(
view, getCursorAnchorInfoForConnectionless(view), delegatePackageName,
view::post, new DelegationCallback(view, delegatePackageName));
- mState.mHasInitiatedHandwriting = true;
mState.mShouldInitHandwriting = false;
} else {
mImm.prepareStylusHandwritingDelegation(view, delegatePackageName);
view.getHandwritingDelegatorCallback().run();
- mState.mHasPreparedHandwritingDelegation = true;
}
+ mState.mHandled = true;
}
/**
@@ -455,7 +451,7 @@
private void onDelegationAccepted(View view) {
if (mState != null) {
- mState.mHasInitiatedHandwriting = true;
+ mState.mHandled = true;
mState.mShouldInitHandwriting = false;
}
if (view instanceof TextView) {
@@ -795,12 +791,12 @@
* This boolean will be set to false, and it won't request to start handwriting.
*/
private boolean mShouldInitHandwriting;
- /**
- * Whether handwriting mode has already been initiated for the current MotionEvent sequence.
- */
- private boolean mHasInitiatedHandwriting;
- private boolean mHasPreparedHandwritingDelegation;
+ /**
+ * Whether the current MotionEvent sequence has been handled by the handwriting initiator,
+ * either by initiating handwriting mode, or by preparing handwriting delegation.
+ */
+ private boolean mHandled;
/**
* Whether the current ongoing stylus MotionEvent sequence already exceeds the
@@ -838,8 +834,7 @@
mStylusDownY = motionEvent.getY(actionIndex);
mShouldInitHandwriting = true;
- mHasInitiatedHandwriting = false;
- mHasPreparedHandwritingDelegation = false;
+ mHandled = false;
mExceedHandwritingSlop = false;
}
}
@@ -1052,8 +1047,6 @@
// Fall back to the old delegation flow
mImm.prepareStylusHandwritingDelegation(mView, mDelegatePackageName);
mView.getHandwritingDelegatorCallback().run();
- mState.mHasInitiatedHandwriting = false;
- mState.mHasPreparedHandwritingDelegation = true;
break;
}
}
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index f5f4fd9..9099f98 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -428,13 +428,11 @@
private BitmapDrawable getBitmapDrawableFromVectorDrawable(Resources resources,
VectorDrawable vectorDrawable) {
- Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
- vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
- // BitmapDrawables and Bitmap have a default density of DisplayMetrics.DENSITY_DEVICE,
- // (which is deprecated in favor of DENSITY_DEVICE_STABLE/resources.densityDpi). In
- // rare cases when device density differs from the resource density, the bitmap will
- // scale as the BitmapDrawable is created. Avoid by explicitly setting density here.
- bitmap.setDensity(resources.getDisplayMetrics().densityDpi);
+ // Ensure we pass the display metrics into the Bitmap constructor so that it is initialized
+ // with the correct density.
+ Bitmap bitmap = Bitmap.createBitmap(resources.getDisplayMetrics(),
+ vectorDrawable.getIntrinsicWidth(),
+ vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888, true /* hasAlpha */);
Canvas canvas = new Canvas(bitmap);
vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
vectorDrawable.draw(canvas);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 042af1f..a2faabc 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16815,10 +16815,15 @@
mAttachInfo.mViewRootImpl.getWindowVisibleDisplayFrame(outRect);
return;
}
- // The view is not attached to a display so we don't have a context.
- // Make a best guess about the display size.
- Display d = DisplayManagerGlobal.getInstance().getRealDisplay(Display.DEFAULT_DISPLAY);
- d.getRectSize(outRect);
+ // TODO (b/327559224): Refine the behavior to better reflect the window environment with API
+ // doc updates.
+ final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
+ final WindowMetrics metrics = windowManager.getMaximumWindowMetrics();
+ final Insets insets = metrics.getWindowInsets().getInsets(
+ WindowInsets.Type.navigationBars() | WindowInsets.Type.displayCutout());
+ outRect.set(metrics.getBounds());
+ outRect.inset(insets);
+ outRect.offsetTo(0, 0);
}
/**
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index fcc8344..68940d6 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1153,6 +1153,7 @@
}
final boolean startInput;
synchronized (mH) {
+ mImeDispatcher.clear();
if (getBindSequenceLocked() != sequence) {
return;
}
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index 51890ec..94c72c6 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -46,5 +46,5 @@
name: "bal_respect_app_switch_state_when_check_bound_by_foreground_uid"
namespace: "responsible_apis"
description: "Prevent BAL based on it is bound by foreground Uid but the app switch is stopped."
- bug: "171459802"
+ bug: "283801068"
}
diff --git a/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java b/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java
index d9ac5a9..30de546 100644
--- a/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/LegacyProtoLogImpl.java
@@ -61,7 +61,7 @@
private static final int PER_CHUNK_SIZE = 1024;
private static final String TAG = "ProtoLog";
private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L;
- static final String PROTOLOG_VERSION = "1.0.0";
+ static final String PROTOLOG_VERSION = "2.0.0";
private static final int DEFAULT_PER_CHUNK_SIZE = 0;
private final File mLogFile;
diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java
index 78bed94..8965385 100644
--- a/core/java/com/android/internal/protolog/ProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java
@@ -98,7 +98,7 @@
*/
public static synchronized IProtoLog getSingleInstance() {
if (sServiceInstance == null) {
- if (android.tracing.Flags.perfettoProtolog()) {
+ if (android.tracing.Flags.perfettoProtologTracing()) {
sServiceInstance =
new PerfettoProtoLogImpl(sViewerConfigPath);
} else {
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index c87b7cd..5e3e1b0 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -84,7 +84,7 @@
CarrierConfigManager#KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_STRING_ARRAY.
If 0, the device always switch to the higher score SIM.
If < 0, the network type and signal strength based auto switch is disabled. -->
- <integer name="auto_data_switch_score_tolerance">4000</integer>
+ <integer name="auto_data_switch_score_tolerance">-1</integer>
<java-symbol type="integer" name="auto_data_switch_score_tolerance" />
<!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 64c17bd..d115bf3 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -247,7 +247,7 @@
newConfig.smallestScreenWidthDp++;
transaction = newTransaction(activityThread);
transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
- activity.getActivityToken(), newConfig));
+ activity.getActivityToken(), newConfig, new ActivityWindowInfo()));
appThread.scheduleTransaction(transaction);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -455,11 +455,11 @@
transaction = newTransaction(activityThread);
transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
- activity.getActivityToken(), activityConfigLandscape));
+ activity.getActivityToken(), activityConfigLandscape, new ActivityWindowInfo()));
transaction.addTransactionItem(ConfigurationChangeItem.obtain(
processConfigPortrait, DEVICE_ID_INVALID));
transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
- activity.getActivityToken(), activityConfigPortrait));
+ activity.getActivityToken(), activityConfigPortrait, new ActivityWindowInfo()));
appThread.scheduleTransaction(transaction);
activity.mTestLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS);
@@ -883,7 +883,7 @@
private static ClientTransaction newActivityConfigTransaction(@NonNull Activity activity,
@NonNull Configuration config) {
final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(
- activity.getActivityToken(), config);
+ activity.getActivityToken(), config, new ActivityWindowInfo());
final ClientTransaction transaction = newTransaction(activity);
transaction.addTransactionItem(item);
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
index 85a1b4e..4db5d1b 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
@@ -107,7 +107,7 @@
@Test
public void testActivityConfigurationChangeItem_getContextToUpdate() {
final ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem
- .obtain(mActivityToken, mConfiguration);
+ .obtain(mActivityToken, mConfiguration, new ActivityWindowInfo());
final Context context = item.getContextToUpdate(mHandler);
assertEquals(mActivity, context);
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 906558f..31ea675 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -82,7 +82,8 @@
@Test
public void testRecycleActivityConfigurationChangeItem() {
- testRecycle(() -> ActivityConfigurationChangeItem.obtain(mActivityToken, config()));
+ testRecycle(() -> ActivityConfigurationChangeItem.obtain(mActivityToken, config(),
+ new ActivityWindowInfo()));
}
@Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index dbb090f..75347bf 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -95,8 +95,11 @@
@Test
public void testActivityConfigChange() {
// Write to parcel
+ final ActivityWindowInfo activityWindowInfo = new ActivityWindowInfo();
+ activityWindowInfo.set(true /* isEmbedded */, new Rect(0, 0, 500, 1000),
+ new Rect(0, 0, 500, 500));
ActivityConfigurationChangeItem item = ActivityConfigurationChangeItem.obtain(
- mActivityToken, config());
+ mActivityToken, config(), activityWindowInfo);
writeAndPrepareForReading(item);
// Read from parcel and assert
@@ -300,7 +303,7 @@
// Write to parcel
NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
- mActivityToken, config());
+ mActivityToken, config(), new ActivityWindowInfo());
StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken,
78 /* configChanges */);
@@ -327,7 +330,7 @@
// Write to parcel
NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
- mActivityToken, config());
+ mActivityToken, config(), new ActivityWindowInfo());
ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
transaction.addTransactionItem(callback1);
diff --git a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
index 51eb41c..b60b806f 100644
--- a/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
+++ b/core/tests/coretests/src/android/view/stylus/HandwritingInitiatorTest.java
@@ -472,6 +472,26 @@
}
@Test
+ public void onTouchEvent_doesNothing_viewDisabled() {
+ mTestView1.setEnabled(false);
+
+ final int x1 = (sHwArea1.left + sHwArea1.right) / 2;
+ final int y1 = (sHwArea1.top + sHwArea1.bottom) / 2;
+ MotionEvent stylusEvent1 = createStylusEvent(ACTION_DOWN, x1, y1, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent1);
+
+ final int x2 = x1 + mHandwritingSlop * 2;
+ final int y2 = y1;
+
+ MotionEvent stylusEvent2 = createStylusEvent(ACTION_MOVE, x2, y2, 0);
+ mHandwritingInitiator.onTouchEvent(stylusEvent2);
+
+ // HandwritingInitiator will not request focus if it is disabled.
+ verify(mTestView1, never()).requestFocus();
+ verify(mHandwritingInitiator, never()).startHandwriting(mTestView1);
+ }
+
+ @Test
public void onTouchEvent_focusView_inputConnectionAlreadyBuilt_stylusMoveOnce_withinHWArea() {
if (!mInitiateWithoutConnection) {
mHandwritingInitiator.onInputConnectionCreated(mTestView1);
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index e8c7a53..0231d3a 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1,5 +1,5 @@
{
- "version": "1.0.0",
+ "version": "2.0.0",
"messages": {
"7286191062634870297": {
"message": "Binding proc %s with config %s",
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 62fe54f..ef03d3a 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -19,9 +19,9 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.security.KeyStore;
import java.io.IOException;
+import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
@@ -47,13 +47,13 @@
}
/**
- * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
+ * Gets the Android KeyStore operation handle corresponding to the provided JCA crypto
* primitive.
*
* <p>The following primitives are supported: {@link Cipher} and {@link Mac}.
*
- * @return KeyStore operation handle or {@code 0} if the provided primitive's KeyStore operation
- * is not in progress.
+ * @return Android KeyStore operation handle or {@code 0} if the provided primitive's Android
+ * KeyStore operation is not in progress.
*
* @throws IllegalArgumentException if the provided primitive is not supported or is not backed
* by AndroidKeyStore provider.
@@ -67,10 +67,10 @@
}
/**
- * Returns an {@code AndroidKeyStore} {@link java.security.KeyStore}} of the specified UID.
- * The {@code KeyStore} contains keys and certificates owned by that UID. Such cross-UID
- * access is permitted to a few system UIDs and only to a few other UIDs (e.g., Wi-Fi, VPN)
- * all of which are system.
+ * Returns an {@code AndroidKeyStore} {@link KeyStore} of the specified UID. The {@code
+ * KeyStore} contains keys and certificates owned by that UID. Such cross-UID access is
+ * permitted to a few system UIDs and only to a few other UIDs (e.g., Wi-Fi, VPN) all of which
+ * are system.
*
* <p>Note: the returned {@code KeyStore} is already initialized/loaded. Thus, there is
* no need to invoke {@code load} on it.
@@ -84,12 +84,12 @@
*/
@SystemApi
@NonNull
- public static java.security.KeyStore getKeyStoreForUid(int uid)
+ public static KeyStore getKeyStoreForUid(int uid)
throws KeyStoreException, NoSuchProviderException {
- final java.security.KeyStore.LoadStoreParameter loadParameter =
+ final KeyStore.LoadStoreParameter loadParameter =
new android.security.keystore2.AndroidKeyStoreLoadStoreParameter(
KeyProperties.legacyUidToNamespace(uid));
- java.security.KeyStore result = java.security.KeyStore.getInstance(PROVIDER_NAME);
+ KeyStore result = KeyStore.getInstance(PROVIDER_NAME);
try {
result.load(loadParameter);
} catch (NoSuchAlgorithmException | CertificateException | IOException e) {
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 244fe30..7aecfd8 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -910,7 +910,7 @@
/**
* Returns whether this key is critical to the device encryption flow.
*
- * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
+ * @see Builder#setCriticalToDeviceEncryption(boolean)
* @hide
*/
public boolean isCriticalToDeviceEncryption() {
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 2495d1a..31b4a5e 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -569,7 +569,7 @@
/**
* Return whether this key is critical to the device encryption flow.
*
- * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
+ * @see Builder#setCriticalToDeviceEncryption(boolean)
* @hide
*/
public boolean isCriticalToDeviceEncryption() {
@@ -1105,9 +1105,10 @@
* Set whether this key is critical to the device encryption flow
*
* This is a special flag only available to system servers to indicate the current key
- * is part of the device encryption flow.
+ * is part of the device encryption flow. Setting this flag causes the key to not
+ * be cryptographically bound to the LSKF even if the key is otherwise authentication
+ * bound.
*
- * @see android.security.KeyStore#FLAG_CRITICAL_TO_DEVICE_ENCRYPTION
* @hide
*/
public Builder setCriticalToDeviceEncryption(boolean critical) {
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperation.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperation.java
index 2c709ae..c42c9e4 100644
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperation.java
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperation.java
@@ -16,18 +16,16 @@
package android.security.keystore;
-import android.security.KeyStore;
-
/**
- * Cryptographic operation backed by {@link KeyStore}.
+ * Cryptographic operation backed by Android KeyStore.
*
* @hide
*/
public interface KeyStoreCryptoOperation {
/**
- * Gets the KeyStore operation handle of this crypto operation.
+ * Gets the Android KeyStore operation handle of this crypto operation.
*
- * @return handle or {@code 0} if the KeyStore operation is not in progress.
+ * @return handle or {@code 0} if the Android KeyStore operation is not in progress.
*/
long getOperationHandle();
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java
index a8dd7f3..8eca67f 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyFactorySpi.java
@@ -16,7 +16,6 @@
package android.security.keystore2;
-import android.security.KeyStore;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyInfo;
@@ -39,8 +38,6 @@
*/
public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi {
- private final KeyStore mKeyStore = KeyStore.getInstance();
-
@Override
protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpecClass)
throws InvalidKeySpecException {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index d204f13..99100de 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -17,7 +17,6 @@
package android.security.keystore2;
import android.annotation.NonNull;
-import android.security.KeyStore;
import android.security.KeyStore2;
import android.security.KeyStoreSecurityLevel;
import android.security.keymaster.KeymasterDefs;
@@ -161,13 +160,13 @@
}
/**
- * Gets the {@link KeyStore} operation handle corresponding to the provided JCA crypto
+ * Gets the Android KeyStore operation handle corresponding to the provided JCA crypto
* primitive.
*
* <p>The following primitives are supported: {@link Cipher}, {@link Signature} and {@link Mac}.
*
- * @return KeyStore operation handle or {@code 0} if the provided primitive's KeyStore operation
- * is not in progress.
+ * @return Android KeyStore operation handle or {@code 0} if the provided primitive's Android
+ * KeyStore operation is not in progress.
*
* @throws IllegalArgumentException if the provided primitive is not supported or is not backed
* by AndroidKeyStore provider.
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
index 2682eb6..2223091 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.security.GateKeeper;
-import android.security.KeyStore;
import android.security.keymaster.KeymasterArguments;
import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyGenParameterSpec;
@@ -46,8 +45,6 @@
*/
public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
- private final KeyStore mKeyStore = KeyStore.getInstance();
-
@Override
protected KeySpec engineGetKeySpec(SecretKey key,
@SuppressWarnings("rawtypes") Class keySpecClass) throws InvalidKeySpecException {
diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java
index 07d6a69..5bd98bc 100644
--- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationStreamer.java
@@ -16,12 +16,11 @@
package android.security.keystore2;
-import android.security.KeyStore;
import android.security.KeyStoreException;
/**
- * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
- * {@code update} and {@code finish} operations.
+ * Helper for streaming a crypto operation's input and output via KeyStore service's {@code update}
+ * and {@code finish} operations.
*
* <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
* update and finish operations. Firstly, KeyStore's update operation can consume only a limited
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index 08b7bb8..39cface 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -201,7 +201,7 @@
return null;
}
return new SplitInfo(primaryActivityStack, secondaryActivityStack,
- mCurrentSplitAttributes, mToken);
+ mCurrentSplitAttributes, SplitInfo.Token.createFromBinder(mToken));
}
static boolean shouldFinishPrimaryWithSecondary(@NonNull SplitRule splitRule) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index ae3a854..038d008 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -35,6 +35,7 @@
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
+import static androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_ACTIVITY_STACK_TOKEN;
import static androidx.window.extensions.embedding.ActivityEmbeddingOptionsProperties.KEY_OVERLAY_TAG;
import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior;
import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior;
@@ -112,10 +113,6 @@
static final boolean ENABLE_SHELL_TRANSITIONS =
SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
- // TODO(b/295993745): remove after prebuilt library is updated.
- private static final String KEY_ACTIVITY_STACK_TOKEN =
- "androidx.window.extensions.embedding.ActivityStackToken";
-
@VisibleForTesting
@GuardedBy("mLock")
final SplitPresenter mPresenter;
@@ -554,7 +551,7 @@
}
@Override
- public void updateActivityStackAttributes(@NonNull IBinder activityStackToken,
+ public void updateActivityStackAttributes(@NonNull ActivityStack.Token activityStackToken,
@NonNull ActivityStackAttributes attributes) {
if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
return;
@@ -563,7 +560,7 @@
Objects.requireNonNull(attributes);
synchronized (mLock) {
- final TaskFragmentContainer container = getContainer(activityStackToken);
+ final TaskFragmentContainer container = getContainer(activityStackToken.getRawToken());
if (container == null) {
Log.w(TAG, "Cannot find TaskFragmentContainer for token:" + activityStackToken);
return;
@@ -583,13 +580,14 @@
@Override
@Nullable
- public ParentContainerInfo getParentContainerInfo(@NonNull IBinder activityStackToken) {
+ public ParentContainerInfo getParentContainerInfo(
+ @NonNull ActivityStack.Token activityStackToken) {
if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
return null;
}
Objects.requireNonNull(activityStackToken);
synchronized (mLock) {
- final TaskFragmentContainer container = getContainer(activityStackToken);
+ final TaskFragmentContainer container = getContainer(activityStackToken.getRawToken());
if (container == null) {
return null;
}
@@ -601,7 +599,7 @@
@Override
@Nullable
- public IBinder getActivityStackToken(@NonNull String tag) {
+ public ActivityStack.Token getActivityStackToken(@NonNull String tag) {
if (!Flags.activityEmbeddingOverlayPresentationFlag()) {
return null;
}
@@ -612,7 +610,8 @@
if (taskFragmentContainer == null) {
return null;
}
- return taskFragmentContainer.getTaskFragmentToken();
+ return ActivityStack.Token.createFromBinder(taskFragmentContainer
+ .getTaskFragmentToken());
}
}
@@ -2761,8 +2760,10 @@
// TODO(b/232042367): Consolidate the activity create handling so that we can handle
// cross-process the same as normal.
- IBinder activityStackToken = options.getBinder(KEY_ACTIVITY_STACK_TOKEN);
- if (activityStackToken != null) {
+ final Bundle bundle = options.getBundle(KEY_ACTIVITY_STACK_TOKEN);
+ if (bundle != null) {
+ final IBinder activityStackToken = ActivityStack.Token.readFromBundle(bundle)
+ .getRawToken();
// Put activityStack token to #KEY_LAUNCH_TASK_FRAGMENT_TOKEN to launch the activity
// into the taskFragment associated with the token.
options.putBinder(KEY_LAUNCH_TASK_FRAGMENT_TOKEN, activityStackToken);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 6fe8e50..a6bf99d 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -367,7 +367,8 @@
if (activities == null) {
return null;
}
- return new ActivityStack(activities, isEmpty(), mToken, mOverlayTag);
+ return new ActivityStack(activities, isEmpty(),
+ ActivityStack.Token.createFromBinder(mToken), mOverlayTag);
}
/** Adds the activity that will be reparented to this container. */
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 34d43ad..28fbadb 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -399,7 +399,8 @@
new ActivityStackAttributes.Builder().build()));
assertThrows(NullPointerException.class, () ->
- mSplitController.updateActivityStackAttributes(new Binder(), null));
+ mSplitController.updateActivityStackAttributes(
+ ActivityStack.Token.createFromBinder(new Binder()), null));
verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any(), any());
}
@@ -408,7 +409,8 @@
public void testUpdateActivityStackAttributes_nullContainer_earlyReturn() {
final TaskFragmentContainer container = mSplitController.newContainer(mActivity,
mActivity.getTaskId());
- mSplitController.updateActivityStackAttributes(container.getTaskFragmentToken(),
+ mSplitController.updateActivityStackAttributes(
+ ActivityStack.Token.createFromBinder(container.getTaskFragmentToken()),
new ActivityStackAttributes.Builder().build());
verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any(), any());
@@ -418,7 +420,8 @@
public void testUpdateActivityStackAttributes_notOverlay_earlyReturn() {
final TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity);
- mSplitController.updateActivityStackAttributes(container.getTaskFragmentToken(),
+ mSplitController.updateActivityStackAttributes(
+ ActivityStack.Token.createFromBinder(container.getTaskFragmentToken()),
new ActivityStackAttributes.Builder().build());
verify(mSplitPresenter, never()).applyActivityStackAttributes(any(), any(), any(), any());
@@ -431,7 +434,8 @@
final ActivityStackAttributes attrs = new ActivityStackAttributes.Builder().build();
final IBinder token = container.getTaskFragmentToken();
- mSplitController.updateActivityStackAttributes(token, attrs);
+ mSplitController.updateActivityStackAttributes(ActivityStack.Token.createFromBinder(token),
+ attrs);
verify(mSplitPresenter).applyActivityStackAttributes(any(), eq(container), eq(attrs),
any());
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index b60943a..00f8b59 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -1437,7 +1437,7 @@
@Test
public void testUpdateSplitAttributes_nullParams_throwException() {
assertThrows(NullPointerException.class,
- () -> mSplitController.updateSplitAttributes(null, SPLIT_ATTRIBUTES));
+ () -> mSplitController.updateSplitAttributes((IBinder) null, SPLIT_ATTRIBUTES));
final SplitContainer splitContainer = mock(SplitContainer.class);
final IBinder token = new Binder();
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
index d1b1af3..490f088 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -16,6 +16,7 @@
-->
<com.android.wm.shell.windowdecor.WindowDecorLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/desktop_mode_caption"
android:layout_width="match_parent"
@@ -27,6 +28,8 @@
android:id="@+id/open_menu_button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
+ android:tint="?androidprv:attr/materialColorOnSurface"
+ android:background="?android:selectableItemBackground"
android:orientation="horizontal"
android:clickable="true"
android:focusable="true"
@@ -78,7 +81,9 @@
android:id="@+id/maximize_button_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="end"/>
+ android:layout_gravity="end"
+ android:clickable="true"
+ android:focusable="true" />
<ImageButton
android:id="@+id/close_window"
@@ -86,9 +91,10 @@
android:layout_height="40dp"
android:padding="4dp"
android:layout_marginEnd="8dp"
+ android:tint="?androidprv:attr/materialColorOnSurface"
+ android:background="?android:selectableItemBackgroundBorderless"
android:contentDescription="@string/close_button_text"
android:src="@drawable/decor_close_button_dark"
android:scaleType="fitCenter"
- android:gravity="end"
- android:background="@null"/>
+ android:gravity="end"/>
</com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
index bb6efce..e0057fe 100644
--- a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
+++ b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
@@ -14,7 +14,8 @@
~ limitations under the License.
-->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyleHorizontal"
@@ -30,7 +31,8 @@
android:layout_height="40dp"
android:padding="9dp"
android:contentDescription="@string/maximize_button_text"
+ android:tint="?androidprv:attr/materialColorOnSurface"
+ android:background="?android:selectableItemBackgroundBorderless"
android:src="@drawable/decor_desktop_mode_maximize_button_dark"
- android:scaleType="fitCenter"
- android:background="@drawable/rounded_button"/>
+ android:scaleType="fitCenter" />
</merge>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
index 93893e3..ef9bf00 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ProtoLogController.java
@@ -51,7 +51,7 @@
final ILogger logger = pw::println;
switch (args[0]) {
case "status": {
- if (android.tracing.Flags.perfettoProtolog()) {
+ if (android.tracing.Flags.perfettoProtologTracing()) {
pw.println("(Deprecated) legacy command. Use Perfetto commands instead.");
return false;
}
@@ -59,7 +59,7 @@
return true;
}
case "start": {
- if (android.tracing.Flags.perfettoProtolog()) {
+ if (android.tracing.Flags.perfettoProtologTracing()) {
pw.println("(Deprecated) legacy command. Use Perfetto commands instead.");
return false;
}
@@ -67,7 +67,7 @@
return true;
}
case "stop": {
- if (android.tracing.Flags.perfettoProtolog()) {
+ if (android.tracing.Flags.perfettoProtologTracing()) {
pw.println("(Deprecated) legacy command. Use Perfetto commands instead.");
return false;
}
@@ -101,7 +101,7 @@
return mShellProtoLog.stopLoggingToLogcat(groups, logger) == 0;
}
case "save-for-bugreport": {
- if (android.tracing.Flags.perfettoProtolog()) {
+ if (android.tracing.Flags.perfettoProtologTracing()) {
pw.println("(Deprecated) legacy command");
return false;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 15350fb..96aaf02 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -1799,11 +1799,12 @@
@Override
public void removeBubble(Bubble removedBubble) {
if (mLayerView != null) {
- mLayerView.removeBubble(removedBubble);
- if (!mBubbleData.hasBubbles() && !isStackExpanded()) {
- mLayerView.setVisibility(INVISIBLE);
- removeFromWindowManagerMaybe();
- }
+ mLayerView.removeBubble(removedBubble, () -> {
+ if (!mBubbleData.hasBubbles() && !isStackExpanded()) {
+ mLayerView.setVisibility(INVISIBLE);
+ removeFromWindowManagerMaybe();
+ }
+ });
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 78a41f7..42799d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -242,13 +242,17 @@
}
/** Removes the given {@code bubble}. */
- public void removeBubble(Bubble bubble) {
+ public void removeBubble(Bubble bubble, Runnable endAction) {
+ Runnable cleanUp = () -> {
+ bubble.cleanupViews();
+ endAction.run();
+ };
if (mBubbleData.getBubbles().isEmpty()) {
// we're removing the last bubble. collapse the expanded view and cleanup bubble views
// at the end.
- collapse(bubble::cleanupViews);
+ collapse(cleanUp);
} else {
- bubble.cleanupViews();
+ cleanUp.run();
}
}
@@ -264,6 +268,9 @@
*/
public void collapse(@Nullable Runnable endAction) {
if (!mIsExpanded) {
+ if (endAction != null) {
+ endAction.run();
+ }
return;
}
mIsExpanded = false;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
index f801b0d..a87116e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java
@@ -75,7 +75,6 @@
private SurfaceControlViewHost mViewHost;
private DividerHandleView mHandle;
private DividerRoundedCorner mCorners;
- private View mBackground;
private int mTouchElevation;
private VelocityTracker mVelocityTracker;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
index 2b10377..194eb47 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java
@@ -277,7 +277,7 @@
}
@Override
- public void onAnimationEnd(@androidx.annotation.NonNull Animator animation) {
+ public void onAnimationEnd(@NonNull Animator animation) {
mRunningAnimationCount--;
animT.remove(mScreenshot);
animT.apply();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index e421356..1c54754 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -163,14 +163,14 @@
/**
* Adds a split pair. This call does not validate the taskIds, only that they are not the same.
*/
- public void addSplitPair(int taskId1, int taskId2, SplitBounds splitBounds) {
+ public boolean addSplitPair(int taskId1, int taskId2, SplitBounds splitBounds) {
if (taskId1 == taskId2) {
- return;
+ return false;
}
if (mSplitTasks.get(taskId1, INVALID_TASK_ID) == taskId2
&& mTaskSplitBoundsMap.get(taskId1).equals(splitBounds)) {
// If the two tasks are already paired and the bounds are the same, then skip updating
- return;
+ return false;
}
// Remove any previous pairs
removeSplitPair(taskId1);
@@ -185,6 +185,7 @@
notifyRecentTasksChanged();
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Add split pair: %d, %d, %s",
taskId1, taskId2, splitBounds);
+ return true;
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
index e52235f..64e26db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
@@ -16,11 +16,14 @@
package com.android.wm.shell.splitscreen;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
+
import android.content.Context;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -50,6 +53,8 @@
void activate(WindowContainerTransaction wct, boolean includingTopTask) {
if (mIsActive) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: main stage includingTopTask=%b",
+ includingTopTask);
if (includingTopTask) {
reparentTopTask(wct);
@@ -64,6 +69,8 @@
void deactivate(WindowContainerTransaction wct, boolean toTop) {
if (!mIsActive) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: main stage toTop=%b rootTaskInfo=%s",
+ toTop, mRootTaskInfo);
mIsActive = false;
if (mRootTaskInfo == null) return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
index 9903113..f5fbae5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java
@@ -16,12 +16,15 @@
package com.android.wm.shell.splitscreen;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
+
import android.app.ActivityManager;
import android.content.Context;
import android.view.SurfaceSession;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.SyncTransactionQueue;
@@ -47,6 +50,8 @@
}
boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b",
+ mChildrenTaskInfo.size(), toTop);
if (mChildrenTaskInfo.size() == 0) return false;
wct.reparentTasks(
mRootTaskInfo.token,
@@ -59,6 +64,8 @@
boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) {
final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove side stage task: task=%d exists=%b", taskId,
+ task != null);
if (task == null) return false;
wct.reparent(task.token, newParent, false /* onTop */);
return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index b60e361..1d9fdeb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -25,6 +25,8 @@
import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
import static com.android.wm.shell.common.split.SplitScreenConstants.FADE_DURATION;
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
@@ -101,6 +103,7 @@
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
@NonNull WindowContainerToken topRoot) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playAnimation: transition=%d", info.getDebugId());
initTransition(transition, finishTransaction, finishCallback);
final TransitSession pendingTransition = getPendingTransition(transition);
@@ -123,10 +126,12 @@
playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot, topRoot);
}
- /** Internal funcation of playAnimation. */
+ /** Internal function of playAnimation. */
private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot,
@NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playInternalAnimation: transition=%d",
+ info.getDebugId());
// Play some place-holder fade animations
final boolean isEnter = isPendingEnter(transition);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -220,6 +225,8 @@
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull WindowContainerToken toTopRoot, @NonNull SplitDecorManager toTopDecor,
@NonNull WindowContainerToken topRoot) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playDragDismissAnimation: transition=%d",
+ info.getDebugId());
initTransition(transition, finishTransaction, finishCallback);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -259,6 +266,7 @@
@NonNull Transitions.TransitionFinishCallback finishCallback,
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
@NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playResizeAnimation: transition=%d", info.getDebugId());
initTransition(transition, finishTransaction, finishCallback);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -312,13 +320,15 @@
@Nullable
private TransitSession getPendingTransition(IBinder transition) {
if (isPendingEnter(transition)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved enter transition");
return mPendingEnter;
} else if (isPendingDismiss(transition)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved dismiss transition");
return mPendingDismiss;
} else if (isPendingResize(transition)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved resize transition");
return mPendingResize;
}
-
return null;
}
@@ -339,7 +349,7 @@
Transitions.TransitionHandler handler,
int extraTransitType, boolean resizeAnim) {
if (mPendingEnter != null) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ " skip to start enter split transition since it already exist. ");
return null;
}
@@ -355,8 +365,10 @@
mPendingEnter = new EnterSession(
transition, remoteTransition, extraTransitType, resizeAnim);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ " deduced Enter split screen");
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setEnterTransition: transitType=%d resize=%b",
+ extraTransitType, resizeAnim);
}
/** Starts a transition to dismiss split. */
@@ -364,7 +376,7 @@
Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
@SplitScreenController.ExitReason int reason) {
if (mPendingDismiss != null) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ " skip to start dismiss split transition since it already exist. reason to "
+ " dismiss = %s", exitReasonToString(reason));
return null;
@@ -381,9 +393,11 @@
@SplitScreenController.ExitReason int reason) {
mPendingDismiss = new DismissSession(transition, reason, dismissTop);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ " deduced Dismiss due to %s. toTop=%s",
exitReasonToString(reason), stageTypeToString(dismissTop));
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setDismissTransition: reason=%s dismissTop=%s",
+ exitReasonToString(reason), stageTypeToString(dismissTop));
}
IBinder startResizeTransition(WindowContainerTransaction wct,
@@ -405,8 +419,9 @@
@Nullable TransitionConsumedCallback consumedCallback,
@Nullable TransitionFinishedCallback finishCallback) {
mPendingResize = new TransitSession(transition, consumedCallback, finishCallback);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
+ " deduced Resize split screen");
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setResizeTransition");
}
void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
@@ -444,12 +459,15 @@
mPendingEnter.onConsumed(aborted);
mPendingEnter = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for enter transition");
} else if (isPendingDismiss(transition)) {
mPendingDismiss.onConsumed(aborted);
mPendingDismiss = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for dismiss transition");
} else if (isPendingResize(transition)) {
mPendingResize.onConsumed(aborted);
mPendingResize = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for resize transition");
}
// TODO: handle transition consumed for active remote handler
@@ -462,12 +480,15 @@
if (isPendingEnter(mAnimatingTransition)) {
mPendingEnter.onFinished(wct, mFinishTransaction);
mPendingEnter = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for enter transition");
} else if (isPendingDismiss(mAnimatingTransition)) {
mPendingDismiss.onFinished(wct, mFinishTransaction);
mPendingDismiss = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for dismiss transition");
} else if (isPendingResize(mAnimatingTransition)) {
mPendingResize.onFinished(wct, mFinishTransaction);
mPendingResize = null;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for resize transition");
}
mActiveRemoteHandler = null;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 2933cf4..7a1595f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -44,6 +44,7 @@
import static com.android.wm.shell.common.split.SplitScreenConstants.splitPositionToString;
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
@@ -138,6 +139,7 @@
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
+import com.android.wm.shell.splitscreen.SplitScreenController.SplitEnterReason;
import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.LegacyTransitions;
import com.android.wm.shell.transition.Transitions;
@@ -313,6 +315,7 @@
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Creating main/side root task");
mMainStage = new MainStage(
mContext,
mTaskOrganizer,
@@ -454,6 +457,8 @@
boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitPosition int stagePosition,
WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "moveToStage: task=%d position=%d", task.taskId,
+ stagePosition);
prepareEnterSplitScreen(wct, task, stagePosition, false /* resizeAnim */);
if (ENABLE_SHELL_TRANSITIONS) {
mSplitTransitions.startEnterTransition(TRANSIT_TO_FRONT, wct,
@@ -474,6 +479,7 @@
}
boolean removeFromSideStage(int taskId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "removeFromSideStage: task=%d", taskId);
final WindowContainerTransaction wct = new WindowContainerTransaction();
/**
@@ -498,11 +504,15 @@
enteredSplitSelect |= listener.onRequestEnterSplitSelect(taskInfo, splitPosition,
taskBounds);
}
- if (enteredSplitSelect) mTaskOrganizer.applyTransaction(wct);
+ if (enteredSplitSelect) {
+ mTaskOrganizer.applyTransaction(wct);
+ }
}
void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
Bundle options, UserHandle user) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startShortcut: pkg=%s id=%s position=%d user=%d",
+ packageName, shortcutId, position, user.getIdentifier());
final boolean isEnteringSplit = !isSplitActive();
IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
@@ -564,6 +574,7 @@
/** Use this method to launch an existing Task via a taskId */
void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startTask: task=%d position=%d", taskId, position);
mSplitRequest = new SplitRequest(taskId, position);
final WindowContainerTransaction wct = new WindowContainerTransaction();
options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */);
@@ -595,6 +606,8 @@
/** Launches an activity into split. */
void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
@Nullable Bundle options) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startIntent: intent=%s position=%d", intent.getIntent(),
+ position);
mSplitRequest = new SplitRequest(intent.getIntent(), position);
if (!ENABLE_SHELL_TRANSITIONS) {
startIntentLegacy(intent, fillInIntent, position, options);
@@ -690,6 +703,9 @@
void startTasks(int taskId1, @Nullable Bundle options1, int taskId2, @Nullable Bundle options2,
@SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startTasks: task1=%d task2=%d position=%d snapPosition=%d",
+ taskId1, taskId2, splitPosition, snapPosition);
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (taskId2 == INVALID_TASK_ID) {
if (mMainStage.containsTask(taskId1) || mSideStage.containsTask(taskId1)) {
@@ -718,6 +734,9 @@
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
@SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startIntentAndTask: intent=%s task1=%d position=%d snapPosition=%d",
+ pendingIntent.getIntent(), taskId, splitPosition, snapPosition);
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (taskId == INVALID_TASK_ID) {
options1 = options1 != null ? options1 : new Bundle();
@@ -740,6 +759,9 @@
int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
@PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition,
InstanceId instanceId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startShortcutAndTask: shortcut=%s task1=%d position=%d snapPosition=%d",
+ shortcutInfo, taskId, splitPosition, snapPosition);
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (taskId == INVALID_TASK_ID) {
options1 = options1 != null ? options1 : new Bundle();
@@ -801,6 +823,10 @@
@Nullable ShortcutInfo shortcutInfo2, @Nullable Bundle options2,
@SplitPosition int splitPosition, @PersistentSnapPosition int snapPosition,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startIntents: intent1=%s intent2=%s position=%d snapPosition=%d",
+ pendingIntent1.getIntent(), pendingIntent2.getIntent(), splitPosition,
+ snapPosition);
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (pendingIntent2 == null) {
options1 = options1 != null ? options1 : new Bundle();
@@ -1302,6 +1328,7 @@
}
void switchSplitPosition(String reason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "switchSplitPosition");
final SurfaceControl.Transaction t = mTransactionPool.acquire();
mTempRect1.setEmpty();
final StageTaskListener topLeftStage =
@@ -1343,7 +1370,7 @@
});
});
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Switch split position: %s", reason);
+ ProtoLog.v(WM_SHELL_SPLIT_SCREEN, "Switch split position: %s", reason);
mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
getSideStagePosition(), mSideStage.getTopChildTaskUid(),
mSplitLayout.isLeftRightSplit());
@@ -1376,11 +1403,12 @@
if (!mMainStage.isActive()) {
return;
}
-
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onKeyguardVisibilityChanged: showing=%b", showing);
setDividerVisibility(!mKeyguardShowing, null);
}
void onFinishedWakingUp() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinishedWakingUp");
if (!mMainStage.isActive()) {
return;
}
@@ -1421,6 +1449,8 @@
}
void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitSplitScreen: topTaskId=%d reason=%s active=%b",
+ toTopTaskId, exitReasonToString(exitReason), mMainStage.isActive());
if (!mMainStage.isActive()) return;
StageTaskListener childrenToTop = null;
@@ -1439,6 +1469,8 @@
private void exitSplitScreen(@Nullable StageTaskListener childrenToTop,
@ExitReason int exitReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitSplitScreen: mainStageToTop=%b reason=%s active=%b",
+ childrenToTop == mMainStage, exitReasonToString(exitReason), mMainStage.isActive());
if (!mMainStage.isActive()) return;
final WindowContainerTransaction wct = new WindowContainerTransaction();
@@ -1447,6 +1479,8 @@
private void applyExitSplitScreen(@Nullable StageTaskListener childrenToTop,
WindowContainerTransaction wct, @ExitReason int exitReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "applyExitSplitScreen: reason=%s",
+ exitReasonToString(exitReason));
if (!mMainStage.isActive() || mIsExiting) return;
onSplitScreenExit();
@@ -1502,7 +1536,6 @@
}
});
- Slog.i(TAG, "applyExitSplitScreen, reason = " + exitReasonToString(exitReason));
// Log the exit
if (childrenToTop != null) {
logExitToStage(exitReason, childrenToTop == mMainStage);
@@ -1527,6 +1560,7 @@
* Exits the split screen by finishing one of the tasks.
*/
protected void exitStage(@SplitPosition int stageToClose) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitStage: stageToClose=%d", stageToClose);
mSplitLayout.flingDividerToDismiss(stageToClose == SPLIT_POSITION_BOTTOM_OR_RIGHT,
EXIT_REASON_APP_FINISHED);
}
@@ -1540,12 +1574,13 @@
try {
activityTaskManagerService.setFocusedTask(getTaskId(stageToFocus));
} catch (RemoteException | NullPointerException e) {
- ProtoLog.e(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ ProtoLog.e(WM_SHELL_SPLIT_SCREEN,
"Unable to update focus on the chosen stage: %s", e.getMessage());
}
}
private void clearRequestIfPresented() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearRequestIfPresented");
if (mSideStageListener.mVisible && mSideStageListener.mHasChildren
&& mMainStageListener.mVisible && mSideStageListener.mHasChildren) {
mSplitRequest = null;
@@ -1581,6 +1616,8 @@
void clearSplitPairedInRecents(@ExitReason int exitReason) {
if (!shouldBreakPairedTaskInRecents(exitReason) || !mShouldUpdateRecents) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "clearSplitPairedInRecents: reason=%s",
+ exitReasonToString(exitReason));
mRecentTasks.ifPresent(recentTasks -> {
// Notify recents if we are exiting in a way that breaks the pair, and disable further
// updates to splits in the recents until we enter split again
@@ -1597,11 +1634,13 @@
void prepareExitSplitScreen(@StageType int stageToTop,
@NonNull WindowContainerTransaction wct) {
if (!mMainStage.isActive()) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareExitSplitScreen: stageToTop=%d", stageToTop);
mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE);
mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN);
}
private void prepareEnterSplitScreen(WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareEnterSplitScreen");
prepareEnterSplitScreen(wct, null /* taskInfo */, SPLIT_POSITION_UNDEFINED,
!mIsDropEntering);
}
@@ -1613,6 +1652,8 @@
void prepareEnterSplitScreen(WindowContainerTransaction wct,
@Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
boolean resizeAnim) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareEnterSplitScreen: position=%d resize=%b",
+ startPosition, resizeAnim);
onSplitScreenEnter();
// Preemptively reset the reparenting behavior if we know that we are entering, as starting
// split tasks with activity trampolines can inadvertently trigger the task to be
@@ -1629,6 +1670,8 @@
private void prepareBringSplit(WindowContainerTransaction wct,
@Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
boolean resizeAnim) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareBringSplit: task=%d isSplitVisible=%b",
+ taskInfo != null ? taskInfo.taskId : -1, isSplitScreenVisible());
if (taskInfo != null) {
wct.startTask(taskInfo.taskId,
resolveStartStage(STAGE_TYPE_UNDEFINED, startPosition, null, wct));
@@ -1649,6 +1692,8 @@
private void prepareActiveSplit(WindowContainerTransaction wct,
@Nullable ActivityManager.RunningTaskInfo taskInfo, @SplitPosition int startPosition,
boolean resizeAnim) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareActiveSplit: task=%d isSplitVisible=%b",
+ taskInfo != null ? taskInfo.taskId : -1, isSplitScreenVisible());
if (!ENABLE_SHELL_TRANSITIONS) {
// Legacy transition we need to create divider here, shell transition case we will
// create it on #finishEnterSplitScreen
@@ -1667,6 +1712,7 @@
}
private void prepareSplitLayout(WindowContainerTransaction wct, boolean resizeAnim) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareSplitLayout: resize=%b", resizeAnim);
if (resizeAnim) {
mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
} else {
@@ -1686,6 +1732,7 @@
}
void finishEnterSplitScreen(SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "finishEnterSplitScreen");
mSplitLayout.update(finishT, true /* resetImePosition */);
mMainStage.getSplitDecorManager().inflate(mContext, mMainStage.mRootLeash,
getMainStageBounds());
@@ -1835,12 +1882,20 @@
leftTopTaskId, rightBottomTaskId, mSplitLayout.calculateCurrentSnapPosition());
if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
// Update the pair for the top tasks
- recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId, splitBounds);
+ boolean added = recentTasks.addSplitPair(mainStageTopTaskId, sideStageTopTaskId,
+ splitBounds);
+ if (added) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "updateRecentTasksSplitPair: adding split pair ltTask=%d rbTask=%d",
+ leftTopTaskId, rightBottomTaskId);
+ }
}
});
}
private void sendSplitVisibilityChanged() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "sendSplitVisibilityChanged: dividerVisible=%b",
+ mDividerVisible);
for (int i = mListeners.size() - 1; i >= 0; --i) {
final SplitScreen.SplitScreenListener l = mListeners.get(i);
l.onSplitVisibilityChanged(mDividerVisible);
@@ -1855,6 +1910,7 @@
throw new IllegalArgumentException(this + "\n Unknown task appeared: " + taskInfo);
}
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: task=%s", taskInfo);
mRootTaskInfo = taskInfo;
mRootTaskLeash = leash;
@@ -1880,6 +1936,8 @@
if (mSplitLayout != null
&& mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)
&& mMainStage.isActive()) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: task=%d updating",
+ taskInfo.taskId);
// Clear the divider remote animating flag as the divider will be re-rendered to apply
// the new rotation config. Don't reset the IME state since those updates are not in
// sync with task info changes.
@@ -1892,6 +1950,7 @@
@Override
@CallSuper
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskVanished: task=%s", taskInfo);
if (mRootTaskInfo == null) {
throw new IllegalArgumentException(this + "\n Unknown task vanished: " + taskInfo);
}
@@ -1911,6 +1970,8 @@
@VisibleForTesting
void onRootTaskAppeared() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskAppeared: rootTask=%s mainRoot=%b sideRoot=%b",
+ mRootTaskInfo, mMainStageListener.mHasRootTask, mSideStageListener.mHasRootTask);
// Wait unit all root tasks appeared.
if (mRootTaskInfo == null
|| !mMainStageListener.mHasRootTask
@@ -1937,6 +1998,8 @@
* #onStageHasChildrenChanged because this would be called every time child task appeared.
* NOTICE: This only be called on legacy transition. */
private void onChildTaskAppeared(StageListenerImpl stageListener, int taskId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onChildTaskAppeared: isMainStage=%b task=%d",
+ stageListener == mMainStageListener, taskId);
// Handle entering split screen while there is a split pair running in the background.
if (stageListener == mSideStageListener && !isSplitScreenVisible() && isSplitActive()
&& mSplitRequest == null) {
@@ -1960,6 +2023,7 @@
}
private void onRootTaskVanished() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRootTaskVanished");
final WindowContainerTransaction wct = new WindowContainerTransaction();
mLaunchAdjacentController.clearLaunchAdjacentRoot();
applyExitSplitScreen(null /* childrenToTop */, wct, EXIT_REASON_ROOT_TASK_VANISHED);
@@ -1990,6 +2054,8 @@
return;
}
+ // TODO Protolog
+
// Check if it needs to dismiss split screen when both stage invisible.
if (!mainStageVisible && mExitSplitScreenOnHide) {
exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RETURN_HOME);
@@ -2020,14 +2086,14 @@
return;
}
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "Request to %s divider bar from %s.",
- (visible ? "show" : "hide"), Debug.getCaller());
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "setDividerVisibility: visible=%b keyguardShowing=%b dividerAnimating=%b caller=%s",
+ visible, mKeyguardShowing, mIsDividerRemoteAnimating, Debug.getCaller());
// Defer showing divider bar after keyguard dismissed, so it won't interfere with keyguard
// dismissing animation.
if (visible && mKeyguardShowing) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
" Defer showing divider bar due to keyguard showing.");
return;
}
@@ -2036,7 +2102,7 @@
sendSplitVisibilityChanged();
if (mIsDividerRemoteAnimating) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
" Skip animating divider bar due to it's remote animating.");
return;
}
@@ -2050,12 +2116,12 @@
private void applyDividerVisibility(@Nullable SurfaceControl.Transaction t) {
final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash();
if (dividerLeash == null) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
" Skip animating divider bar due to divider leash not ready.");
return;
}
if (mIsDividerRemoteAnimating) {
- ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
" Skip animating divider bar due to it's remote animating.");
return;
}
@@ -2119,6 +2185,8 @@
/** Callback when split roots have child or haven't under it.
* NOTICE: This only be called on legacy transition. */
private void onStageHasChildrenChanged(StageListenerImpl stageListener) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onStageHasChildrenChanged: isMainStage=%b",
+ stageListener == mMainStageListener);
final boolean hasChildren = stageListener.mHasChildren;
final boolean isSideStage = stageListener == mSideStageListener;
if (!hasChildren && !mIsExiting && mMainStage.isActive()) {
@@ -2170,13 +2238,15 @@
}
@Override
- public void onSnappedToDismiss(boolean bottomOrRight, int reason) {
+ public void onSnappedToDismiss(boolean bottomOrRight, @ExitReason int exitReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onSnappedToDismiss: bottomOrRight=%b reason=%s",
+ bottomOrRight, exitReasonToString(exitReason));
final boolean mainStageToTop =
bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
: mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
final StageTaskListener toTopStage = mainStageToTop ? mMainStage : mSideStage;
if (!ENABLE_SHELL_TRANSITIONS) {
- exitSplitScreen(toTopStage, reason);
+ exitSplitScreen(toTopStage, exitReason);
return;
}
@@ -2219,6 +2289,7 @@
@Override
public void onLayoutSizeChanged(SplitLayout layout) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onLayoutSizeChanged");
// Reset this flag every time onLayoutSizeChanged.
mShowDecorImmediately = false;
@@ -2278,8 +2349,11 @@
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
final StageTaskListener bottomRightStage =
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage;
- return layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo,
+ boolean updated = layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo,
bottomRightStage.mRootTaskInfo);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "updateWindowBounds: topLeftStage=%s bottomRightStage=%s",
+ layout.getBounds1(), layout.getBounds2());
+ return updated;
}
void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t,
@@ -2291,6 +2365,9 @@
(layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash,
bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer,
applyResizingOffset);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "updateSurfaceBounds: topLeftStage=%s bottomRightStage=%s",
+ layout.getBounds1(), layout.getBounds2());
}
@Override
@@ -2329,6 +2406,8 @@
@Override
public void setLayoutOffsetTarget(int offsetX, int offsetY, SplitLayout layout) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setLayoutOffsetTarget: x=%d y=%d",
+ offsetX, offsetY);
final StageTaskListener topLeftStage =
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
final StageTaskListener bottomRightStage =
@@ -2343,6 +2422,7 @@
if (displayId != DEFAULT_DISPLAY) {
return;
}
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onDisplayAdded: display=%d", displayId);
mDisplayController.addDisplayChangingController(this::onDisplayChange);
}
@@ -2357,8 +2437,14 @@
private void onDisplayChange(int displayId, int fromRotation, int toRotation,
@Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) {
- if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) return;
+ if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) {
+ return;
+ }
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "onDisplayChange: display=%d fromRot=%d toRot=%d config=%s",
+ displayId, fromRotation, toRotation,
+ newDisplayAreaInfo != null ? newDisplayAreaInfo.configuration : null);
mSplitLayout.rotateTo(toRotation);
if (newDisplayAreaInfo != null) {
mSplitLayout.updateConfiguration(newDisplayAreaInfo.configuration);
@@ -2369,6 +2455,7 @@
@VisibleForTesting
void onFoldedStateChanged(boolean folded) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFoldedStateChanged: folded=%b", folded);
mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
if (!folded) return;
@@ -2439,6 +2526,8 @@
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
if (triggerTask == null) {
if (isSplitActive()) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d display rotation",
+ request.getDebugId());
// Check if the display is rotating.
final TransitionRequestInfo.DisplayChange displayChange =
request.getDisplayChange();
@@ -2467,6 +2556,8 @@
}
if (isSplitActive()) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d split active",
+ request.getDebugId());
// Try to handle everything while in split-screen, so return a WCT even if it's empty.
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split"
+ "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
@@ -2541,6 +2632,8 @@
return null;
} else {
if (isOpening && getStageOfTask(triggerTask) != null) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "handleRequest: transition=%d enter split",
+ request.getDebugId());
// One task is appearing into split, prepare to enter split screen.
out = new WindowContainerTransaction();
prepareEnterSplitScreen(out);
@@ -2557,6 +2650,8 @@
*/
public void addEnterOrExitIfNeeded(@Nullable TransitionRequestInfo request,
@NonNull WindowContainerTransaction outWCT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "addEnterOrExitIfNeeded: transition=%d",
+ request.getDebugId());
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
if (triggerTask != null && triggerTask.displayId != mDisplayId) {
// Skip handling task on the other display.
@@ -2591,6 +2686,7 @@
public void mergeAnimation(IBinder transition, TransitionInfo info,
SurfaceControl.Transaction t, IBinder mergeTarget,
Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "mergeAnimation: transition=%d", info.getDebugId());
mSplitTransitions.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
}
@@ -2602,6 +2698,7 @@
@Override
public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
@Nullable SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed");
mSplitTransitions.onTransitionConsumed(transition, aborted, finishT);
}
@@ -2617,6 +2714,7 @@
// If we're not in split-mode, just abort so something else can handle it.
if (!mMainStage.isActive()) return false;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startAnimation: transition=%d", info.getDebugId());
mSplitLayout.setFreezeDividerWindow(false);
final StageChangeRecord record = new StageChangeRecord();
final int transitType = info.getType();
@@ -2727,6 +2825,8 @@
if (mMixedHandler.animatePendingSplitWithDisplayChange(transition, info,
startTransaction, finishTransaction, finishCallback)) {
if (mSplitTransitions.isPendingResize(transition)) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startAnimation: transition=%d display change", info.getDebugId());
// Only need to update in resize because divider exist before transition.
mSplitLayout.update(startTransaction, true /* resetImePosition */);
startTransaction.apply();
@@ -2797,6 +2897,8 @@
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startPendingAnimation: transition=%d",
+ info.getDebugId());
boolean shouldAnimate = true;
if (mSplitTransitions.isPendingEnter(transition)) {
shouldAnimate = startPendingEnterAnimation(transition,
@@ -2830,6 +2932,7 @@
/** Called to clean-up state and do house-keeping after the animation is done. */
public void onTransitionAnimationComplete() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionAnimationComplete");
// If still playing, let it finish.
if (!mMainStage.isActive() && !mIsExiting) {
// Update divider state after animation so that it is still around and positioned
@@ -2842,6 +2945,8 @@
@NonNull SplitScreenTransitions.EnterSession enterTransition,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
@NonNull SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startPendingEnterAnimation: enterTransition=%s",
+ enterTransition);
// First, verify that we actually have opened apps in both splits.
TransitionInfo.Change mainChild = null;
TransitionInfo.Change sideChild = null;
@@ -2959,6 +3064,7 @@
}
public void goToFullscreenFromSplit() {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "goToFullscreenFromSplit");
// If main stage is focused, toEnd = true if
// mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT. Otherwise toEnd = false
// If side stage is focused, toEnd = true if
@@ -2974,6 +3080,7 @@
/** Move the specified task to fullscreen, regardless of focus state. */
public void moveTaskToFullscreen(int taskId, int exitReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "moveTaskToFullscreen");
boolean leftOrTop;
if (mMainStage.containsTask(taskId)) {
leftOrTop = (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
@@ -2994,6 +3101,7 @@
*/
public void onPipExpandToSplit(WindowContainerTransaction wct,
ActivityManager.RunningTaskInfo taskInfo) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onPipExpandToSplit: task=%s", taskInfo);
prepareEnterSplitScreen(wct, taskInfo, getActivateSplitPosition(taskInfo),
false /*resizeAnim*/);
@@ -3040,6 +3148,9 @@
public void prepareDismissAnimation(@StageType int toStage, @ExitReason int dismissReason,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
@NonNull SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "prepareDismissAnimation: transition=%d toStage=%d reason=%s",
+ info.getDebugId(), toStage, exitReasonToString(dismissReason));
// Make some noise if things aren't totally expected. These states shouldn't effect
// transitions locally, but remotes (like Launcher) may get confused if they were
// depending on listener callbacks. This can happen because task-organizer callbacks
@@ -3126,6 +3237,9 @@
@NonNull SplitScreenTransitions.DismissSession dismissTransition,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t,
@NonNull SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "startPendingDismissAnimation: transition=%d dismissTransition=%s",
+ info.getDebugId(), dismissTransition);
prepareDismissAnimation(dismissTransition.mDismissTop, dismissTransition.mReason, info,
t, finishT);
if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) {
@@ -3146,6 +3260,8 @@
/** Call this when starting the open-recents animation while split-screen is active. */
public void onRecentsInSplitAnimationStart(TransitionInfo info) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsInSplitAnimationStart: transition=%d",
+ info.getDebugId());
if (isSplitScreenVisible()) {
// Cache tasks on live tile.
for (int i = 0; i < info.getChanges().size(); ++i) {
@@ -3178,6 +3294,7 @@
/** Call this when the recents animation during split-screen finishes. */
public void onRecentsInSplitAnimationFinish(WindowContainerTransaction finishWct,
SurfaceControl.Transaction finishT) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsInSplitAnimationFinish");
mPausingTasks.clear();
// Check if the recent transition is finished by returning to the current
// split, so we can restore the divider bar.
@@ -3203,6 +3320,7 @@
/** Call this when the recents animation finishes by doing pair-to-pair switch. */
public void onRecentsPairToPairAnimationFinish(WindowContainerTransaction finishWct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsPairToPairAnimationFinish");
// Pair-to-pair switch happened so here should evict the live tile from its stage.
// Otherwise, the task will remain in stage, and occluding the new task when next time
// user entering recents.
@@ -3284,6 +3402,7 @@
* handled.
*/
private void setSplitsVisible(boolean visible) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setSplitsVisible: visible=%b", visible);
mMainStageListener.mVisible = mSideStageListener.mVisible = visible;
mMainStageListener.mHasChildren = mSideStageListener.mHasChildren = visible;
}
@@ -3292,6 +3411,7 @@
* Sets drag info to be logged when splitscreen is next entered.
*/
public void onDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onDroppedToSplit: position=%d", position);
if (!isSplitScreenVisible()) {
mIsDropEntering = true;
mSkipEvictingMainStageChildren = true;
@@ -3308,7 +3428,8 @@
/**
* Sets info to be logged when splitscreen is next entered.
*/
- public void onRequestToSplit(InstanceId sessionId, int enterReason) {
+ public void onRequestToSplit(InstanceId sessionId, @SplitEnterReason int enterReason) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRequestToSplit: reason=%d", enterReason);
if (!isSplitScreenVisible() && !ENABLE_SHELL_TRANSITIONS) {
// If split running background, exit split first.
// Skip this on shell transition due to we could evict existing tasks on transition
@@ -3384,6 +3505,7 @@
@Override
public void onNoLongerSupportMultiWindow(ActivityManager.RunningTaskInfo taskInfo) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onNoLongerSupportMultiWindow: task=%s", taskInfo);
if (mMainStage.isActive()) {
final boolean isMainStage = mMainStageListener == this;
if (!ENABLE_SHELL_TRANSITIONS) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index af7bf36..f33ab33 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -25,6 +25,7 @@
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
import android.annotation.CallSuper;
@@ -44,6 +45,7 @@
import androidx.annotation.NonNull;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -175,6 +177,9 @@
@Override
@CallSuper
public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskAppeared: task=%d taskParent=%d rootTask=%d",
+ taskInfo.taskId, taskInfo.parentTaskId,
+ mRootTaskInfo != null ? mRootTaskInfo.taskId : -1);
if (mRootTaskInfo == null) {
mRootLeash = leash;
mRootTaskInfo = taskInfo;
@@ -225,6 +230,9 @@
|| !ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
|| !ArrayUtils.contains(CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE,
taskInfo.getWindowingMode())) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
+ "onTaskInfoChanged: task=%d no longer supports multiwindow",
+ taskInfo.taskId);
// Leave split screen if the task no longer supports multi window or have
// uncontrolled task.
mCallbacks.onNoLongerSupportMultiWindow(taskInfo);
@@ -251,6 +259,7 @@
@Override
@CallSuper
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskVanished: task=%d", taskInfo.taskId);
final int taskId = taskInfo.taskId;
if (mRootTaskInfo.taskId == taskId) {
mCallbacks.onRootTaskVanished();
@@ -333,6 +342,7 @@
}
void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "addTask: task=%d", task.taskId);
// Clear overridden bounds and windowing mode to make sure the child task can inherit
// windowing mode and bounds from split root.
wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
@@ -342,6 +352,7 @@
}
void reorderChild(int taskId, boolean onTop, WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "reorderChild: task=%d onTop=%b", taskId, onTop);
if (!containsTask(taskId)) {
return;
}
@@ -357,6 +368,7 @@
/** Collects all the current child tasks and prepares transaction to evict them to display. */
void evictAllChildren(WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evicting all children");
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
@@ -367,11 +379,13 @@
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
if (taskId == taskInfo.taskId) continue;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict other child: task=%d", taskId);
wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
}
}
void evictNonOpeningChildren(RemoteAnimationTarget[] apps, WindowContainerTransaction wct) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "evictNonOpeningChildren");
final SparseArray<ActivityManager.RunningTaskInfo> toBeEvict = mChildrenTaskInfo.clone();
for (int i = 0; i < apps.length; i++) {
if (apps[i].mode == MODE_OPENING) {
@@ -380,6 +394,7 @@
}
for (int i = toBeEvict.size() - 1; i >= 0; i--) {
final ActivityManager.RunningTaskInfo taskInfo = toBeEvict.valueAt(i);
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict non-opening child: task=%d", taskInfo.taskId);
wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
}
}
@@ -388,12 +403,15 @@
for (int i = mChildrenTaskInfo.size() - 1; i >= 0; i--) {
final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i);
if (!taskInfo.isVisible) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict invisible child: task=%d",
+ taskInfo.taskId);
wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
}
}
}
void evictChildren(WindowContainerTransaction wct, int taskId) {
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Evict child: task=%d", taskId);
final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.get(taskId);
if (taskInfo != null) {
wct.reparent(taskInfo.token, null /* parent */, false /* onTop */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index c1406d0..caa894f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -22,8 +22,10 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.view.InputDevice.SOURCE_TOUCHSCREEN;
+import static android.view.MotionEvent.ACTION_CANCEL;
import static android.view.MotionEvent.ACTION_HOVER_ENTER;
import static android.view.MotionEvent.ACTION_HOVER_EXIT;
+import static android.view.MotionEvent.ACTION_UP;
import static android.view.WindowInsets.Type.statusBars;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -59,7 +61,6 @@
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.View;
-import android.view.ViewConfiguration;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -321,8 +322,8 @@
private final GestureDetector mGestureDetector;
private boolean mIsDragging;
+ private boolean mTouchscreenInUse;
private boolean mHasLongClicked;
- private boolean mShouldClick;
private int mDragPointerId = -1;
private final Runnable mCloseMaximizeWindowRunnable;
@@ -343,6 +344,10 @@
@Override
public void onClick(View v) {
+ if (mIsDragging) {
+ mIsDragging = false;
+ return;
+ }
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
final int id = v.getId();
if (id == R.id.close_window) {
@@ -421,6 +426,10 @@
@Override
public boolean onTouch(View v, MotionEvent e) {
final int id = v.getId();
+ if ((e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN) {
+ mTouchscreenInUse = e.getActionMasked() != ACTION_UP
+ && e.getActionMasked() != ACTION_CANCEL;
+ }
if (id != R.id.caption_handle && id != R.id.desktop_mode_caption
&& id != R.id.open_menu_button && id != R.id.close_window
&& id != R.id.maximize_window) {
@@ -432,31 +441,19 @@
if (!mHasLongClicked && id != R.id.maximize_window) {
decoration.closeMaximizeMenuIfNeeded(e);
}
-
- final long eventDuration = e.getEventTime() - e.getDownTime();
- final boolean isTouchScreen =
- (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN;
- final boolean shouldLongClick = isTouchScreen && id == R.id.maximize_window
- && !mIsDragging && !mHasLongClicked
- && eventDuration >= ViewConfiguration.getLongPressTimeout();
- if (shouldLongClick) {
- v.performLongClick();
- mHasLongClicked = true;
- return true;
- }
-
return mDragDetector.onMotionEvent(v, e);
}
@Override
public boolean onLongClick(View v) {
final int id = v.getId();
- if (id == R.id.maximize_window) {
+ if (id == R.id.maximize_window && mTouchscreenInUse) {
final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
moveTaskToFront(decoration.mTaskInfo);
if (decoration.isMaximizeMenuActive()) {
decoration.closeMaximizeMenu();
} else {
+ mHasLongClicked = true;
decoration.createMaximizeMenu();
}
return true;
@@ -515,11 +512,9 @@
if (mGestureDetector.onTouchEvent(e)) {
return true;
}
- if (e.getActionMasked() == MotionEvent.ACTION_CANCEL) {
- // If a motion event is cancelled, reset mShouldClick so a click is not accidentally
- // performed.
- mShouldClick = false;
- }
+ final int id = v.getId();
+ final boolean touchingButton = (id == R.id.close_window || id == R.id.maximize_window
+ || id == R.id.open_menu_button);
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
mDragPointerId = e.getPointerId(0);
@@ -527,12 +522,12 @@
0 /* ctrlType */, e.getRawX(0),
e.getRawY(0));
mIsDragging = false;
- mShouldClick = true;
mHasLongClicked = false;
- return true;
+ // Do not consume input event if a button is touched, otherwise it would
+ // prevent the button's ripple effect from showing.
+ return !touchingButton;
}
case MotionEvent.ACTION_MOVE: {
- mShouldClick = false;
// If a decor's resize drag zone is active, don't also try to reposition it.
if (decoration.isHandlingDragResize()) break;
decoration.closeMaximizeMenu();
@@ -553,11 +548,6 @@
case MotionEvent.ACTION_CANCEL: {
final boolean wasDragging = mIsDragging;
if (!wasDragging) {
- if (mShouldClick && v != null && !mHasLongClicked) {
- v.performClick();
- mShouldClick = false;
- return true;
- }
return false;
}
if (e.findPointerIndex(mDragPointerId) == -1) {
@@ -576,8 +566,15 @@
position,
new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)),
newTaskBounds));
- mIsDragging = false;
- return true;
+ if (touchingButton && !mHasLongClicked) {
+ // We need the input event to not be consumed here to end the ripple
+ // effect on the touched button. We will reset drag state in the ensuing
+ // onClick call that results.
+ return false;
+ } else {
+ mIsDragging = false;
+ return true;
+ }
}
}
return true;
diff --git a/media/java/android/media/RingtoneSelection.java b/media/java/android/media/RingtoneSelection.java
deleted file mode 100644
index b7c3721..0000000
--- a/media/java/android/media/RingtoneSelection.java
+++ /dev/null
@@ -1,742 +0,0 @@
-/*
- * Copyright (C) 2023 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.media;
-
-import static java.util.Objects.requireNonNull;
-
-import android.annotation.FlaggedApi;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.TestApi;
-import android.content.ContentProvider;
-import android.content.ContentResolver;
-import android.net.Uri;
-import android.os.UserHandle;
-import android.os.vibrator.Flags;
-import android.provider.MediaStore;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * Immutable representation a desired ringtone, usually originating from a user preference.
- * Unlike sound-only Uris, a "silent" setting is an explicit selection value, rather than null.
- *
- * <p>This representation can be converted into (or from) a URI form for storing within a string
- * preference or when using the ringtone picker via {@link RingtoneManager#ACTION_RINGTONE_PICKER}.
- * It does not carry any actual media data - it only references the components that make
- * up the preference. Initial selections can be built using {@link RingtoneSelection.Builder}.
- *
- * <p>A RingtoneSelection is typically played by passing into a {@link Ringtone.Builder}, and
- * supplementing with contextual defaults from the application. Bad Uris are handled by the
- * {@link Ringtone} class - the RingtoneSelection doesn't validate the target of the Uri.
- *
- * <p>When a RingtoneSelection is created/loaded, the values of its properties are modified
- * to be internally consistent and reflect effective values - with the exception of not verifying
- * the actual URI content. For example, loading a selection Uri that sets a sound source to
- * {@link #SOUND_SOURCE_URI}, but doesn't also have a sound Uri set, will result in this class
- * instead returning {@link #SOUND_SOURCE_UNSPECIFIED} from {@link #getSoundSource}.
- *
- * <h2>Storing preferences</h2>
- *
- * <p>A ringtone preference can have several states: either unset, set to a ringtone selection Uri,
- * or, from prior to the introduction of {@code RingtoneSelection}, set to a sound-only Uri or
- * explicitly set to null to indicate silent.
- *
- * @hide
- */
-@TestApi
-@FlaggedApi(Flags.FLAG_HAPTICS_CUSTOMIZATION_ENABLED)
-public final class RingtoneSelection {
-
- /**
- * The sound source was specified but its value was not recognized. This value is used
- * internally for not stripping unrecognised (possibly future) values during processing.
- * @hide
- */
- public static final int SOUND_SOURCE_UNKNOWN = -1;
-
- /**
- * The sound source is not explicitly specified, so it can follow default behavior for its
- * context.
- */
- public static final int SOUND_SOURCE_UNSPECIFIED = 0;
-
- /**
- * Sound is explicitly disabled, such as the user having selected "Silent" in the sound picker.
- */
- public static final int SOUND_SOURCE_OFF = 1;
-
- /**
- * The sound Uri should be used as the source of sound.
- */
- public static final int SOUND_SOURCE_URI = 2;
-
- /**
- * The sound should explicitly use the system default.
- *
- * <p>This value isn't valid within the system default itself.
- */
- public static final int SOUND_SOURCE_SYSTEM_DEFAULT = 3;
-
- // Note: Value 4 reserved for possibility of SOURCE_SOURCE_APPLICATION_DEFAULT.
-
- /**
- * Directive for how to make sound.
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = "SOUND_SOURCE_", value = {
- SOUND_SOURCE_UNKNOWN,
- SOUND_SOURCE_UNSPECIFIED,
- SOUND_SOURCE_OFF,
- SOUND_SOURCE_URI,
- SOUND_SOURCE_SYSTEM_DEFAULT,
- })
- public @interface SoundSource {}
-
- /**
- * The vibration source was specified but its value was not recognized.
- * This value is used internally for not stripping unrecognised (possibly
- * future) values during processing.
- * @hide
- */
- public static final int VIBRATION_SOURCE_UNKNOWN = -1;
-
- /**
- * Vibration source is not explicitly specified. If vibration is enabled, this will use the
- * first available of {@link #VIBRATION_SOURCE_AUDIO_CHANNEL},
- * {@link #VIBRATION_SOURCE_APPLICATION_DEFAULT}, or {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
- */
- public static final int VIBRATION_SOURCE_UNSPECIFIED = 0;
-
- /** Specifies that vibration is explicitly disabled for this ringtone. */
- public static final int VIBRATION_SOURCE_OFF = 1;
-
- /** The vibration Uri should be used as the source of vibration. */
- public static final int VIBRATION_SOURCE_URI = 2;
-
- /**
- * The vibration should explicitly use the system default.
- *
- * <p>This value isn't valid within the system default itself.
- */
- public static final int VIBRATION_SOURCE_SYSTEM_DEFAULT = 3;
-
- /**
- * Specifies that vibration should use the vibration provided by the application. This is
- * typically the application's own default for the use-case, provided via
- * {@link Ringtone.Builder#setVibrationEffect}. For notification channels, this is the vibration
- * effect saved on the notification channel.
- *
- * <p>If no vibration is specified by the application, this value behaves if the source was
- * {@link #VIBRATION_SOURCE_UNSPECIFIED}.
- *
- * <p>This value isn't valid within the system default.
- */
- public static final int VIBRATION_SOURCE_APPLICATION_DEFAULT = 4;
-
- /**
- * Specifies that vibration should use haptic audio channels from the
- * sound Uri. If the sound URI doesn't have haptic channels, then reverts to the order specified
- * by {@link #VIBRATION_SOURCE_UNSPECIFIED}.
- */
- // Numeric gap from VIBRATION_SOURCE_APPLICATION_DEFAULT in case we want other common elements.
- public static final int VIBRATION_SOURCE_AUDIO_CHANNEL = 10;
-
- /**
- * Specifies that vibration should generate haptic audio channels from the
- * audio tracks of the sound Uri.
- *
- * If the sound Uri already has haptic channels, then behaves as though
- * {@link #VIBRATION_SOURCE_AUDIO_CHANNEL} was specified instead.
- */
- public static final int VIBRATION_SOURCE_HAPTIC_GENERATOR = 11;
-
- /**
- * Directive for how to vibrate.
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = "VIBRATION_SOURCE_", value = {
- VIBRATION_SOURCE_UNKNOWN,
- VIBRATION_SOURCE_UNSPECIFIED,
- VIBRATION_SOURCE_OFF,
- VIBRATION_SOURCE_URI,
- VIBRATION_SOURCE_APPLICATION_DEFAULT,
- VIBRATION_SOURCE_AUDIO_CHANNEL,
- VIBRATION_SOURCE_HAPTIC_GENERATOR,
- })
- public @interface VibrationSource {}
-
- /**
- * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as the sound Uri
- * for the returned {@link RingtoneSelection}, with null meaning {@link #SOUND_SOURCE_OFF},
- * and symbolic default URIs ({@link RingtoneManager#getDefaultUri}) meaning
- * {@link #SOUND_SOURCE_SYSTEM_DEFAULT}.
- *
- * <p>This behavior is particularly suited to loading values from older settings that may
- * contain a raw sound Uri or null for silent.
- *
- * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false.
- */
- public static final int FROM_URI_RINGTONE_SELECTION_OR_SOUND = 1;
-
- /**
- * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as the vibration
- * Uri for the returned {@link RingtoneSelection}, with null meaning
- * {@link #VIBRATION_SOURCE_OFF} and symbolic default URIs
- * ({@link RingtoneManager#getDefaultUri}) meaning {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
- *
- * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false.
- */
- public static final int FROM_URI_RINGTONE_SELECTION_OR_VIBRATION = 2;
-
- /**
- * Configures {@link #RingtoneSelection#fromUri} to treat an unrecognized Uri as an invalid
- * value. Null or an invalid values will revert to default behavior correspnoding to
- * {@link #DEFAULT_SELECTION_URI_STRING}. Symbolic default URIs
- * ({@link RingtoneManager#getDefaultUri}) will set both
- * {@link #SOUND_SOURCE_SYSTEM_DEFAULT} and {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
- *
- * <p>An unrecognized Uri is one for which {@link #isRingtoneSelectionUri(Uri)} returns false,
- * which include {@code null}.
- */
- public static final int FROM_URI_RINGTONE_SELECTION_ONLY = 3;
-
- /**
- * How to treat values in {@link #fromUri}.
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = "FROM_URI_", value = {
- FROM_URI_RINGTONE_SELECTION_OR_SOUND,
- FROM_URI_RINGTONE_SELECTION_OR_VIBRATION,
- FROM_URI_RINGTONE_SELECTION_ONLY
- })
- public @interface FromUriBehavior {}
-
- private static final String BASE_RINGTONE_URI = "content://media/ringtone";
- /**
- * String representation of a RingtoneSelection Uri that says to use defaults (equivalent
- * to {@code new RingtoneSelection.Builder().build()}).
- */
- public static final String DEFAULT_SELECTION_URI_STRING = BASE_RINGTONE_URI;
-
- private static final String MEDIA_URI_RINGTONE_PATH = "/ringtone";
-
- /* Query param keys. */
- private static final String URI_PARAM_SOUND_URI = "su";
- private static final String URI_PARAM_SOUND_SOURCE = "ss";
- private static final String URI_PARAM_VIBRATION_URI = "vu";
- private static final String URI_PARAM_VIBRATION_SOURCE = "vs";
-
- /* Common param values */
- private static final String SOURCE_OFF_STRING = "off";
- private static final String SOURCE_SYSTEM_DEFAULT_STRING = "sys";
-
- /* Vibration source param values. */
- private static final String VIBRATION_SOURCE_AUDIO_CHANNEL_STRING = "ac";
- private static final String VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING = "app";
- private static final String VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING = "hg";
-
- @Nullable
- private final Uri mSoundUri;
- @SoundSource
- private final int mSoundSource;
-
- @Nullable
- private final Uri mVibrationUri;
- @VibrationSource
- private final int mVibrationSource;
-
- private RingtoneSelection(@Nullable Uri soundUri, @SoundSource int soundSource,
- @Nullable Uri vibrationUri, @VibrationSource int vibrationSource) {
- // Enforce guarantees on the source values: revert to unspecified if they depend on
- // something that's not set.
- //
- // The non-public "unknown" value can't appear in a getter result, it's just a reserved
- // "null" value and should be treated the same as an unrecognized value. This can be seen
- // in Uri parsing. For this and other unrecognized values, we either revert them to the URI
- // source, if a Uri was included, or the "unspecified" source otherwise. This can be
- // seen in action in the Uri parsing.
- //
- // The "unspecified" source is a public value meaning that there is no specific
- // behavior indicated, and the defaults and fallbacks should be applied. For example, an
- // vibration source value of "system default" means to explicitly use the system default
- // vibration. However, an "unspecified" vibration source will first see if audio coupled
- // or application-default vibrations are available.
- mSoundSource = switch (soundSource) {
- // Supported explicit values that don't have a Uri.
- case SOUND_SOURCE_OFF, SOUND_SOURCE_UNSPECIFIED, SOUND_SOURCE_SYSTEM_DEFAULT ->
- soundSource;
- // Uri and unknown/unrecognized values: use a Uri if one is present, else revert to
- // unspecified.
- default ->
- soundUri != null ? SOUND_SOURCE_URI : SOUND_SOURCE_UNSPECIFIED;
- };
- mVibrationSource = switch (vibrationSource) {
- // Enforce vibration sources that require a sound Uri.
- case VIBRATION_SOURCE_AUDIO_CHANNEL, VIBRATION_SOURCE_HAPTIC_GENERATOR ->
- soundUri != null ? vibrationSource : VIBRATION_SOURCE_UNSPECIFIED;
- // Supported explicit values that don't rely on any Uri.
- case VIBRATION_SOURCE_OFF, VIBRATION_SOURCE_UNSPECIFIED,
- VIBRATION_SOURCE_SYSTEM_DEFAULT, VIBRATION_SOURCE_APPLICATION_DEFAULT ->
- vibrationSource;
- // Uri and unknown/unrecognized values: use a Uri if one is present, else revert to
- // unspecified.
- default ->
- vibrationUri != null ? VIBRATION_SOURCE_URI : VIBRATION_SOURCE_UNSPECIFIED;
- };
- // Clear Uri values if they're un-used by the source.
- mSoundUri = mSoundSource == SOUND_SOURCE_URI ? soundUri : null;
- mVibrationUri = mVibrationSource == VIBRATION_SOURCE_URI ? vibrationUri : null;
- }
-
- /**
- * Returns the stored sound behavior.
- */
- @SoundSource
- public int getSoundSource() {
- return mSoundSource;
- }
-
- /**
- * Returns the sound Uri for this selection. This is guaranteed to be non-null if
- * {@link #getSoundSource} returns {@link #SOUND_SOURCE_URI}.
- */
- @Nullable
- public Uri getSoundUri() {
- return mSoundUri;
- }
-
- /**
- * Returns the selected vibration behavior.
- */
- @VibrationSource
- public int getVibrationSource() {
- return mVibrationSource;
- }
-
- /**
- * Returns the vibration Uri for this selection. This is guaranteed to be non-null if
- * {@link #getVibrationSource} returns {@link #SOUND_SOURCE_URI}.
- */
- @Nullable
- public Uri getVibrationUri() {
- return mVibrationUri;
- }
-
- /**
- * Converts the ringtone selection into a Uri-form, suitable for storing as a user preference
- * or returning as a result.
- */
- @NonNull
- public Uri toUri() {
- Uri.Builder builder = new Uri.Builder()
- .scheme(ContentResolver.SCHEME_CONTENT)
- .authority(MediaStore.AUTHORITY)
- .path(MEDIA_URI_RINGTONE_PATH);
- if (mSoundUri != null) {
- builder.appendQueryParameter(URI_PARAM_SOUND_URI, mSoundUri.toString());
- }
- // Only off is explicit for sound sources
- String soundSourceStr = soundSourceToString(mSoundSource);
- if (soundSourceStr != null) {
- builder.appendQueryParameter(URI_PARAM_SOUND_SOURCE, soundSourceStr);
- }
- if (mVibrationUri != null) {
- builder.appendQueryParameter(URI_PARAM_VIBRATION_URI, mVibrationUri.toString());
- }
- String vibrationSourceStr = vibrationSourceToString(mVibrationSource);
- if (vibrationSourceStr != null) {
- builder.appendQueryParameter(URI_PARAM_VIBRATION_SOURCE, vibrationSourceStr);
- }
- return builder.build();
- }
-
- /**
- * Returns true if the Uri is an encoded {@link RingtoneSelection}. This method doesn't
- * validate the parameters of the selection.
- *
- * @see #fromUri
- * @see #toUri
- */
- public static boolean isRingtoneSelectionUri(@Nullable Uri uri) {
- if (uri == null) {
- return false;
- }
- // Any URI content://media/ringtone
- return ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())
- && MediaStore.AUTHORITY.equals(
- ContentProvider.getAuthorityWithoutUserId(uri.getAuthority()))
- && MEDIA_URI_RINGTONE_PATH.equals(uri.getPath());
- }
-
- /**
- * Strip the specified userId from internal Uris. Non-stripped userIds will typically be
- * for work profiles referencing system ringtones from the host user.
- *
- * This is only for use in RingtoneManager.
- * @hide
- */
- @VisibleForTesting
- public RingtoneSelection getWithoutUserId(int userIdToStrip) {
- if (mSoundSource != SOUND_SOURCE_URI && mVibrationSource != VIBRATION_SOURCE_URI) {
- return this;
- }
-
- // Ok if uri is null. We only replace explicit references to the specified (current) userId.
- int soundUserId = ContentProvider.getUserIdFromUri(mSoundUri, UserHandle.USER_NULL);
- int vibrationUserId = ContentProvider.getUserIdFromUri(mVibrationUri, UserHandle.USER_NULL);
- boolean needToChangeSound =
- soundUserId != UserHandle.USER_NULL && soundUserId == userIdToStrip;
- boolean needToChangeVibration =
- vibrationUserId != UserHandle.USER_NULL && vibrationUserId == userIdToStrip;
-
- // Anything to do?
- if (!needToChangeSound && !needToChangeVibration) {
- return this;
- }
-
- RingtoneSelection.Builder updated = new Builder(this);
- // The relevant uris can't be null, because they contain userIdToStrip.
- if (needToChangeSound) {
- // mSoundUri is not null, so the result of getUriWithoutUserId won't be null.
- updated.setSoundSource(ContentProvider.getUriWithoutUserId(mSoundUri));
- }
- if (needToChangeVibration) {
- updated.setVibrationSource(ContentProvider.getUriWithoutUserId(mVibrationUri));
- }
- return updated.build();
- }
-
- @Override
- public String toString() {
- return toUri().toString();
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == this) {
- return true;
- }
- if (!(o instanceof RingtoneSelection other)) {
- return false;
- }
- return this.mSoundSource == other.mSoundSource
- && this.mVibrationSource == other.mVibrationSource
- && Objects.equals(this.mSoundUri, other.mSoundUri)
- && Objects.equals(this.mVibrationUri, other.mVibrationUri);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mSoundSource, mVibrationSource, mSoundUri, mVibrationUri);
- }
-
- /**
- * Converts a Uri into a RingtoneSelection.
- *
- * <p>Null values, Uris that {@link #isRingtoneSelectionUri(Uri)} returns false (except for
- * old-style symbolic default Uris {@link RingtoneManager#getDefaultUri}) will be treated
- * according to the behaviour specified by the {@code unrecognizedValueBehavior} parameter.
- *
- * <p>Symbolic default Uris (where {@link RingtoneManager#getDefaultType(Uri)} returns -1,
- * will map sources to {@link #SOUND_SOURCE_SYSTEM_DEFAULT} and
- * {@link #VIBRATION_SOURCE_SYSTEM_DEFAULT}.
- *
- * @param uri The Uri to convert, potentially null.
- * @param unrecognizedValueBehavior indicates how to treat values for which
- * {@link #isRingtoneSelectionUri(Uri)} returns false (including null).
- * @return the RingtoneSelection represented by the given uri.
- */
- @NonNull
- public static RingtoneSelection fromUri(@Nullable Uri uri,
- @FromUriBehavior int unrecognizedValueBehavior) {
- if (isRingtoneSelectionUri(uri)) {
- return parseRingtoneSelectionUri(uri);
- }
- // Old symbolic default URIs map to the sources suggested by the unrecognized behavior.
- // It doesn't always map to both sources because the app may have its own default behavior
- // to apply, so non-primary sources are left as unspecified, which will revert to the
- // system default in the absence of an app default.
- boolean isDefaultUri = RingtoneManager.getDefaultType(uri) > 0;
- RingtoneSelection.Builder builder = new RingtoneSelection.Builder();
- switch (unrecognizedValueBehavior) {
- case FROM_URI_RINGTONE_SELECTION_ONLY:
- if (isDefaultUri) {
- builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT);
- builder.setVibrationSource(VIBRATION_SOURCE_SYSTEM_DEFAULT);
- }
- // Always return unspecified (defaults) for unrecognized ringtone selection Uris.
- return builder.build();
- case FROM_URI_RINGTONE_SELECTION_OR_SOUND:
- if (uri == null) {
- return builder.setSoundSource(SOUND_SOURCE_OFF).build();
- } else if (isDefaultUri) {
- return builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT).build();
- } else {
- return builder.setSoundSource(uri).build();
- }
- case FROM_URI_RINGTONE_SELECTION_OR_VIBRATION:
- if (uri == null) {
- return builder.setVibrationSource(VIBRATION_SOURCE_OFF).build();
- } else if (isDefaultUri) {
- return builder.setVibrationSource(VIBRATION_SOURCE_SYSTEM_DEFAULT).build();
- } else {
- // Unlike sound, there's no legacy settings alias uri for the default.
- return builder.setVibrationSource(uri).build();
- }
- default:
- throw new IllegalArgumentException("Unknown behavior parameter: "
- + unrecognizedValueBehavior);
- }
- }
-
- /**
- * Parses the Uri, which has already been checked for {@link #isRingtoneSelectionUri(Uri)}.
- * As a special case to be more compatible, if the RingtoneSelection has a userId specified in
- * the authority, then this is pushed down into the sound and vibration Uri, as the selection
- * itself is agnostic.
- */
- @NonNull
- private static RingtoneSelection parseRingtoneSelectionUri(@NonNull Uri uri) {
- RingtoneSelection.Builder builder = new RingtoneSelection.Builder();
- int soundSource = stringToSoundSource(uri.getQueryParameter(URI_PARAM_SOUND_SOURCE));
- int vibrationSource = stringToVibrationSource(
- uri.getQueryParameter(URI_PARAM_VIBRATION_SOURCE));
- Uri soundUri = getParamAsUri(uri, URI_PARAM_SOUND_URI);
- Uri vibrationUri = getParamAsUri(uri, URI_PARAM_VIBRATION_URI);
-
- // Add userId if necessary. This won't override an existing one in the sound/vib Uris.
- int userIdFromAuthority = ContentProvider.getUserIdFromAuthority(
- uri.getAuthority(), UserHandle.USER_NULL);
- if (userIdFromAuthority != UserHandle.USER_NULL) {
- // Won't override existing user id.
- if (soundUri != null) {
- soundUri = ContentProvider.maybeAddUserId(soundUri, userIdFromAuthority);
- }
- if (vibrationUri != null) {
- vibrationUri = ContentProvider.maybeAddUserId(vibrationUri, userIdFromAuthority);
- }
- }
-
- // Set sound uri if present, but map system default Uris to the system default source.
- if (soundUri != null) {
- if (RingtoneManager.getDefaultType(uri) >= 0) {
- builder.setSoundSource(SOUND_SOURCE_SYSTEM_DEFAULT);
- } else {
- builder.setSoundSource(soundUri);
- }
- }
- if (vibrationUri != null) {
- builder.setVibrationSource(vibrationUri);
- }
-
- // Don't set the source if there's a URI and the source is default, because that will
- // override the Uri source set above. In effect, we prioritise "explicit" sources over
- // an implicit Uri source - except for "default", which isn't really explicit.
- if (soundSource != SOUND_SOURCE_UNSPECIFIED || soundUri == null) {
- builder.setSoundSource(soundSource);
- }
- if (vibrationSource != VIBRATION_SOURCE_UNSPECIFIED || vibrationUri == null) {
- builder.setVibrationSource(vibrationSource);
- }
- return builder.build();
- }
-
- @Nullable
- private static Uri getParamAsUri(@NonNull Uri uri, String param) {
- // This returns the uri-decoded value, no need to further decode.
- String value = uri.getQueryParameter(param);
- if (value == null) {
- return null;
- }
- return Uri.parse(value);
- }
-
- /**
- * Converts the {@link SoundSource} to the uri query param value for it, or null
- * if the sound source is default, unknown, or implicit (uri).
- */
- @Nullable
- private static String soundSourceToString(@SoundSource int soundSource) {
- return switch (soundSource) {
- case SOUND_SOURCE_OFF -> SOURCE_OFF_STRING;
- case SOUND_SOURCE_SYSTEM_DEFAULT -> SOURCE_SYSTEM_DEFAULT_STRING;
- default -> null;
- };
- }
-
- /**
- * Returns the sound source int corresponding to the query string value. Returns
- * {@link #SOUND_SOURCE_UNKNOWN} if the value isn't recognised, and
- * {@link #SOUND_SOURCE_UNSPECIFIED} if the value is {@code null} (not in the Uri).
- */
- @SoundSource
- private static int stringToSoundSource(@Nullable String soundSource) {
- if (soundSource == null) {
- return SOUND_SOURCE_UNSPECIFIED;
- }
- return switch (soundSource) {
- case SOURCE_OFF_STRING -> SOUND_SOURCE_OFF;
- case SOURCE_SYSTEM_DEFAULT_STRING -> SOUND_SOURCE_SYSTEM_DEFAULT;
- default -> SOUND_SOURCE_UNKNOWN;
- };
- }
-
- /**
- * Converts the {@code vibrationSource} to the uri query param value for it, or null
- * if the vibration source is default, unknown, or implicit (uri).
- */
- @Nullable
- private static String vibrationSourceToString(@VibrationSource int vibrationSource) {
- return switch (vibrationSource) {
- case VIBRATION_SOURCE_OFF -> SOURCE_OFF_STRING;
- case VIBRATION_SOURCE_AUDIO_CHANNEL -> VIBRATION_SOURCE_AUDIO_CHANNEL_STRING;
- case VIBRATION_SOURCE_HAPTIC_GENERATOR -> VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING;
- case VIBRATION_SOURCE_APPLICATION_DEFAULT ->
- VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING;
- case VIBRATION_SOURCE_SYSTEM_DEFAULT -> SOURCE_SYSTEM_DEFAULT_STRING;
- default -> null;
- };
- }
-
- @VibrationSource
- private static int stringToVibrationSource(@Nullable String vibrationSource) {
- if (vibrationSource == null) {
- return VIBRATION_SOURCE_UNSPECIFIED;
- }
- return switch (vibrationSource) {
- case SOURCE_OFF_STRING -> VIBRATION_SOURCE_OFF;
- case SOURCE_SYSTEM_DEFAULT_STRING -> VIBRATION_SOURCE_SYSTEM_DEFAULT;
- case VIBRATION_SOURCE_AUDIO_CHANNEL_STRING -> VIBRATION_SOURCE_AUDIO_CHANNEL;
- case VIBRATION_SOURCE_HAPTIC_GENERATOR_STRING -> VIBRATION_SOURCE_HAPTIC_GENERATOR;
- case VIBRATION_SOURCE_APPLICATION_DEFAULT_STRING ->
- VIBRATION_SOURCE_APPLICATION_DEFAULT;
- default -> VIBRATION_SOURCE_UNKNOWN;
- };
- }
-
- /**
- * Builder for {@link RingtoneSelection}. In general, this builder will be used by interfaces
- * allowing the user to configure their selection. Once a selection is stored as a Uri, then
- * the RingtoneSelection can be loaded directly using {@link RingtoneSelection#fromUri}.
- */
- @FlaggedApi(Flags.FLAG_HAPTICS_CUSTOMIZATION_ENABLED)
- public static final class Builder {
- private Uri mSoundUri;
- private Uri mVibrationUri;
- @SoundSource private int mSoundSource = SOUND_SOURCE_UNSPECIFIED;
- @VibrationSource private int mVibrationSource = VIBRATION_SOURCE_UNSPECIFIED;
-
- /**
- * Creates a new {@link RingtoneSelection} builder. A default ringtone selection has its
- * sound and vibration source unspecified, which means they would fall back to app/system
- * defaults.
- */
- public Builder() {}
-
- /**
- * Creates a builder initialized with the given ringtone selection.
- */
- public Builder(@NonNull RingtoneSelection selection) {
- requireNonNull(selection);
- mSoundSource = selection.getSoundSource();
- mSoundUri = selection.getSoundUri();
- mVibrationSource = selection.getVibrationSource();
- mVibrationUri = selection.getVibrationUri();
- }
-
- /**
- * Sets the desired sound source.
- *
- * <p>Values other than {@link #SOUND_SOURCE_URI} will clear any previous sound Uri.
- * For {@link #SOUND_SOURCE_URI}, the {@link #setSoundSource(Uri)} method should be
- * used instead, as setting it here will have no effect unless the Uri is also set.
- */
- @NonNull
- public Builder setSoundSource(@SoundSource int soundSource) {
- mSoundSource = soundSource;
- if (soundSource != SOUND_SOURCE_URI && soundSource != SOUND_SOURCE_UNKNOWN) {
- // Note that this means the configuration of "silent sound, but use haptic
- // generator" is currently not supported. Future support could be added by either
- // using the vibration uri in that case, or by having a special
- // "setSoundUriForVibrationOnly(Uri)" method that sets sound source to off but
- // also retains the Uri.
- mSoundUri = null;
- }
- return this;
- }
-
- /**
- * Sets the sound source to {@link #SOUND_SOURCE_URI}, and the sound Uri to the
- * specified {@link Uri}.
- */
- @NonNull
- public Builder setSoundSource(@NonNull Uri soundUri) {
- // getCanonicalUri shouldn't return null. If it somehow did, then the
- // RingtoneSelection constructor will revert this to unspecified.
- mSoundUri = requireNonNull(soundUri).getCanonicalUri();
- mSoundSource = SOUND_SOURCE_URI;
- return this;
- }
-
- /**
- * Sets the vibration source to the specified value.
- *
- * <p>Values other than {@link #VIBRATION_SOURCE_URI} will clear any previous vibration Uri.
- * For {@link #VIBRATION_SOURCE_URI}, the {@link #setVibrationSource(Uri)} method should be
- * used instead, as setting it here will have no effect unless the Uri is also set.
- */
- @NonNull
- public Builder setVibrationSource(@VibrationSource int vibrationSource) {
- mVibrationSource = vibrationSource;
- if (vibrationSource != VIBRATION_SOURCE_URI
- && vibrationSource != VIBRATION_SOURCE_UNKNOWN) {
- mVibrationUri = null;
- }
- return this;
- }
-
- /**
- * Sets the vibration source to {@link #VIBRATION_SOURCE_URI}, and the vibration Uri to the
- * specified {@link Uri}.
- */
- @NonNull
- public Builder setVibrationSource(@NonNull Uri vibrationUri) {
- // getCanonicalUri shouldn't return null. If it somehow did, then the
- // RingtoneSelection constructor will revert this to unspecified.
- mVibrationUri = requireNonNull(vibrationUri).getCanonicalUri();
- mVibrationSource = VIBRATION_SOURCE_URI;
- return this;
- }
-
- /**
- * Returns the ringtone Uri that was configured.
- */
- @NonNull
- public RingtoneSelection build() {
- return new RingtoneSelection(mSoundUri, mSoundSource, mVibrationUri, mVibrationSource);
- }
- }
-}
diff --git a/media/tests/projection/Android.bp b/media/tests/projection/Android.bp
index c9a8864..fd5f195 100644
--- a/media/tests/projection/Android.bp
+++ b/media/tests/projection/Android.bp
@@ -3,6 +3,7 @@
//########################################################################
package {
+ default_team: "trendy_team_lse_desktop_os_experience",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_license"
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
index 82b47a9..527701c 100644
--- a/packages/CredentialManager/res/values/strings.xml
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -63,9 +63,9 @@
<!-- This appears as the description body of the modal bottom sheet which provides all available providers for users to choose. [CHAR LIMIT=200] -->
<string name="choose_provider_body">Select a password manager to save your info and sign in faster next time</string>
<!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is passkey. [CHAR LIMIT=200] -->
- <string name="choose_create_option_passkey_title">Create passkey for <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
+ <string name="choose_create_option_passkey_title">Create passkey to sign in to <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
<!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is password. [CHAR LIMIT=200] -->
- <string name="choose_create_option_password_title">Save password for <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
+ <string name="choose_create_option_password_title">Save password to sign in to <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
<!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is others. [CHAR LIMIT=200] -->
<string name="choose_create_option_sign_in_title">Save sign-in info for <xliff:g id="appName" example="Tribank">%1$s</xliff:g>?</string>
<!-- Types which are inserted as a placeholder as credentialTypes for other strings. [CHAR LIMIT=200] -->
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
index 1326023..c118f88 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/autofill/CredentialAutofillService.kt
@@ -237,11 +237,8 @@
if (providerList.isEmpty()) {
return false
}
- var totalEntryCount = 0
- providerList.forEach { provider ->
- totalEntryCount += provider.credentialEntryList.size
- }
val providerDisplayInfo: ProviderDisplayInfo = toProviderDisplayInfo(providerList)
+ var totalEntryCount = providerDisplayInfo.sortedUserNameToCredentialEntryList.size
val inlineSuggestionsRequest = filLRequest.inlineSuggestionsRequest
val inlineMaxSuggestedCount = inlineSuggestionsRequest?.maxSuggestionCount ?: 0
val inlinePresentationSpecs = inlineSuggestionsRequest?.inlinePresentationSpecs
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index 7277550..ccc4660 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -554,15 +554,11 @@
if (credentialEntryInfo.icon == null) painterResource(R.drawable.ic_other_sign_in_24)
else null,
entryHeadlineText = username,
- entrySecondLineText = if (
- credentialEntryInfo.credentialType == CredentialType.PASSWORD) {
- "••••••••••••"
- } else {
- val itemsToDisplay = listOf(
+ entrySecondLineText = listOf(
displayName,
credentialEntryInfo.credentialTypeDisplayName,
credentialEntryInfo.providerDisplayName
- ).filterNot(TextUtils::isEmpty)
+ ).filterNot(TextUtils::isEmpty).let { itemsToDisplay ->
if (itemsToDisplay.isEmpty()) null
else itemsToDisplay.joinToString(
separator = stringResource(R.string.get_dialog_sign_in_type_username_separator)
diff --git a/packages/PackageInstaller/res/layout/install_content_view.xml b/packages/PackageInstaller/res/layout/install_content_view.xml
index 2ecd2d5..524a88a 100644
--- a/packages/PackageInstaller/res/layout/install_content_view.xml
+++ b/packages/PackageInstaller/res/layout/install_content_view.xml
@@ -24,114 +24,116 @@
android:paddingLeft="?android:attr/dialogPreferredPadding"
android:paddingRight="?android:attr/dialogPreferredPadding">
- <LinearLayout
- android:id="@+id/staging"
+ <LinearLayout
+ android:id="@+id/staging"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="invisible">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/message_staging" />
+
+ <ProgressBar
+ android:id="@+id/progress_indeterminate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="invisible">
+ android:paddingTop="8dp"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:indeterminate="true" />
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/message_staging" />
+ </LinearLayout>
- <ProgressBar
- android:id="@+id/progress_indeterminate"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- style="?android:attr/progressBarStyleHorizontal"
- android:indeterminate="true" />
+ <LinearLayout
+ android:id="@+id/installing"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="invisible">
- </LinearLayout>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/installing" />
- <LinearLayout
- android:id="@+id/installing"
+ <ProgressBar
+ android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="invisible">
+ android:paddingTop="8dp"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:indeterminate="true" />
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/installing" />
+ </LinearLayout>
- <ProgressBar
- android:id="@+id/progress"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="8dp"
- style="?android:attr/progressBarStyleHorizontal"
- android:indeterminate="true" />
+ <TextView
+ android:id="@+id/install_confirm_question"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_confirm_question"
+ android:visibility="invisible"
+ android:scrollbars="vertical" />
- </LinearLayout>
+ <TextView
+ android:id="@+id/install_confirm_question_update"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_confirm_question_update"
+ android:visibility="invisible"
+ android:scrollbars="vertical" />
- <TextView
- android:id="@+id/install_confirm_question"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_confirm_question"
- android:visibility="invisible" />
+ <TextView
+ android:id="@+id/install_success"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_done"
+ android:visibility="invisible" />
- <TextView
- android:id="@+id/install_confirm_question_update"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_confirm_question_update"
- android:visibility="invisible" />
+ <TextView
+ android:id="@+id/install_failed"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_failed"
+ android:visibility="invisible" />
- <TextView
- android:id="@+id/install_success"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_done"
- android:visibility="invisible" />
+ <TextView
+ android:id="@+id/install_failed_blocked"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_failed_blocked"
+ android:visibility="invisible" />
- <TextView
- android:id="@+id/install_failed"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_failed"
- android:visibility="invisible" />
+ <TextView
+ android:id="@+id/install_failed_conflict"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_failed_conflict"
+ android:visibility="invisible" />
- <TextView
- android:id="@+id/install_failed_blocked"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_failed_blocked"
- android:visibility="invisible" />
+ <TextView
+ android:id="@+id/install_failed_incompatible"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_failed_incompatible"
+ android:visibility="invisible" />
- <TextView
- android:id="@+id/install_failed_conflict"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_failed_conflict"
- android:visibility="invisible" />
+ <TextView
+ android:id="@+id/install_failed_invalid_apk"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead"
+ android:text="@string/install_failed_invalid_apk"
+ android:visibility="invisible" />
- <TextView
- android:id="@+id/install_failed_incompatible"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_failed_incompatible"
- android:visibility="invisible" />
-
- <TextView
- android:id="@+id/install_failed_invalid_apk"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead"
- android:text="@string/install_failed_invalid_apk"
- android:visibility="invisible" />
-
-</FrameLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/PackageInstaller/res/layout/uninstall_content_view.xml b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
index 5666c0e..434e333 100644
--- a/packages/PackageInstaller/res/layout/uninstall_content_view.xml
+++ b/packages/PackageInstaller/res/layout/uninstall_content_view.xml
@@ -18,31 +18,36 @@
<!-- Check box that is displayed in the activity resolver UI for the user
to make their selection the preferred activity. -->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:theme="?android:attr/alertDialogTheme"
- android:orientation="vertical"
- android:paddingTop="8dp"
- android:paddingStart="?android:attr/dialogPreferredPadding"
- android:paddingEnd="?android:attr/dialogPreferredPadding"
- android:clipToPadding="false">
+ android:layout_height="wrap_content">
- <TextView
- android:id="@+id/message"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@android:style/TextAppearance.Material.Subhead" />
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="?android:attr/alertDialogTheme"
+ android:orientation="vertical"
+ android:paddingTop="8dp"
+ android:paddingStart="?android:attr/dialogPreferredPadding"
+ android:paddingEnd="?android:attr/dialogPreferredPadding"
+ android:clipToPadding="false">
- <CheckBox
- android:id="@+id/keepData"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:layout_marginStart="-8dp"
- android:paddingLeft="8sp"
- android:visibility="gone"
- style="@android:style/TextAppearance.Material.Subhead" />
+ <TextView
+ android:id="@+id/message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@android:style/TextAppearance.Material.Subhead" />
-</LinearLayout>
\ No newline at end of file
+ <CheckBox
+ android:id="@+id/keepData"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginStart="-8dp"
+ android:paddingLeft="8sp"
+ android:visibility="gone"
+ style="@android:style/TextAppearance.Material.Subhead" />
+
+ </LinearLayout>
+</ScrollView>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index cf6aab6..e95a8e6 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -51,6 +51,7 @@
import android.text.Html;
import android.text.Spanned;
import android.text.TextUtils;
+import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.Button;
@@ -174,6 +175,7 @@
}
viewToEnable.setVisibility(View.VISIBLE);
+ viewToEnable.setMovementMethod(new ScrollingMovementMethod());
mEnableOk = true;
mOk.setEnabled(true);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
index dbe32cc..0a4aa48 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/ui/fragments/InstallConfirmationFragment.java
@@ -22,6 +22,7 @@
import android.content.DialogInterface;
import android.os.Bundle;
import android.text.Html;
+import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -94,6 +95,7 @@
viewToEnable = dialogView.requireViewById(R.id.install_confirm_question);
}
viewToEnable.setVisibility(View.VISIBLE);
+ viewToEnable.setMovementMethod(new ScrollingMovementMethod());
return mDialog;
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
index bb7e857..3acf075 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
@@ -18,7 +18,6 @@
import androidx.compose.foundation.clickable
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import com.android.settingslib.spa.framework.common.EntryMacro
@@ -107,14 +106,9 @@
) {
val onClickWithLog = wrapOnClickWithLog(model.onClick)
val enabled = model.enabled()
- val modifier = remember(enabled) {
- if (onClickWithLog != null) {
- Modifier.clickable(
- enabled = enabled,
- onClick = onClickWithLog
- )
- } else Modifier
- }
+ val modifier = if (onClickWithLog != null) {
+ Modifier.clickable(enabled = enabled, onClick = onClickWithLog)
+ } else Modifier
EntryHighlight {
BasePreference(
title = model.title,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 452dc03..d23cd0c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -35,11 +35,13 @@
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import com.android.systemui.media.controls.ui.composable.MediaCarousel
import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
@@ -63,6 +65,7 @@
private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
private val bottomAreaSection: BottomAreaSection,
private val settingsMenuSection: SettingsMenuSection,
+ private val mediaCarouselSection: MediaCarouselSection,
private val clockInteractor: KeyguardClockInteractor,
) : ComposableLockscreenSceneBlueprint {
@@ -112,10 +115,16 @@
if (viewModel.isLargeClockVisible) {
Spacer(modifier = Modifier.weight(weight = 1f))
- with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+ with(clockSection) {
+ LargeClock(
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
}
- if (viewModel.areNotificationsVisible) {
+ with(mediaCarouselSection) { MediaCarousel() }
+
+ if (viewModel.areNotificationsVisible(resources = resources)) {
with(notificationSection) {
Notifications(
modifier = Modifier.fillMaxWidth().weight(weight = 1f)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index 71c60c7..c422c4b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -35,6 +35,7 @@
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
@@ -63,6 +64,7 @@
private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
private val bottomAreaSection: BottomAreaSection,
private val settingsMenuSection: SettingsMenuSection,
+ private val mediaCarouselSection: MediaCarouselSection,
private val clockInteractor: KeyguardClockInteractor,
) : ComposableLockscreenSceneBlueprint {
@@ -115,7 +117,9 @@
with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
}
- if (viewModel.areNotificationsVisible) {
+ with(mediaCarouselSection) { MediaCarousel() }
+
+ if (viewModel.areNotificationsVisible(resources = resources)) {
with(notificationSection) {
Notifications(
modifier = Modifier.fillMaxWidth().weight(weight = 1f)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
index af836b6..d218425 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
@@ -41,6 +41,7 @@
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
@@ -70,6 +71,7 @@
private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
private val bottomAreaSection: BottomAreaSection,
private val settingsMenuSection: SettingsMenuSection,
+ private val mediaCarouselSection: MediaCarouselSection,
private val clockInteractor: KeyguardClockInteractor,
private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
) : ComposableLockscreenSceneBlueprint {
@@ -100,6 +102,14 @@
modifier = Modifier.fillMaxHeight().weight(weight = 1f),
horizontalAlignment = Alignment.CenterHorizontally,
) {
+ with(clockSection) {
+ SmallClock(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmallClockTopChanged,
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
+
with(smartSpaceSection) {
SmartSpace(
burnInParams = burnIn.parameters,
@@ -121,9 +131,13 @@
)
}
- Spacer(modifier = Modifier.weight(weight = 1f))
- with(clockSection) { LargeClock() }
- Spacer(modifier = Modifier.weight(weight = 1f))
+ if (viewModel.isLargeClockVisible) {
+ Spacer(modifier = Modifier.weight(weight = 1f))
+ with(clockSection) { LargeClock() }
+ Spacer(modifier = Modifier.weight(weight = 1f))
+ }
+
+ with(mediaCarouselSection) { MediaCarousel() }
}
with(notificationSection) {
val splitShadeTopMargin: Dp =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
index e2e7a95..f86623f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
@@ -41,12 +41,14 @@
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.SmartSpaceSection
import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
import com.android.systemui.keyguard.ui.composable.section.WeatherClockSection
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
+import com.android.systemui.media.controls.ui.composable.MediaCarousel
import com.android.systemui.res.R
import com.android.systemui.shade.LargeScreenHeaderHelper
import dagger.Binds
@@ -68,6 +70,7 @@
private val bottomAreaSection: BottomAreaSection,
private val settingsMenuSection: SettingsMenuSection,
private val clockInteractor: KeyguardClockInteractor,
+ private val mediaCarouselSection: MediaCarouselSection,
) : ComposableLockscreenSceneBlueprint {
override val id: String = WEATHER_CLOCK_BLUEPRINT_ID
@@ -107,7 +110,9 @@
)
}
- if (viewModel.areNotificationsVisible) {
+ with(mediaCarouselSection) { MediaCarousel() }
+
+ if (viewModel.areNotificationsVisible(resources = resources)) {
with(notificationSection) {
Notifications(
modifier = Modifier.fillMaxWidth().weight(weight = 1f)
@@ -228,6 +233,7 @@
private val clockInteractor: KeyguardClockInteractor,
private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
private val weatherClockSection: WeatherClockSection,
+ private val mediaCarouselSection: MediaCarouselSection,
) : ComposableLockscreenSceneBlueprint {
override val id: String = SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
@@ -276,6 +282,8 @@
),
)
}
+
+ with(mediaCarouselSection) { MediaCarousel() }
}
with(notificationSection) {
val splitShadeTopMargin: Dp =
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
index 335c915..152cc67 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
@@ -39,7 +39,6 @@
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import javax.inject.Inject
/** Provides small clock and large clock composables for the default clock face. */
@@ -49,7 +48,6 @@
private val viewModel: KeyguardClockViewModel,
private val clockInteractor: KeyguardClockInteractor,
private val aodBurnInViewModel: AodBurnInViewModel,
- private val lockscreenSmartspaceController: LockscreenSmartspaceController,
) {
@Composable
@@ -62,15 +60,11 @@
val currentClock by viewModel.currentClock.collectAsState()
viewModel.clock = currentClock
- if (clockSize != KeyguardClockSwitch.SMALL) {
+ if (clockSize != KeyguardClockSwitch.SMALL || currentClock?.smallClock?.view == null) {
onTopChanged(null)
return
}
- if (currentClock?.smallClock?.view == null) {
- return
- }
-
val view = LocalView.current
DisposableEffect(view) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
new file mode 100644
index 0000000..dae120c
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/MediaCarouselSection.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.composable.section
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.res.dimensionResource
+import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.keyguard.ui.viewmodel.MediaCarouselViewModel
+import com.android.systemui.media.controls.ui.composable.MediaCarousel
+import com.android.systemui.media.controls.ui.controller.MediaCarouselController
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.media.dagger.MediaModule
+import com.android.systemui.res.R
+import com.android.systemui.util.animation.MeasurementInput
+import javax.inject.Inject
+import javax.inject.Named
+
+class MediaCarouselSection
+@Inject
+constructor(
+ private val mediaCarouselController: MediaCarouselController,
+ @param:Named(MediaModule.KEYGUARD) private val mediaHost: MediaHost,
+ private val mediaCarouselViewModel: MediaCarouselViewModel,
+) {
+
+ @Composable
+ fun SceneScope.MediaCarousel(modifier: Modifier = Modifier) {
+ if (!mediaCarouselViewModel.isMediaVisible) {
+ return
+ }
+
+ if (mediaCarouselController.mediaFrame == null) {
+ return
+ }
+
+ val mediaHeight = dimensionResource(R.dimen.qs_media_session_height_expanded)
+ // TODO(b/312714128): MediaPlayer background size is not as expected.
+ MediaCarousel(
+ modifier =
+ modifier.height(mediaHeight).fillMaxWidth().onSizeChanged { size ->
+ // Notify controller to size the carousel for the
+ // current space
+ mediaHost.measurementInput = MeasurementInput(size.width, size.height)
+ mediaCarouselController.setSceneContainerSize(size.width, size.height)
+ },
+ mediaHost = mediaHost,
+ layoutWidth = 0, // Layout width is not used.
+ layoutHeight = with(LocalDensity.current) { mediaHeight.toPx() }.toInt(),
+ carouselController = mediaCarouselController,
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
index 61b2d4e..d3e4553 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/media/controls/ui/composable/MediaCarousel.kt
@@ -16,9 +16,12 @@
package com.android.systemui.media.controls.ui.composable
+import android.view.ViewGroup
+import android.widget.FrameLayout
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.contains
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.media.controls.ui.controller.MediaCarouselController
@@ -45,6 +48,20 @@
AndroidView(
modifier = modifier.element(MediaCarousel.Elements.Content),
- factory = { _ -> carouselController.mediaFrame },
+ factory = { context ->
+ FrameLayout(context).apply {
+ val mediaFrame = carouselController.mediaFrame
+ (mediaFrame.parent as? ViewGroup)?.removeView(mediaFrame)
+ addView(mediaFrame)
+ }
+ },
+ update = {
+ if (it.contains(carouselController.mediaFrame)) {
+ return@AndroidView
+ }
+ val mediaFrame = carouselController.mediaFrame
+ (mediaFrame.parent as? ViewGroup)?.removeView(mediaFrame)
+ it.addView(mediaFrame)
+ },
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index b611e0a..36919d0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -1052,32 +1052,6 @@
biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(true)
faceAuthenticateIsCalled()
}
- @Test
- fun authFailedCallAfterAuthLockedOutErrorShouldBeIgnored() =
- testScope.runTest {
- initCollectors()
- allPreconditionsToRunFaceAuthAreTrue()
- runCurrent()
- assertThat(canFaceAuthRun()).isTrue()
-
- underTest.requestAuthenticate(FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED, false)
- runCurrent()
-
- faceAuthenticateIsCalled()
- authenticationCallback.value.onAuthenticationError(
- FACE_ERROR_LOCKOUT_PERMANENT,
- "Too many attempts, face not available"
- )
-
- val lockoutError = authStatus() as ErrorFaceAuthenticationStatus
- assertThat(lockedOut()).isTrue()
- assertThat(lockoutError.isLockoutError()).isTrue()
-
- authenticationCallback.value.onAuthenticationFailed()
- runCurrent()
-
- assertThat(authStatus()).isEqualTo(lockoutError)
- }
private suspend fun TestScope.testGatingCheckForFaceAuth(
gatingCheckModifier: suspend () -> Unit
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 3484025..cd4db2f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -79,34 +79,66 @@
}
@Test
- fun dozeAmountTransitionTest() = runTest {
- val dozeAmountSteps by collectValues(underTest.dozeAmountTransition)
+ fun dozeAmountTransitionTest_AodToFromLockscreen() =
+ testScope.runTest {
+ val dozeAmountSteps by collectValues(underTest.dozeAmountTransition)
- val steps = mutableListOf<TransitionStep>()
+ val steps = mutableListOf<TransitionStep>()
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
- steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0.8f, RUNNING))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
- steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.8f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
- steps.forEach {
- repository.sendTransitionStep(it)
- runCurrent()
+ steps.forEach {
+ repository.sendTransitionStep(it)
+ runCurrent()
+ }
+
+ assertThat(dozeAmountSteps.subList(0, 3))
+ .isEqualTo(
+ listOf(
+ steps[0].copy(value = 1f - steps[0].value),
+ steps[1].copy(value = 1f - steps[1].value),
+ steps[2].copy(value = 1f - steps[2].value),
+ )
+ )
+ assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7))
}
- assertThat(dozeAmountSteps.subList(0, 3))
- .isEqualTo(
- listOf(
- steps[0].copy(value = 1f - steps[0].value),
- steps[1].copy(value = 1f - steps[1].value),
- steps[2].copy(value = 1f - steps[2].value),
+ @Test
+ fun dozeAmountTransitionTest_AodToFromGone() =
+ testScope.runTest {
+ val dozeAmountSteps by collectValues(underTest.dozeAmountTransition)
+
+ val steps = mutableListOf<TransitionStep>()
+
+ steps.add(TransitionStep(AOD, GONE, 0f, STARTED))
+ steps.add(TransitionStep(AOD, GONE, 0.3f, RUNNING))
+ steps.add(TransitionStep(AOD, GONE, 1f, FINISHED))
+ steps.add(TransitionStep(GONE, AOD, 0f, STARTED))
+ steps.add(TransitionStep(GONE, AOD, 0.1f, RUNNING))
+ steps.add(TransitionStep(GONE, AOD, 0.3f, RUNNING))
+ steps.add(TransitionStep(GONE, AOD, 1f, FINISHED))
+
+ steps.forEach {
+ repository.sendTransitionStep(it)
+ runCurrent()
+ }
+
+ assertThat(dozeAmountSteps.subList(0, 3))
+ .isEqualTo(
+ listOf(
+ steps[0].copy(value = 1f - steps[0].value),
+ steps[1].copy(value = 1f - steps[1].value),
+ steps[2].copy(value = 1f - steps[2].value),
+ )
)
- )
- assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7))
- }
+ assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7))
+ }
@Test
fun finishedKeyguardStateTests() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToGoneTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/DozingToLockscreenTransitionViewModelTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
index d4dd2ac..ad1cef1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
+import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -46,6 +47,7 @@
fun setup() {
with(kosmos) {
fakeFeatureFlagsClassic.set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, true)
+ overrideResource(R.bool.config_use_split_notification_shade, false)
underTest = lockscreenContentViewModel
}
}
@@ -87,11 +89,21 @@
}
@Test
+ fun areNotificationsVisible_splitShadeTrue_true() =
+ with(kosmos) {
+ testScope.runTest {
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+
+ assertThat(underTest.areNotificationsVisible(context.resources)).isTrue()
+ }
+ }
+ @Test
fun areNotificationsVisible_withSmallClock_true() =
with(kosmos) {
testScope.runTest {
kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
- assertThat(underTest.areNotificationsVisible).isTrue()
+ assertThat(underTest.areNotificationsVisible(context.resources)).isTrue()
}
}
@@ -100,7 +112,7 @@
with(kosmos) {
testScope.runTest {
kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
- assertThat(underTest.areNotificationsVisible).isFalse()
+ assertThat(underTest.areNotificationsVisible(context.resources)).isFalse()
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
index e139466..bef9515 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
@@ -36,10 +36,10 @@
import com.android.systemui.testKosmos
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
-import kotlin.test.Test
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Test
import org.junit.runner.RunWith
@ExperimentalCoroutinesApi
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModelTest.kt
index 28f5eba..86b3f33 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModelTest.kt
@@ -102,6 +102,24 @@
values.forEach { assertThat(it).isEqualTo(0f) }
}
+
+ @Test
+ fun lockscreenAlphaFadesOutAndFinishesVisible() =
+ testScope.runTest {
+ val alpha by collectValues(underTest.lockscreenAlpha)
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.DOZING,
+ testScope,
+ )
+
+ assertThat(alpha[0]).isEqualTo(1f)
+ // Halfway through, it will have faded out
+ assertThat(alpha[1]).isEqualTo(0f)
+ // FINISHED alpha should be visible, to support pulsing
+ assertThat(alpha[2]).isEqualTo(1f)
+ }
+
@Test
fun deviceEntryBackgroundViewDisappear() =
testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index 7a564ac..43ab93a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -33,10 +33,10 @@
import com.android.systemui.testKosmos
import com.google.common.collect.Range
import com.google.common.truth.Truth
-import kotlin.test.Test
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Test
import org.junit.runner.RunWith
@ExperimentalCoroutinesApi
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToAodTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToLockscreenTransitionViewModelTest.kt
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index fff0a31..667f516 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -615,6 +615,7 @@
private fun TestScope.emulatePendingTransitionProgress(
expectedVisible: Boolean = true,
) {
+ val isVisible by collectLastValue(sceneContainerViewModel.isVisible)
assertWithMessage("The FakeSceneDataSource has to be paused for this to do anything.")
.that(fakeSceneDataSource.isPaused)
.isTrue()
@@ -651,7 +652,7 @@
runCurrent()
assertWithMessage("Visibility mismatch after scene transition from $from to $to!")
- .that(sceneContainerViewModel.isVisible.value)
+ .that(isVisible)
.isEqualTo(expectedVisible)
assertThat(sceneContainerViewModel.currentScene.value).isEqualTo(to)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index dd3eb68..db94c39 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
package com.android.systemui.scene.domain.interactor
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -31,6 +33,7 @@
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
@@ -275,4 +278,18 @@
underTest.setVisible(true, "reason")
assertThat(isVisible).isTrue()
}
+
+ @Test
+ fun isVisible_duringRemoteUserInteraction_forcedVisible() =
+ testScope.runTest {
+ underTest.setVisible(false, "reason")
+ val isVisible by collectLastValue(underTest.isVisible)
+ assertThat(isVisible).isFalse()
+ underTest.onRemoteUserInteractionStarted("reason")
+ assertThat(isVisible).isTrue()
+
+ underTest.onUserInteractionFinished()
+
+ assertThat(isVisible).isFalse()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
index ffbdafe..27ae8b6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
package com.android.systemui.scene.ui.viewmodel
+import android.view.MotionEvent
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -35,9 +34,9 @@
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -50,7 +49,7 @@
private val kosmos = testKosmos()
private val testScope by lazy { kosmos.testScope }
- private val interactor by lazy { kosmos.sceneInteractor }
+ private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val fakeSceneDataSource = kosmos.fakeSceneDataSource
private val sceneContainerConfig = kosmos.sceneContainerConfig
private val falsingManager = kosmos.fakeFalsingManager
@@ -62,7 +61,7 @@
kosmos.fakeSceneContainerFlags.enabled = true
underTest =
SceneContainerViewModel(
- sceneInteractor = interactor,
+ sceneInteractor = sceneInteractor,
falsingInteractor = kosmos.falsingInteractor,
powerInteractor = kosmos.powerInteractor,
)
@@ -74,10 +73,10 @@
val isVisible by collectLastValue(underTest.isVisible)
assertThat(isVisible).isTrue()
- interactor.setVisible(false, "reason")
+ sceneInteractor.setVisible(false, "reason")
assertThat(isVisible).isFalse()
- interactor.setVisible(true, "reason")
+ sceneInteractor.setVisible(true, "reason")
assertThat(isVisible).isTrue()
}
@@ -199,4 +198,20 @@
underTest.onMotionEvent(mock())
assertThat(kosmos.fakePowerRepository.userTouchRegistered).isTrue()
}
+
+ @Test
+ fun remoteUserInteraction_keepsContainerVisible() =
+ testScope.runTest {
+ sceneInteractor.setVisible(false, "reason")
+ val isVisible by collectLastValue(underTest.isVisible)
+ assertThat(isVisible).isFalse()
+ sceneInteractor.onRemoteUserInteractionStarted("reason")
+ assertThat(isVisible).isTrue()
+
+ underTest.onMotionEvent(
+ mock { whenever(actionMasked).thenReturn(MotionEvent.ACTION_UP) }
+ )
+
+ assertThat(isVisible).isFalse()
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 6390e82..db4d42f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -53,6 +53,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.settings.FakeGlobalSettings;
import com.android.systemui.util.time.FakeSystemClock;
@@ -142,6 +143,12 @@
}
}
+ @Override
+ public void SysuiSetup() throws Exception {
+ super.SysuiSetup();
+ mSetFlagsRule.disableFlags(NotificationThrottleHun.FLAG_NAME);
+ }
+
@Test
public void testShowNotification_addsEntry() {
final BaseHeadsUpManager alm = createHeadsUpManager();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
index ec23f76..c032d7c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
@@ -39,6 +39,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
+import com.android.systemui.statusbar.notification.shared.NotificationThrottleHun;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -136,6 +137,8 @@
@Before
public void setUp() {
+ mSetFlagsRule.disableFlags(NotificationThrottleHun.FLAG_NAME);
+
when(mShadeInteractor.isAnyExpanded()).thenReturn(StateFlowKt.MutableStateFlow(false));
final AccessibilityManagerWrapper accessibilityMgr =
mDependency.injectMockDependency(AccessibilityManagerWrapper.class);
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 05e07a7..169a4e0 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -413,7 +413,6 @@
listenForDozing(this)
if (migrateClocksToBlueprint()) {
listenForDozeAmountTransition(this)
- listenForAnyStateToAodTransition(this)
} else {
listenForDozeAmount(this)
}
@@ -522,19 +521,6 @@
}
}
- /**
- * When keyguard is displayed again after being gone, the clock must be reset to full dozing.
- */
- @VisibleForTesting
- internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job {
- return scope.launch {
- keyguardTransitionInteractor.transitionStepsToState(AOD)
- .filter { it.transitionState == TransitionState.STARTED }
- .filter { it.from != LOCKSCREEN }
- .collect { handleDoze(1f) }
- }
- }
-
@VisibleForTesting
internal fun listenForDozing(scope: CoroutineScope): Job {
return scope.launch {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
index 363dd01..f528ec8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java
@@ -18,11 +18,16 @@
import android.content.res.ColorStateList;
import android.content.res.Configuration;
+import android.hardware.biometrics.BiometricSourceType;
+import android.os.SystemClock;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
+import android.util.Log;
+import android.util.Pair;
import android.view.View;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -40,6 +45,16 @@
public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
extends ViewController<T> {
/**
+ * Pair representing:
+ * first - BiometricSource the currently displayed message is associated with.
+ * second - Timestamp the biometric message came in uptimeMillis.
+ * This Pair can be null if the message is not associated with a biometric.
+ */
+ @Nullable
+ private Pair<BiometricSourceType, Long> mMessageBiometricSource = null;
+ private static final Long SKIP_SHOWING_FACE_MESSAGE_AFTER_FP_MESSAGE_MS = 3500L;
+
+ /**
* Delay before speaking an accessibility announcement. Used to prevent
* lift-to-type from interrupting itself.
*/
@@ -149,12 +164,42 @@
* Sets a message to the underlying text view.
*/
public void setMessage(CharSequence s, boolean animate) {
+ setMessage(s, animate, null);
+ }
+
+ /**
+ * Sets a message to the underlying text view.
+ */
+ public void setMessage(CharSequence s, BiometricSourceType biometricSourceType) {
+ setMessage(s, true, biometricSourceType);
+ }
+
+ private void setMessage(
+ CharSequence s,
+ boolean animate,
+ BiometricSourceType biometricSourceType) {
+ final long uptimeMillis = SystemClock.uptimeMillis();
+ if (skipShowingFaceMessage(biometricSourceType, uptimeMillis)) {
+ Log.d("KeyguardMessageAreaController", "Skip showing face message \"" + s + "\"");
+ return;
+ }
+ mMessageBiometricSource = new Pair<>(biometricSourceType, uptimeMillis);
if (mView.isDisabled()) {
return;
}
mView.setMessage(s, animate);
}
+ private boolean skipShowingFaceMessage(
+ BiometricSourceType biometricSourceType, Long currentUptimeMillis
+ ) {
+ return mMessageBiometricSource != null
+ && biometricSourceType == BiometricSourceType.FACE
+ && mMessageBiometricSource.first == BiometricSourceType.FINGERPRINT
+ && (currentUptimeMillis - mMessageBiometricSource.second)
+ < SKIP_SHOWING_FACE_MESSAGE_AFTER_FP_MESSAGE_MS;
+ }
+
public void setMessage(int resId) {
String message = resId != 0 ? mView.getResources().getString(resId) : null;
setMessage(message);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 38c2829e..b7667a8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -34,6 +34,7 @@
import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
import static android.os.BatteryManager.CHARGING_POLICY_DEFAULT;
+import static android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
@@ -437,7 +438,8 @@
}
};
- private final OnSubscriptionsChangedListener mSubscriptionListener =
+ @VisibleForTesting
+ final OnSubscriptionsChangedListener mSubscriptionListener =
new OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
@@ -586,16 +588,14 @@
private void handleSimSubscriptionInfoChanged() {
Assert.isMainThread();
mLogger.v("onSubscriptionInfoChanged()");
- List<SubscriptionInfo> sil = mSubscriptionManager
- .getCompleteActiveSubscriptionInfoList();
- if (sil != null) {
- for (SubscriptionInfo subInfo : sil) {
+ List<SubscriptionInfo> subscriptionInfos = getSubscriptionInfo(true /* forceReload */);
+ if (!subscriptionInfos.isEmpty()) {
+ for (SubscriptionInfo subInfo : subscriptionInfos) {
mLogger.logSubInfo(subInfo);
}
} else {
mLogger.v("onSubscriptionInfoChanged: list is null");
}
- List<SubscriptionInfo> subscriptionInfos = getSubscriptionInfo(true /* forceReload */);
// Hack level over 9000: Because the subscription id is not yet valid when we see the
// first update in handleSimStateChange, we need to force refresh all SIM states
@@ -658,18 +658,18 @@
/**
* @return List of SubscriptionInfo records, maybe empty but never null.
+ *
+ * Note that this method will filter out any subscription which is PROFILE_CLASS_PROVISIONING
*/
public List<SubscriptionInfo> getSubscriptionInfo(boolean forceReload) {
List<SubscriptionInfo> sil = mSubscriptionInfo;
if (sil == null || forceReload) {
- sil = mSubscriptionManager.getCompleteActiveSubscriptionInfoList();
+ mSubscriptionInfo = mSubscriptionManager.getCompleteActiveSubscriptionInfoList()
+ .stream()
+ .filter(subInfo -> subInfo.getProfileClass() != PROFILE_CLASS_PROVISIONING)
+ .toList();
}
- if (sil == null) {
- // getCompleteActiveSubscriptionInfoList was null callers expect an empty list.
- mSubscriptionInfo = new ArrayList<>();
- } else {
- mSubscriptionInfo = sil;
- }
+
return new ArrayList<>(mSubscriptionInfo);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
index d2ad096..ce4032a 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
@@ -16,6 +16,7 @@
package com.android.keyguard.logging
+import android.hardware.biometrics.BiometricSourceType
import com.android.systemui.biometrics.AuthRippleController
import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController
import com.android.systemui.log.LogBuffer
@@ -117,6 +118,26 @@
)
}
+ fun logDropNonFingerprintMessage(
+ message: CharSequence,
+ followUpMessage: CharSequence?,
+ biometricSourceType: BiometricSourceType?,
+ ) {
+ buffer.log(
+ KeyguardIndicationController.TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = message.toString()
+ str2 = followUpMessage?.toString()
+ str3 = biometricSourceType?.name
+ },
+ {
+ "droppingNonFingerprintMessage message=$str1 " +
+ "followUpMessage:$str2 biometricSourceType:$str3"
+ }
+ )
+ }
+
fun logUpdateBatteryIndication(
powerIndication: String,
pluggedIn: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 0bd44f0..f4cd5b9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -17,7 +17,6 @@
package com.android.systemui.biometrics;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
-import static android.hardware.biometrics.Flags.customBiometricPrompt;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION;
@@ -33,6 +32,7 @@
import android.hardware.biometrics.BiometricAuthenticator.Modality;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager.Authenticators;
+import android.hardware.biometrics.Flags;
import android.hardware.biometrics.PromptInfo;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -402,7 +402,12 @@
@Nullable FaceSensorPropertiesInternal faceProps,
@NonNull VibratorHelper vibratorHelper
) {
- if (Utils.isBiometricAllowed(config.mPromptInfo) || customBiometricPrompt()) {
+ // Set this value before showing either of the prompt.
+ mPromptSelectorInteractorProvider.get().setShouldShowBpWithoutIconForCredential(
+ config.mPromptInfo);
+
+ if (Utils.isBiometricAllowed(config.mPromptInfo)
+ || mPromptViewModel.getShowBpWithoutIconForCredential().getValue()) {
addBiometricView(config, layoutInflater, viewModel, fpProps, faceProps, vibratorHelper);
} else if (constraintBp() && Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo)) {
addCredentialView(true, false);
@@ -411,7 +416,6 @@
}
}
-
private void addBiometricView(@NonNull Config config, @NonNull LayoutInflater layoutInflater,
@NonNull PromptViewModel viewModel,
@Nullable FingerprintSensorPropertiesInternal fpProps,
@@ -534,7 +538,8 @@
() -> animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED));
if (constraintBp()) {
// Do nothing on attachment with constraintLayout
- } else if (Utils.isBiometricAllowed(mConfig.mPromptInfo) || customBiometricPrompt()) {
+ } else if (Utils.isBiometricAllowed(mConfig.mPromptInfo)
+ || mPromptViewModel.getShowBpWithoutIconForCredential().getValue()) {
mBiometricScrollView.addView(mBiometricView.asView());
} else if (Utils.isDeviceCredentialAllowed(mConfig.mPromptInfo)) {
addCredentialView(true /* animatePanel */, false /* animateContents */);
@@ -819,6 +824,9 @@
final Runnable endActionRunnable = () -> {
setVisibility(View.INVISIBLE);
+ if (Flags.customBiometricPrompt()) {
+ mPromptSelectorInteractorProvider.get().resetPrompt();
+ }
removeWindowIfAttached();
};
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
index ad7bb0e..b87fadf 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/PromptRepository.kt
@@ -16,8 +16,11 @@
package com.android.systemui.biometrics.data.repository
+import android.hardware.biometrics.Flags
import android.hardware.biometrics.PromptInfo
import com.android.systemui.biometrics.AuthController
+import com.android.systemui.biometrics.Utils
+import com.android.systemui.biometrics.Utils.isDeviceCredentialAllowed
import com.android.systemui.biometrics.shared.model.PromptKind
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
@@ -65,6 +68,18 @@
*/
val isConfirmationRequired: Flow<Boolean>
+ /**
+ * If biometric prompt without icon needs to show for displaying content prior to credential
+ * view.
+ */
+ val showBpWithoutIconForCredential: StateFlow<Boolean>
+
+ /**
+ * Update whether biometric prompt without icon needs to show for displaying content prior to
+ * credential view, which should be set before [setPrompt].
+ */
+ fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo)
+
/** Update the prompt configuration, which should be set before [isShowing]. */
fun setPrompt(
promptInfo: PromptInfo,
@@ -129,6 +144,19 @@
}
.distinctUntilChanged()
+ private val _showBpWithoutIconForCredential: MutableStateFlow<Boolean> = MutableStateFlow(false)
+ override val showBpWithoutIconForCredential = _showBpWithoutIconForCredential.asStateFlow()
+
+ override fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo) {
+ val hasCredentialViewShown = kind.value !is PromptKind.Biometric
+ val showBpForCredential =
+ Flags.customBiometricPrompt() &&
+ !Utils.isBiometricAllowed(promptInfo) &&
+ isDeviceCredentialAllowed(promptInfo) &&
+ promptInfo.contentView != null
+ _showBpWithoutIconForCredential.value = showBpForCredential && !hasCredentialViewShown
+ }
+
override fun setPrompt(
promptInfo: PromptInfo,
userId: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
index e3facff..94cea57 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
@@ -30,6 +30,7 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -59,6 +60,13 @@
/** If the prompt is currently showing. */
val isShowing: Flow<Boolean> = biometricPromptRepository.isShowing
+ /**
+ * If biometric prompt without icon needs to show for displaying content prior to credential
+ * view.
+ */
+ val showBpWithoutIconForCredential: StateFlow<Boolean> =
+ biometricPromptRepository.showBpWithoutIconForCredential
+
/** Metadata about the current credential prompt, including app-supplied preferences. */
val prompt: Flow<BiometricPromptRequest.Credential?> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
index b3f9574..45816c1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
@@ -32,6 +32,7 @@
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@@ -70,6 +71,18 @@
/** Fingerprint sensor type */
val sensorType: Flow<FingerprintSensorType>
+ /**
+ * If biometric prompt without icon needs to show for displaying content prior to credential
+ * view.
+ */
+ val showBpWithoutIconForCredential: StateFlow<Boolean>
+
+ /**
+ * Update whether biometric prompt without icon needs to show for displaying content prior to
+ * credential view, which should be set before [PromptRepository.setPrompt].
+ */
+ fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo)
+
/** Use biometrics for authentication. */
fun useBiometricsForAuthentication(
promptInfo: PromptInfo,
@@ -154,6 +167,12 @@
override val sensorType: Flow<FingerprintSensorType> = fingerprintPropertyRepository.sensorType
+ override val showBpWithoutIconForCredential = promptRepository.showBpWithoutIconForCredential
+
+ override fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo) {
+ promptRepository.setShouldShowBpWithoutIconForCredential(promptInfo)
+ }
+
override fun useBiometricsForAuthentication(
promptInfo: PromptInfo,
userId: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
index 6133a51c..6f079e2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricPromptRequest.kt
@@ -17,6 +17,7 @@
val title: String,
val subtitle: String,
val description: String,
+ val contentView: PromptContentView?,
val userInfo: BiometricUserInfo,
val operationInfo: BiometricOperationInfo,
val showEmergencyCallButton: Boolean,
@@ -33,11 +34,11 @@
title = info.title?.toString() ?: "",
subtitle = info.subtitle?.toString() ?: "",
description = info.description?.toString() ?: "",
+ contentView = info.contentView,
userInfo = userInfo,
operationInfo = operationInfo,
showEmergencyCallButton = info.isShowEmergencyCallButton
) {
- val contentView: PromptContentView? = info.contentView
val logoRes: Int = info.logoRes
val logoBitmap: Bitmap? = info.logoBitmap
val logoDescription: String? = info.logoDescription
@@ -54,6 +55,7 @@
title = (info.deviceCredentialTitle ?: info.title)?.toString() ?: "",
subtitle = (info.deviceCredentialSubtitle ?: info.subtitle)?.toString() ?: "",
description = (info.deviceCredentialDescription ?: info.description)?.toString() ?: "",
+ contentView = info.contentView,
userInfo = userInfo,
operationInfo = operationInfo,
showEmergencyCallButton = info.isShowEmergencyCallButton
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 cd5b124..ea39247 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
@@ -22,7 +22,6 @@
import android.hardware.biometrics.BiometricAuthenticator
import android.hardware.biometrics.BiometricConstants
import android.hardware.biometrics.BiometricPrompt
-import android.hardware.biometrics.Flags.customBiometricPrompt
import android.hardware.face.FaceManager
import android.text.method.ScrollingMovementMethod
import android.util.Log
@@ -151,17 +150,6 @@
// these do not change and need to be set before any size transitions
val modalities = viewModel.modalities.first()
- // If there is no biometrics available, biometric prompt is showing just for displaying
- // content, no authentication needed.
- if (!(customBiometricPrompt() && modalities.isEmpty)) {
- PromptIconViewBinder.bind(
- iconView,
- iconOverlayView,
- iconSizeOverride,
- viewModel,
- )
- }
-
if (modalities.hasFingerprint) {
/**
* Load the given [rawResources] immediately so they are cached for use in the
@@ -233,6 +221,19 @@
)
}
+ lifecycleScope.launch {
+ viewModel.showBpWithoutIconForCredential.collect {
+ if (!it) {
+ PromptIconViewBinder.bind(
+ iconView,
+ iconOverlayView,
+ iconSizeOverride,
+ viewModel,
+ )
+ }
+ }
+ }
+
// TODO(b/251476085): migrate legacy icon controllers and remove
// The fingerprint sensor is started by the legacy
// AuthContainerView#onDialogAnimatedIn in all cases but the implicit coex flow
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 a37d916..8336d5e 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
@@ -21,7 +21,6 @@
import android.animation.ValueAnimator
import android.graphics.Outline
import android.graphics.Rect
-import android.hardware.biometrics.Flags
import android.transition.AutoTransition
import android.transition.TransitionManager
import android.view.Surface
@@ -55,6 +54,7 @@
import com.android.systemui.biometrics.ui.viewmodel.isRight
import com.android.systemui.biometrics.ui.viewmodel.isSmall
import com.android.systemui.biometrics.ui.viewmodel.isTop
+import com.android.systemui.keyguard.ui.view.layout.sections.setVisibility
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import kotlin.math.abs
@@ -111,14 +111,9 @@
val smallConstraintSet = ConstraintSet()
smallConstraintSet.clone(mediumConstraintSet)
- viewsToHideWhenSmall.forEach { smallConstraintSet.setVisibility(it.id, View.GONE) }
val largeConstraintSet = ConstraintSet()
largeConstraintSet.clone(mediumConstraintSet)
- viewsToHideWhenSmall.forEach { largeConstraintSet.setVisibility(it.id, View.GONE) }
- largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
- largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
- largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
largeConstraintSet.setGuidelineBegin(leftGuideline.id, 0)
largeConstraintSet.setGuidelineEnd(rightGuideline.id, 0)
largeConstraintSet.setGuidelineEnd(bottomGuideline.id, 0)
@@ -219,13 +214,18 @@
}
}
- view.repeatWhenAttached {
- var currentSize: PromptSize? = null
- val modalities = viewModel.modalities.first()
- // TODO(b/288175072): Move all visibility settings together.
- // If there is no biometrics available, biometric prompt is showing just for
- // displaying content, no authentication needed.
- if (Flags.customBiometricPrompt() && modalities.isEmpty) {
+ fun setConstraintSetVisibility() {
+ viewsToHideWhenSmall.forEach {
+ mediumConstraintSet.setVisibility(it.id, it.showContentOrHide())
+ largeConstraintSet.setVisibility(it.id, View.GONE)
+ smallConstraintSet.setVisibility(it.id, View.GONE)
+ }
+
+ largeConstraintSet.setVisibility(iconHolderView.id, View.GONE)
+ largeConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
+ largeConstraintSet.setVisibility(R.id.indicator, View.GONE)
+
+ if (viewModel.showBpWithoutIconForCredential.value) {
smallConstraintSet.setVisibility(iconHolderView.id, View.GONE)
smallConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
smallConstraintSet.setVisibility(R.id.indicator, View.GONE)
@@ -233,12 +233,17 @@
mediumConstraintSet.setVisibility(R.id.biometric_icon_overlay, View.GONE)
mediumConstraintSet.setVisibility(R.id.indicator, View.GONE)
}
+ }
+
+ view.repeatWhenAttached {
+ var currentSize: PromptSize? = null
+
lifecycleScope.launch {
combine(viewModel.position, viewModel.size, ::Pair).collect {
(position, size) ->
view.doOnAttach {
measureBounds(position)
-
+ setConstraintSetVisibility()
when {
size.isSmall -> {
val ratio =
@@ -313,7 +318,6 @@
// TODO(b/251476085): migrate the legacy panel controller and simplify this
view.repeatWhenAttached {
var currentSize: PromptSize? = null
- val modalities = viewModel.modalities.first()
lifecycleScope.launch {
/**
* View is only set visible in BiometricViewSizeBinder once PromptSize is
@@ -331,11 +335,13 @@
// prepare for animated size transitions
for (v in viewsToHideWhenSmall) {
- v.showContentOrHide(forceHide = size.isSmall)
+ v.visibility = v.showContentOrHide(forceHide = size.isSmall)
}
- if (Flags.customBiometricPrompt() && modalities.isEmpty) {
+
+ if (viewModel.showBpWithoutIconForCredential.value) {
iconHolderView.visibility = View.GONE
}
+
if (currentSize == null && size.isSmall) {
iconHolderView.alpha = 0f
}
@@ -344,9 +350,9 @@
}
// TODO(b/302735104): Fix wrong height due to the delay of
- // PromptContentView. addOnLayoutChangeListener() will cause crash when
- // showing credential view, since |PromptIconViewModel| won't release
- // the flow.
+ // PromptContentView. addOnLayoutChangeListener() will cause crash
+ // when showing credential view, since |PromptIconViewModel| won't
+ // release the flow.
// propagate size changes to legacy panel controller and animate
// transitions
view.doOnLayout {
@@ -460,15 +466,14 @@
return r == Surface.ROTATION_90 || r == Surface.ROTATION_270
}
-private fun View.showContentOrHide(forceHide: Boolean = false) {
+private fun View.showContentOrHide(forceHide: Boolean = false): Int {
val isTextViewWithBlankText = this is TextView && this.text.isBlank()
val isImageViewWithoutImage = this is ImageView && this.drawable == null
- visibility =
- if (forceHide || isTextViewWithBlankText || isImageViewWithoutImage) {
- View.GONE
- } else {
- View.VISIBLE
- }
+ return if (forceHide || isTextViewWithBlankText || isImageViewWithoutImage) {
+ View.GONE
+ } else {
+ View.VISIBLE
+ }
}
private fun View.centerX(): Int {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
index 03c5c53..46be8c7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/CredentialViewModel.kt
@@ -4,13 +4,13 @@
import android.graphics.drawable.Drawable
import android.text.InputType
import com.android.internal.widget.LockPatternView
-import com.android.systemui.res.R
import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.domain.interactor.CredentialStatus
import com.android.systemui.biometrics.domain.interactor.PromptCredentialInteractor
import com.android.systemui.biometrics.domain.model.BiometricPromptRequest
import com.android.systemui.biometrics.shared.model.BiometricUserInfo
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
import javax.inject.Inject
import kotlin.reflect.KClass
import kotlinx.coroutines.flow.Flow
@@ -32,14 +32,16 @@
/** Top level information about the prompt. */
val header: Flow<CredentialHeaderViewModel> =
- credentialInteractor.prompt.filterIsInstance<BiometricPromptRequest.Credential>().map {
- request ->
+ combine(
+ credentialInteractor.prompt.filterIsInstance<BiometricPromptRequest.Credential>(),
+ credentialInteractor.showBpWithoutIconForCredential
+ ) { request, showBpWithoutIconForCredential ->
BiometricPromptHeaderViewModelImpl(
request,
user = request.userInfo,
title = request.title,
- subtitle = request.subtitle,
- description = request.description,
+ subtitle = if (showBpWithoutIconForCredential) "" else request.subtitle,
+ description = if (showBpWithoutIconForCredential) "" else request.description,
icon = applicationContext.asLockIcon(request.userInfo.deviceCredentialOwnerId),
showEmergencyCallButton = request.showEmergencyCallButton
)
@@ -145,7 +147,7 @@
.createLaunchEmergencyDialerIntent(null)
.setFlags(
android.content.Intent.FLAG_ACTIVITY_NEW_TASK or
- android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
+ android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
)
context.startActivity(intent)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index c933e0e..2c749ba 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -47,6 +47,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
@@ -123,6 +124,9 @@
/** The kind of credential the user has. */
val credentialKind: Flow<PromptKind> = promptSelectorInteractor.credentialKind
+ val showBpWithoutIconForCredential: StateFlow<Boolean> =
+ promptSelectorInteractor.showBpWithoutIconForCredential
+
/** The label to use for the cancel button. */
val negativeButtonText: Flow<String> =
promptSelectorInteractor.prompt.map { it?.negativeButtonText ?: "" }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 1157d97..19af371 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -51,6 +51,7 @@
import com.android.systemui.controls.dagger.ControlsModule;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.SystemUser;
+import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.demomode.dagger.DemoModeModule;
import com.android.systemui.deviceentry.DeviceEntryModule;
import com.android.systemui.display.DisplayModule;
@@ -365,7 +366,8 @@
SysUiState sysUiState,
FeatureFlags featureFlags,
NotifPipelineFlags notifPipelineFlags,
- @Main Executor sysuiMainExecutor) {
+ @Main Executor sysuiMainExecutor,
+ @UiBackground Executor sysuiUiBgExecutor) {
return Optional.ofNullable(BubblesManager.create(context,
bubblesOptional,
notificationShadeWindowController,
@@ -384,7 +386,8 @@
sysUiState,
featureFlags,
notifPipelineFlags,
- sysuiMainExecutor));
+ sysuiMainExecutor,
+ sysuiUiBgExecutor));
}
@Binds
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
index cf7d601..baae986 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -62,6 +62,10 @@
import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.repository.UserRepository
import com.google.errorprone.annotations.CompileTimeConstant
+import java.io.PrintWriter
+import java.util.Arrays
+import java.util.stream.Collectors
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -83,10 +87,6 @@
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
-import java.io.PrintWriter
-import java.util.Arrays
-import java.util.stream.Collectors
-import javax.inject.Inject
/**
* API to run face authentication and detection for device entry / on keyguard (as opposed to the
@@ -431,11 +431,11 @@
override fun onAuthenticationFailed() {
_isAuthenticated.value = false
faceAuthLogger.authenticationFailed()
+ _authenticationStatus.value = FailedFaceAuthenticationStatus()
if (!_isLockedOut.value) {
// onAuthenticationError gets invoked before onAuthenticationFailed when the
// last auth attempt locks out face authentication.
- // Skip updating the authentication status in such a scenario.
- _authenticationStatus.value = FailedFaceAuthenticationStatus()
+ // Skip onFaceAuthRequestCompleted in such a scenario.
onFaceAuthRequestCompleted()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 9fc3692..6aed944 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -31,8 +31,8 @@
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.FlowPreview
-import kotlinx.coroutines.flow.sample
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@SysUISingleton
@@ -62,7 +62,6 @@
listenForTransitionToCamera(scope, keyguardInteractor)
}
- @FlowPreview
private fun listenForAlternateBouncerToLockscreenHubAodOrDozing() {
scope.launch {
keyguardInteractor.alternateBouncerShowing
@@ -71,7 +70,7 @@
// happening prematurely.
// This should eventually be removed in favor of
// [KeyguardTransitionInteractor#startDismissKeyguardTransition]
- .sample(150L)
+ .onEach { delay(150L) }
.sampleCombine(
keyguardInteractor.primaryBouncerShowing,
startedKeyguardTransitionStep,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 7263ae9..cb1571e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -44,6 +44,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
@@ -287,7 +288,7 @@
if (KeyguardWmStateRefactor.isEnabled) {
// When the refactor is enabled, we no longer use isKeyguardGoingAway.
scope.launch {
- swipeToDismissInteractor.dismissFling.collect { _ ->
+ swipeToDismissInteractor.dismissFling.filterNotNull().collect { _ ->
startTransitionTo(KeyguardState.GONE)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index d1fd719..719edd7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -45,7 +45,6 @@
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.shareIn
/** Encapsulates business-logic related to the keyguard transitions. */
@@ -177,10 +176,16 @@
* Lockscreen (0f).
*/
val dozeAmountTransition: Flow<TransitionStep> =
- merge(
- aodToLockscreenTransition.map { step -> step.copy(value = 1f - step.value) },
- lockscreenToAodTransition,
- )
+ repository.transitions
+ .filter { step -> step.from == AOD || step.to == AOD }
+ .map { step ->
+ if (step.from == AOD) {
+ step.copy(value = 1 - step.value)
+ } else {
+ step
+ }
+ }
+ .shareIn(scope, SharingStarted.Eagerly, replay = 1)
/** The last [TransitionStep] with a [TransitionState] of STARTED */
val startedKeyguardTransitionStep: Flow<TransitionStep> =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
index 8a3b57b..5741b94 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
@@ -29,7 +29,6 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combineTransform
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onStart
/** Models UI state for the alpha of the AOD (always-on display). */
@@ -46,24 +45,23 @@
/** The alpha level for the entire lockscreen while in AOD. */
val alpha: Flow<Float> =
combineTransform(
- keyguardTransitionInteractor.transitions,
- goneToAodTransitionViewModel.enterFromTopAnimationAlpha.onStart { emit(0f) },
- goneToDozingTransitionViewModel.lockscreenAlpha.onStart { emit(0f) },
- keyguardInteractor.keyguardAlpha.onStart { emit(1f) },
- ) { step, goneToAodAlpha, goneToDozingAlpha, keyguardAlpha ->
- if (step.to == GONE) {
- // When transitioning to GONE, only emit a value when complete as other
- // transitions may be controlling the alpha fade
- if (step.value == 1f) {
- emit(0f)
- }
- } else if (step.from == GONE && step.to == AOD) {
- emit(goneToAodAlpha)
- } else if (step.from == GONE && step.to == DOZING) {
- emit(goneToDozingAlpha)
- } else if (!migrateClocksToBlueprint()) {
- emit(keyguardAlpha)
+ keyguardTransitionInteractor.transitions,
+ goneToAodTransitionViewModel.enterFromTopAnimationAlpha.onStart { emit(0f) },
+ goneToDozingTransitionViewModel.lockscreenAlpha.onStart { emit(0f) },
+ keyguardInteractor.keyguardAlpha.onStart { emit(1f) },
+ ) { step, goneToAodAlpha, goneToDozingAlpha, keyguardAlpha ->
+ if (step.to == GONE) {
+ // When transitioning to GONE, only emit a value when complete as other
+ // transitions may be controlling the alpha fade
+ if (step.value == 1f) {
+ emit(0f)
}
+ } else if (step.from == GONE && step.to == AOD) {
+ emit(goneToAodAlpha)
+ } else if (step.from == GONE && step.to == DOZING) {
+ emit(goneToDozingAlpha)
+ } else if (!migrateClocksToBlueprint()) {
+ emit(keyguardAlpha)
}
- .distinctUntilChanged()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
index 8665aab..7be390a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt
@@ -28,10 +28,6 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.BurnInModel
-import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
-import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
-import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
-import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
@@ -47,7 +43,6 @@
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
/**
@@ -127,17 +122,9 @@
params: BurnInParameters,
): Flow<BurnInModel> {
return combine(
- merge(
- keyguardTransitionInteractor.transition(GONE, AOD).map { it.value },
- keyguardTransitionInteractor.transition(AOD, PRIMARY_BOUNCER).map {
- 1f - it.value
- },
- keyguardTransitionInteractor.transition(ALTERNATE_BOUNCER, AOD).map {
- it.value
- },
- keyguardTransitionInteractor.dozeAmountTransition.map { it.value },
- )
- .map { dozeAmount -> Interpolators.FAST_OUT_SLOW_IN.getInterpolation(dozeAmount) },
+ keyguardTransitionInteractor.dozeAmountTransition.map {
+ Interpolators.FAST_OUT_SLOW_IN.getInterpolation(it.value)
+ },
burnInInteractor.keyguardBurnIn,
) { interpolated, burnIn ->
val useScaleOnly =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 921eb66..bdcaf09 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -74,6 +74,10 @@
private val dozingToLockscreenTransitionViewModel: DozingToLockscreenTransitionViewModel,
private val glanceableHubToLockscreenTransitionViewModel:
GlanceableHubToLockscreenTransitionViewModel,
+ private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
+ private val goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel,
+ private val lockscreenToAodTransitionViewModel: LockscreenToAodTransitionViewModel,
+ private val lockscreenToDozingTransitionViewModel: LockscreenToDozingTransitionViewModel,
private val lockscreenToDreamingTransitionViewModel: LockscreenToDreamingTransitionViewModel,
private val lockscreenToGlanceableHubTransitionViewModel:
LockscreenToGlanceableHubTransitionViewModel,
@@ -133,17 +137,24 @@
fun alpha(viewState: ViewStateAccessor): Flow<Float> {
return combine(
communalInteractor.isIdleOnCommunal,
- keyguardTransitionInteractor.transitionValue(GONE).onStart { emit(0f) },
+ keyguardTransitionInteractor
+ .transitionValue(GONE)
+ .map { it == 1f }
+ .onStart { emit(false) }
+ .distinctUntilChanged(),
// The transitions are mutually exclusive, so they are safe to merge to get the last
// value emitted by any of them. Do not add flows that cannot make this guarantee.
merge(
- aodAlphaViewModel.alpha,
alphaOnShadeExpansion,
keyguardInteractor.dismissAlpha.filterNotNull(),
alternateBouncerToGoneTransitionViewModel.lockscreenAlpha,
aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
dozingToLockscreenTransitionViewModel.lockscreenAlpha,
glanceableHubToLockscreenTransitionViewModel.keyguardAlpha,
+ goneToAodTransitionViewModel.enterFromTopAnimationAlpha,
+ goneToDozingTransitionViewModel.lockscreenAlpha,
+ lockscreenToAodTransitionViewModel.lockscreenAlpha(viewState),
+ lockscreenToDozingTransitionViewModel.lockscreenAlpha,
lockscreenToDreamingTransitionViewModel.lockscreenAlpha,
lockscreenToGlanceableHubTransitionViewModel.keyguardAlpha,
lockscreenToGoneTransitionViewModel.lockscreenAlpha(viewState),
@@ -156,8 +167,8 @@
primaryBouncerToLockscreenTransitionViewModel.lockscreenAlpha,
)
.onStart { emit(1f) }
- ) { isIdleOnCommunal, goneValue, alpha ->
- if (isIdleOnCommunal || goneValue == 1f) {
+ ) { isIdleOnCommunal, gone, alpha ->
+ if (isIdleOnCommunal || gone) {
// Keyguard should not show while the communal hub is fully visible. This check
// is added since at the moment, closing the notification shade will cause the
// keyguard alpha to be set back to 1. Also ensure keyguard is never visible
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index d792889..23320be 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -23,6 +23,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.SplitShadeStateController
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
@@ -39,6 +40,7 @@
private val interactor: KeyguardBlueprintInteractor,
private val authController: AuthController,
val longPress: KeyguardLongPressViewModel,
+ val splitShadeStateController: SplitShadeStateController,
) {
private val clockSize = clockInteractor.clockSize
@@ -46,8 +48,10 @@
get() = authController.isUdfpsSupported
val isLargeClockVisible: Boolean
get() = clockSize.value == KeyguardClockSwitch.LARGE
- val areNotificationsVisible: Boolean
- get() = !isLargeClockVisible
+ fun areNotificationsVisible(resources: Resources): Boolean {
+ return !isLargeClockVisible ||
+ splitShadeStateController.shouldUseSplitNotificationShade(resources)
+ }
fun getSmartSpacePaddingTop(resources: Resources): Int {
return if (isLargeClockVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
index 7bf51a7..1f9f304 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModel.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import android.util.MathUtils
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
@@ -67,6 +68,15 @@
onCancel = { 1f },
)
+ fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
+ var startAlpha = 1f
+ return transitionAnimation.sharedFlow(
+ duration = 500.milliseconds,
+ onStart = { startAlpha = viewState.alpha() },
+ onStep = { MathUtils.lerp(startAlpha, 1f, it) },
+ )
+ }
+
override val deviceEntryParentViewAlpha: Flow<Float> =
deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest {
isUdfpsEnrolledAndEnabled ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
index b60c52b..c836f01 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDozingTransitionViewModel.kt
@@ -44,6 +44,14 @@
to = KeyguardState.DOZING,
)
+ val lockscreenAlpha: Flow<Float> =
+ transitionAnimation.sharedFlow(
+ duration = 250.milliseconds,
+ onStep = { 1 - it },
+ onFinish = { 1f },
+ onCancel = { 1f },
+ )
+
val shortcutsAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
duration = 250.milliseconds,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/MediaCarouselViewModel.kt
new file mode 100644
index 0000000..027a739
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/MediaCarouselViewModel.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
+import javax.inject.Inject
+
+class MediaCarouselViewModel @Inject constructor(mediaDataManager: MediaDataManager) {
+ val isMediaVisible: Boolean = mediaDataManager.hasActiveMediaOrRecommendation()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 000f3c09d..5f8b5dd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -221,7 +221,7 @@
// If scene framework is enabled, set the scene container window to
// visible and let the touch "slip" into that window.
if (mSceneContainerFlags.isEnabled()) {
- mSceneInteractor.get().setVisible(true, "swipe down on launcher");
+ mSceneInteractor.get().onRemoteUserInteractionStarted("launcher swipe");
} else {
mShadeViewControllerLazy.get().startInputFocusTransfer();
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
index a302194..e60dff1 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt
@@ -49,6 +49,13 @@
private val _isVisible = MutableStateFlow(true)
val isVisible: StateFlow<Boolean> = _isVisible.asStateFlow()
+ /**
+ * Whether there's an ongoing remotely-initiated user interaction.
+ *
+ * For more information see the logic in `SceneInteractor` that mutates this.
+ */
+ val isRemoteUserInteractionOngoing = MutableStateFlow(false)
+
private val defaultTransitionState = ObservableTransitionState.Idle(config.initialSceneKey)
private val _transitionState = MutableStateFlow<Flow<ObservableTransitionState>?>(null)
val transitionState: StateFlow<ObservableTransitionState> =
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 0add444..6b7c672 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -31,6 +31,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
@@ -121,7 +122,21 @@
)
/** Whether the scene container is visible. */
- val isVisible: StateFlow<Boolean> = repository.isVisible
+ val isVisible: StateFlow<Boolean> =
+ combine(
+ repository.isVisible,
+ repository.isRemoteUserInteractionOngoing,
+ ) { isVisible, isRemoteUserInteractionOngoing ->
+ isVisibleInternal(
+ raw = isVisible,
+ isRemoteUserInteractionOngoing = isRemoteUserInteractionOngoing,
+ )
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = isVisibleInternal()
+ )
/**
* Returns the keys of all scenes in the container.
@@ -164,7 +179,14 @@
repository.changeScene(toScene, transitionKey)
}
- /** Sets the visibility of the container. */
+ /**
+ * Sets the visibility of the container.
+ *
+ * Please do not call this from outside of the scene framework. If you are trying to force the
+ * visibility to visible or invisible, prefer making changes to the existing caller of this
+ * method or to upstream state used to calculate [isVisible]; for an example of the latter,
+ * please see [onRemoteUserInteractionStarted] and [onUserInteractionFinished].
+ */
fun setVisible(isVisible: Boolean, loggingReason: String) {
val wasVisible = repository.isVisible.value
if (wasVisible == isVisible) {
@@ -180,6 +202,31 @@
}
/**
+ * Notifies that a remote user interaction has begun.
+ *
+ * This is a user interaction that originates outside of the UI of the scene container and
+ * possibly outside of the System UI process itself.
+ *
+ * As an example, consider the dragging that can happen in the launcher that expands the shade.
+ * This is a user interaction that begins remotely (as it starts in the launcher process) and is
+ * then rerouted by window manager to System UI. While the user interaction definitely continues
+ * within the System UI process and code, it also originates remotely.
+ */
+ fun onRemoteUserInteractionStarted(loggingReason: String) {
+ logger.logRemoteUserInteractionStarted(loggingReason)
+ repository.isRemoteUserInteractionOngoing.value = true
+ }
+
+ /**
+ * Notifies that the current user interaction (internally or remotely started, see
+ * [onRemoteUserInteractionStarted]) has finished.
+ */
+ fun onUserInteractionFinished() {
+ logger.logUserInteractionFinished()
+ repository.isRemoteUserInteractionOngoing.value = false
+ }
+
+ /**
* Binds the given flow so the system remembers it.
*
* Note that you must call is with `null` when the UI is done or risk a memory leak.
@@ -187,4 +234,11 @@
fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
repository.setTransitionState(transitionState)
}
+
+ private fun isVisibleInternal(
+ raw: Boolean = repository.isVisible.value,
+ isRemoteUserInteractionOngoing: Boolean = repository.isRemoteUserInteractionOngoing.value,
+ ): Boolean {
+ return raw || isRemoteUserInteractionOngoing
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
index d59fcff..cbf7b3e 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/logger/SceneLogger.kt
@@ -95,6 +95,26 @@
)
}
+ fun logRemoteUserInteractionStarted(
+ reason: String,
+ ) {
+ logBuffer.log(
+ tag = TAG,
+ level = LogLevel.INFO,
+ messageInitializer = { str1 = reason },
+ messagePrinter = { "remote user interaction started, reason: $str3" },
+ )
+ }
+
+ fun logUserInteractionFinished() {
+ logBuffer.log(
+ tag = TAG,
+ level = LogLevel.INFO,
+ messageInitializer = {},
+ messagePrinter = { "user interaction finished" },
+ )
+ }
+
companion object {
private const val TAG = "SceneFramework"
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index 4cd3baa..91861aa 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -68,6 +68,12 @@
fun onMotionEvent(event: MotionEvent) {
powerInteractor.onUserTouch()
falsingInteractor.onTouchEvent(event)
+ if (
+ event.actionMasked == MotionEvent.ACTION_UP ||
+ event.actionMasked == MotionEvent.ACTION_CANCEL
+ ) {
+ sceneInteractor.onUserInteractionFinished()
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 7b330b0..fd3c480 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -3074,7 +3074,9 @@
}
private void onClosingFinished() {
- mOpenCloseListener.onClosingFinished();
+ if (mOpenCloseListener != null) {
+ mOpenCloseListener.onClosingFinished();
+ }
setClosingWithAlphaFadeout(false);
mMediaHierarchyManager.closeGuts();
}
@@ -4705,7 +4707,9 @@
if (mSplitShadeEnabled && !isKeyguardShowing()) {
mQsController.setExpandImmediate(true);
}
- mOpenCloseListener.onOpenStarted();
+ if (mOpenCloseListener != null) {
+ mOpenCloseListener.onOpenStarted();
+ }
}
if (state == STATE_CLOSED) {
mQsController.setExpandImmediate(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 19fe60a..1ec86ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -71,6 +71,7 @@
import android.provider.DeviceConfig;
import android.text.TextUtils;
import android.text.format.Formatter;
+import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
@@ -187,6 +188,7 @@
private CharSequence mTransientIndication;
private CharSequence mBiometricMessage;
private CharSequence mBiometricMessageFollowUp;
+ private BiometricSourceType mBiometricMessageSource;
protected ColorStateList mInitialTextColorState;
private boolean mVisible;
private boolean mOrganizationOwnedDevice;
@@ -206,7 +208,7 @@
private int mBatteryLevel;
private boolean mBatteryPresent = true;
private long mChargingTimeRemaining;
- private String mBiometricErrorMessageToShowOnScreenOn;
+ private Pair<String, BiometricSourceType> mBiometricErrorMessageToShowOnScreenOn;
private final Set<Integer> mCoExFaceAcquisitionMsgIdsToShow;
private final FaceHelpMessageDeferral mFaceAcquiredMessageDeferral;
private boolean mInited;
@@ -225,15 +227,18 @@
mIsActiveDreamLockscreenHosted = isLockscreenHosted;
updateDeviceEntryIndication(false);
};
- private final ScreenLifecycle.Observer mScreenObserver =
- new ScreenLifecycle.Observer() {
+ private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
@Override
public void onScreenTurnedOn() {
mHandler.removeMessages(MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON);
if (mBiometricErrorMessageToShowOnScreenOn != null) {
String followUpMessage = mFaceLockedOutThisAuthSession
? faceLockedOutFollowupMessage() : null;
- showBiometricMessage(mBiometricErrorMessageToShowOnScreenOn, followUpMessage);
+ showBiometricMessage(
+ mBiometricErrorMessageToShowOnScreenOn.first,
+ followUpMessage,
+ mBiometricErrorMessageToShowOnScreenOn.second
+ );
// We want to keep this message around in case the screen was off
hideBiometricMessageDelayed(DEFAULT_HIDE_DELAY_MS);
mBiometricErrorMessageToShowOnScreenOn = null;
@@ -879,8 +884,35 @@
updateTransient();
}
- private void showBiometricMessage(CharSequence biometricMessage) {
- showBiometricMessage(biometricMessage, null);
+ private void showSuccessBiometricMessage(
+ CharSequence biometricMessage,
+ @Nullable CharSequence biometricMessageFollowUp,
+ BiometricSourceType biometricSourceType
+ ) {
+ showBiometricMessage(biometricMessage, biometricMessageFollowUp, biometricSourceType, true);
+ }
+
+ private void showSuccessBiometricMessage(CharSequence biometricMessage,
+ BiometricSourceType biometricSourceType) {
+ showSuccessBiometricMessage(biometricMessage, null, biometricSourceType);
+ }
+
+ private void showBiometricMessage(CharSequence biometricMessage,
+ BiometricSourceType biometricSourceType) {
+ showBiometricMessage(biometricMessage, null, biometricSourceType, false);
+ }
+
+ private void showBiometricMessage(
+ CharSequence biometricMessage,
+ @Nullable CharSequence biometricMessageFollowUp,
+ BiometricSourceType biometricSourceType
+ ) {
+ showBiometricMessage(
+ biometricMessage,
+ biometricMessageFollowUp,
+ biometricSourceType,
+ false
+ );
}
/**
@@ -889,15 +921,33 @@
* by {@link KeyguardIndicationRotateTextViewController}, see class for rotating message
* logic.
*/
- private void showBiometricMessage(CharSequence biometricMessage,
- @Nullable CharSequence biometricMessageFollowUp) {
+ private void showBiometricMessage(
+ CharSequence biometricMessage,
+ @Nullable CharSequence biometricMessageFollowUp,
+ BiometricSourceType biometricSourceType,
+ boolean isSuccessMessage
+ ) {
if (TextUtils.equals(biometricMessage, mBiometricMessage)
+ && biometricSourceType == mBiometricMessageSource
&& TextUtils.equals(biometricMessageFollowUp, mBiometricMessageFollowUp)) {
return;
}
+ if (!isSuccessMessage
+ && mBiometricMessageSource == FINGERPRINT
+ && biometricSourceType != FINGERPRINT) {
+ // drop all non-fingerprint biometric messages if there's a fingerprint message showing
+ mKeyguardLogger.logDropNonFingerprintMessage(
+ biometricMessage,
+ biometricMessageFollowUp,
+ biometricSourceType
+ );
+ return;
+ }
+
mBiometricMessage = biometricMessage;
mBiometricMessageFollowUp = biometricMessageFollowUp;
+ mBiometricMessageSource = biometricSourceType;
mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK);
hideBiometricMessageDelayed(
@@ -914,6 +964,7 @@
if (mBiometricMessage != null || mBiometricMessageFollowUp != null) {
mBiometricMessage = null;
mBiometricMessageFollowUp = null;
+ mBiometricMessageSource = null;
mHideBiometricMessageHandler.cancel();
updateBiometricMessage();
}
@@ -1085,7 +1136,8 @@
} else {
message = mContext.getString(R.string.keyguard_retry);
}
- mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState);
+ mStatusBarKeyguardViewManager.setKeyguardMessage(message, mInitialTextColorState,
+ null);
}
} else {
final boolean canSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer(
@@ -1097,34 +1149,40 @@
|| mAccessibilityManager.isTouchExplorationEnabled();
if (udfpsSupported && faceAuthenticated) { // co-ex
if (a11yEnabled) {
- showBiometricMessage(
+ showSuccessBiometricMessage(
mContext.getString(R.string.keyguard_face_successful_unlock),
- mContext.getString(R.string.keyguard_unlock)
+ mContext.getString(R.string.keyguard_unlock),
+ FACE
);
} else {
- showBiometricMessage(
+ showSuccessBiometricMessage(
mContext.getString(R.string.keyguard_face_successful_unlock),
- mContext.getString(R.string.keyguard_unlock_press)
+ mContext.getString(R.string.keyguard_unlock_press),
+ FACE
);
}
} else if (faceAuthenticated) { // face-only
- showBiometricMessage(
+ showSuccessBiometricMessage(
mContext.getString(R.string.keyguard_face_successful_unlock),
- mContext.getString(R.string.keyguard_unlock)
+ mContext.getString(R.string.keyguard_unlock),
+ FACE
);
} else if (udfpsSupported) { // udfps-only
if (a11yEnabled) {
- showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+ showSuccessBiometricMessage(
+ mContext.getString(R.string.keyguard_unlock),
+ null
+ );
} else {
- showBiometricMessage(mContext.getString(
- R.string.keyguard_unlock_press));
+ showSuccessBiometricMessage(mContext.getString(
+ R.string.keyguard_unlock_press), null);
}
} else { // no security or unlocked by a trust agent
- showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+ showSuccessBiometricMessage(mContext.getString(R.string.keyguard_unlock), null);
}
} else {
// suggest swiping up for the primary authentication bouncer
- showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
+ showBiometricMessage(mContext.getString(R.string.keyguard_unlock), null);
}
}
}
@@ -1228,6 +1286,13 @@
&& msgId != BIOMETRIC_HELP_FACE_NOT_AVAILABLE;
final boolean faceAuthFailed = biometricSourceType == FACE
&& msgId == BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; // ran through matcher & failed
+ if (faceAuthFailed && mFaceLockedOutThisAuthSession) {
+ mKeyguardLogger.logBiometricMessage(
+ "skipped showing faceAuthFailed message due to lockout",
+ msgId,
+ helpString);
+ return;
+ }
final boolean fpAuthFailed = biometricSourceType == FINGERPRINT
&& msgId == BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED; // ran matcher & failed
final boolean isUnlockWithFingerprintPossible = canUnlockWithFingerprint();
@@ -1245,49 +1310,55 @@
mBouncerMessageInteractor.setFaceAcquisitionMessage(helpString);
}
mStatusBarKeyguardViewManager.setKeyguardMessage(helpString,
- mInitialTextColorState);
+ mInitialTextColorState, biometricSourceType);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
if (isCoExFaceAcquisitionMessage && msgId == FACE_ACQUIRED_TOO_DARK) {
showBiometricMessage(
helpString,
- mContext.getString(R.string.keyguard_suggest_fingerprint)
+ mContext.getString(R.string.keyguard_suggest_fingerprint),
+ biometricSourceType
);
} else if (faceAuthFailed && isUnlockWithFingerprintPossible) {
showBiometricMessage(
mContext.getString(R.string.keyguard_face_failed),
- mContext.getString(R.string.keyguard_suggest_fingerprint)
+ mContext.getString(R.string.keyguard_suggest_fingerprint),
+ biometricSourceType
);
} else if (fpAuthFailed
&& mKeyguardUpdateMonitor.isCurrentUserUnlockedWithFace()) {
// face had already previously unlocked the device, so instead of showing a
// fingerprint error, tell them they have already unlocked with face auth
// and how to enter their device
- showBiometricMessage(
+ showSuccessBiometricMessage(
mContext.getString(R.string.keyguard_face_successful_unlock),
- mContext.getString(R.string.keyguard_unlock)
+ mContext.getString(R.string.keyguard_unlock),
+ null
);
} else if (fpAuthFailed
&& mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser())) {
- showBiometricMessage(
+ showSuccessBiometricMessage(
getTrustGrantedIndication(),
- mContext.getString(R.string.keyguard_unlock)
+ mContext.getString(R.string.keyguard_unlock),
+ null
);
} else if (faceAuthUnavailable) {
showBiometricMessage(
helpString,
isUnlockWithFingerprintPossible
? mContext.getString(R.string.keyguard_suggest_fingerprint)
- : mContext.getString(R.string.keyguard_unlock)
+ : mContext.getString(R.string.keyguard_unlock),
+ biometricSourceType
);
} else {
- showBiometricMessage(helpString);
+ showBiometricMessage(helpString, biometricSourceType);
}
} else if (faceAuthFailed) {
// show action to unlock
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ACTION_TO_UNLOCK),
TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
} else {
- mBiometricErrorMessageToShowOnScreenOn = helpString;
+ mBiometricErrorMessageToShowOnScreenOn =
+ new Pair<>(helpString, biometricSourceType);
mHandler.sendMessageDelayed(
mHandler.obtainMessage(MSG_RESET_ERROR_MESSAGE_ON_SCREEN_ON),
1000);
@@ -1333,7 +1404,7 @@
} else if (mIndicationHelper.isFaceLockoutErrorMsg(msgId)) {
handleFaceLockoutError(errString);
} else {
- showErrorMessageNowOrLater(errString, null);
+ showErrorMessageNowOrLater(errString, null, FACE);
}
}
@@ -1343,7 +1414,7 @@
msgId,
errString);
} else {
- showErrorMessageNowOrLater(errString, null);
+ showErrorMessageNowOrLater(errString, null, FINGERPRINT);
}
}
@@ -1371,7 +1442,7 @@
@Override
public void onTrustAgentErrorMessage(CharSequence message) {
- showBiometricMessage(message);
+ showBiometricMessage(message, null);
}
@Override
@@ -1459,12 +1530,13 @@
// had too many unsuccessful attempts.
if (!mFaceLockedOutThisAuthSession) {
mFaceLockedOutThisAuthSession = true;
- showErrorMessageNowOrLater(errString, followupMessage);
+ showErrorMessageNowOrLater(errString, followupMessage, FACE);
} else if (!mAuthController.isUdfpsFingerDown()) {
// On subsequent lockouts, we show a more generic locked out message.
showErrorMessageNowOrLater(
mContext.getString(R.string.keyguard_face_unlock_unavailable),
- followupMessage);
+ followupMessage,
+ FACE);
}
}
@@ -1484,7 +1556,8 @@
&& !mStatusBarKeyguardViewManager.isBouncerShowing()) {
showBiometricMessage(
deferredFaceMessage,
- mContext.getString(R.string.keyguard_suggest_fingerprint)
+ mContext.getString(R.string.keyguard_suggest_fingerprint),
+ FACE
);
} else {
// otherwise, don't show any message
@@ -1496,7 +1569,8 @@
// user to manually retry.
showBiometricMessage(
deferredFaceMessage,
- mContext.getString(R.string.keyguard_unlock)
+ mContext.getString(R.string.keyguard_unlock),
+ FACE
);
} else {
// Face-only
@@ -1510,13 +1584,15 @@
getCurrentUser()) && mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed();
}
- private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg) {
+ private void showErrorMessageNowOrLater(String errString, @Nullable String followUpMsg,
+ BiometricSourceType biometricSourceType) {
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState);
+ mStatusBarKeyguardViewManager.setKeyguardMessage(errString, mInitialTextColorState,
+ biometricSourceType);
} else if (mScreenLifecycle.getScreenState() == SCREEN_ON) {
- showBiometricMessage(errString, followUpMsg);
+ showBiometricMessage(errString, followUpMsg, biometricSourceType);
} else {
- mBiometricErrorMessageToShowOnScreenOn = errString;
+ mBiometricErrorMessageToShowOnScreenOn = new Pair<>(errString, biometricSourceType);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index d10ca3d..6b47ac1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -239,10 +239,22 @@
ViewGroup.LayoutParams layoutParams = getLayoutParams();
mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
layoutParams.height = mStatusBarHeight - waterfallTopInset;
+ updateSystemIconsContainerHeight();
updatePaddings();
setLayoutParams(layoutParams);
}
+ private void updateSystemIconsContainerHeight() {
+ View systemIconsContainer = findViewById(R.id.system_icons);
+ ViewGroup.LayoutParams layoutParams = systemIconsContainer.getLayoutParams();
+ int newSystemIconsHeight =
+ getResources().getDimensionPixelSize(R.dimen.status_bar_system_icons_height);
+ if (layoutParams.height != newSystemIconsHeight) {
+ layoutParams.height = newSystemIconsHeight;
+ systemIconsContainer.setLayoutParams(layoutParams);
+ }
+ }
+
private void updatePaddings() {
int statusBarPaddingStart = getResources().getDimensionPixelSize(
R.dimen.status_bar_padding_start);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 29fd225..d1055c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -23,7 +23,6 @@
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import static com.android.systemui.util.kotlin.JavaAdapterKt.combineFlows;
import android.content.Context;
@@ -98,6 +97,7 @@
import com.android.systemui.unfold.FoldAodAnimationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import dagger.Lazy;
@@ -348,6 +348,8 @@
private Lazy<KeyguardSurfaceBehindInteractor> mSurfaceBehindInteractor;
private Lazy<KeyguardDismissActionInteractor> mKeyguardDismissActionInteractor;
+ private final JavaAdapter mJavaAdapter;
+
@Inject
public StatusBarKeyguardViewManager(
Context context,
@@ -378,7 +380,8 @@
Lazy<WindowManagerLockscreenVisibilityInteractor> wmLockscreenVisibilityInteractor,
Lazy<KeyguardDismissActionInteractor> keyguardDismissActionInteractorLazy,
SelectedUserInteractor selectedUserInteractor,
- Lazy<KeyguardSurfaceBehindInteractor> surfaceBehindInteractor
+ Lazy<KeyguardSurfaceBehindInteractor> surfaceBehindInteractor,
+ JavaAdapter javaAdapter
) {
mContext = context;
mViewMediatorCallback = callback;
@@ -411,6 +414,7 @@
mKeyguardDismissActionInteractor = keyguardDismissActionInteractorLazy;
mSelectedUserInteractor = selectedUserInteractor;
mSurfaceBehindInteractor = surfaceBehindInteractor;
+ mJavaAdapter = javaAdapter;
}
KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@@ -481,8 +485,7 @@
if (KeyguardWmStateRefactor.isEnabled()) {
// Show the keyguard views whenever we've told WM that the lockscreen is visible.
- collectFlow(
- getViewRootImpl().getView(),
+ mJavaAdapter.alwaysCollectFlow(
combineFlows(
mWmLockscreenVisibilityInteractor.get().getLockscreenVisibility(),
mSurfaceBehindInteractor.get().isAnimatingSurface(),
@@ -781,7 +784,7 @@
}
updateAlternateBouncerShowing(mAlternateBouncerInteractor.show());
- setKeyguardMessage(message, null);
+ setKeyguardMessage(message, null, null);
return;
}
@@ -1444,11 +1447,12 @@
}
/** Display security message to relevant KeyguardMessageArea. */
- public void setKeyguardMessage(String message, ColorStateList colorState) {
+ public void setKeyguardMessage(String message, ColorStateList colorState,
+ BiometricSourceType biometricSourceType) {
if (mAlternateBouncerInteractor.isVisibleState()) {
if (mKeyguardMessageAreaController != null) {
DeviceEntryUdfpsRefactor.assertInLegacyMode();
- mKeyguardMessageAreaController.setMessage(message);
+ mKeyguardMessageAreaController.setMessage(message, biometricSourceType);
}
} else {
mPrimaryBouncerInteractor.showMessage(message, colorState);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 65dede8..cb61534 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -28,6 +28,7 @@
import static com.android.server.notification.Flags.screenshareNotificationHiding;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
import android.app.INotificationManager;
import android.app.Notification;
@@ -50,6 +51,7 @@
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.flags.FeatureFlags;
@@ -97,6 +99,7 @@
private final Context mContext;
private final Bubbles mBubbles;
private final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final KeyguardStateController mKeyguardStateController;
private final ShadeController mShadeController;
private final IStatusBarService mBarService;
private final INotificationManager mNotificationManager;
@@ -109,6 +112,7 @@
private final NotifPipeline mNotifPipeline;
private final NotifPipelineFlags mNotifPipelineFlags;
private final Executor mSysuiMainExecutor;
+ private final Executor mSysuiUiBgExecutor;
private final Bubbles.SysuiProxy mSysuiProxy;
// TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline
@@ -116,6 +120,8 @@
private final StatusBarWindowCallback mStatusBarWindowCallback;
private final Runnable mSensitiveStateChangedListener;
private boolean mPanelExpanded;
+ private boolean mKeyguardShowing;
+ private boolean mDreamingOrInPreview;
/**
* Creates {@link BubblesManager}, returns {@code null} if Optional {@link Bubbles} not present
@@ -140,7 +146,8 @@
SysUiState sysUiState,
FeatureFlags featureFlags,
NotifPipelineFlags notifPipelineFlags,
- Executor sysuiMainExecutor) {
+ Executor sysuiMainExecutor,
+ Executor sysuiUiBgExecutor) {
if (bubblesOptional.isPresent()) {
return new BubblesManager(context,
bubblesOptional.get(),
@@ -160,7 +167,8 @@
sysUiState,
featureFlags,
notifPipelineFlags,
- sysuiMainExecutor);
+ sysuiMainExecutor,
+ sysuiUiBgExecutor);
} else {
return null;
}
@@ -185,10 +193,12 @@
SysUiState sysUiState,
FeatureFlags featureFlags,
NotifPipelineFlags notifPipelineFlags,
- Executor sysuiMainExecutor) {
+ Executor sysuiMainExecutor,
+ Executor sysuiUiBgExecutor) {
mContext = context;
mBubbles = bubbles;
mNotificationShadeWindowController = notificationShadeWindowController;
+ mKeyguardStateController = keyguardStateController;
mShadeController = shadeController;
mNotificationManager = notificationManager;
mDreamManager = dreamManager;
@@ -200,6 +210,7 @@
mNotifPipeline = notifPipeline;
mNotifPipelineFlags = notifPipelineFlags;
mSysuiMainExecutor = sysuiMainExecutor;
+ mSysuiUiBgExecutor = sysuiUiBgExecutor;
mBarService = statusBarService == null
? IStatusBarService.Stub.asInterface(
@@ -208,12 +219,11 @@
setupNotifPipeline();
- keyguardStateController.addCallback(new KeyguardStateController.Callback() {
+ // TODO(b/327410864): use KeyguardTransitionInteractor to listen for keyguard changes
+ mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onKeyguardShowingChanged() {
- boolean isUnlockedShade = !keyguardStateController.isShowing()
- && !isDreamingOrInPreview();
- bubbles.onStatusBarStateChanged(isUnlockedShade);
+ updateKeyguardAndDreamingState();
}
});
@@ -256,6 +266,14 @@
mPanelExpanded = panelExpanded;
mBubbles.onNotificationPanelExpandedChanged(panelExpanded);
}
+ if (!mKeyguardShowing && mDreamingOrInPreview && !isDreaming) {
+ // We check for dreaming state changes when keyguard status changes.
+ // This causes us to miss events if dreaming state changes after keyguard.
+ // Add a check here for the case where keyguard is dismissed before
+ // dreaming state changes. Otherwise bubbles remain invisible.
+ // TODO(b/327410864): use KeyguardTransitionInteractor for dreaming changes
+ updateKeyguardAndDreamingState();
+ }
};
notificationShadeWindowController.registerCallback(mStatusBarWindowCallback);
@@ -395,6 +413,19 @@
mBubbles.setSysuiProxy(mSysuiProxy);
}
+ private void updateKeyguardAndDreamingState() {
+ mSysuiUiBgExecutor.execute(() -> {
+ mKeyguardShowing = mKeyguardStateController.isShowing();
+ mDreamingOrInPreview = isDreamingOrInPreview();
+ boolean isUnlockedShade = !mKeyguardShowing && !mDreamingOrInPreview;
+ ProtoLog.d(WM_SHELL_BUBBLES,
+ "handleKeyguardOrDreamChange isUnlockedShade=%b keyguardShowing=%b "
+ + "dreamingOrInPreview=%b",
+ isUnlockedShade, mKeyguardShowing, mDreamingOrInPreview);
+ mBubbles.onStatusBarStateChanged(isUnlockedShade);
+ });
+ }
+
private boolean isDreamingOrInPreview() {
try {
return mDreamManager.isDreamingOrInPreview();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 3026966..0f8a813 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -336,48 +336,6 @@
}
@Test
- fun listenForTransitionToAodFromGone_updatesClockDozeAmountToOne() =
- runBlocking(IMMEDIATE) {
- val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.AOD))
- .thenReturn(transitionStep)
-
- val job = underTest.listenForAnyStateToAodTransition(this)
- transitionStep.value =
- TransitionStep(
- from = KeyguardState.GONE,
- to = KeyguardState.AOD,
- transitionState = TransitionState.STARTED,
- )
- yield()
-
- verify(animations, times(2)).doze(1f)
-
- job.cancel()
- }
-
- @Test
- fun listenForTransitionToAodFromLockscreen_neverUpdatesClockDozeAmount() =
- runBlocking(IMMEDIATE) {
- val transitionStep = MutableStateFlow(TransitionStep())
- whenever(keyguardTransitionInteractor.transitionStepsToState(KeyguardState.AOD))
- .thenReturn(transitionStep)
-
- val job = underTest.listenForAnyStateToAodTransition(this)
- transitionStep.value =
- TransitionStep(
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.AOD,
- transitionState = TransitionState.STARTED,
- )
- yield()
-
- verify(animations, never()).doze(1f)
-
- job.cancel()
- }
-
- @Test
fun unregisterListeners_validate() =
runBlocking(IMMEDIATE) {
underTest.unregisterListeners()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
index d4522d0..93e7602 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java
@@ -19,11 +19,14 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.hardware.biometrics.BiometricSourceType;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -119,4 +122,51 @@
when(mKeyguardMessageArea.getText()).thenReturn(msg);
assertThat(mMessageAreaController.getMessage()).isEqualTo(msg);
}
+
+ @Test
+ public void testFingerprintMessageUpdate() {
+ String msg = "fpMessage";
+ mMessageAreaController.setMessage(
+ msg, BiometricSourceType.FINGERPRINT
+ );
+ verify(mKeyguardMessageArea).setMessage(msg, /* animate= */ true);
+
+ String msg2 = "fpMessage2";
+ mMessageAreaController.setMessage(
+ msg2, BiometricSourceType.FINGERPRINT
+ );
+ verify(mKeyguardMessageArea).setMessage(msg2, /* animate= */ true);
+ }
+
+ @Test
+ public void testFaceMessageDroppedWhileFingerprintMessageShowing() {
+ String fpMsg = "fpMessage";
+ mMessageAreaController.setMessage(
+ fpMsg, BiometricSourceType.FINGERPRINT
+ );
+ verify(mKeyguardMessageArea).setMessage(eq(fpMsg), /* animate= */ anyBoolean());
+
+ String faceMessage = "faceMessage";
+ mMessageAreaController.setMessage(
+ faceMessage, BiometricSourceType.FACE
+ );
+ verify(mKeyguardMessageArea, never())
+ .setMessage(eq(faceMessage), /* animate= */ anyBoolean());
+ }
+
+ @Test
+ public void testGenericMessageShowsAfterFingerprintMessageShowing() {
+ String fpMsg = "fpMessage";
+ mMessageAreaController.setMessage(
+ fpMsg, BiometricSourceType.FINGERPRINT
+ );
+ verify(mKeyguardMessageArea).setMessage(eq(fpMsg), /* animate= */ anyBoolean());
+
+ String genericMessage = "genericMessage";
+ mMessageAreaController.setMessage(
+ genericMessage, null
+ );
+ verify(mKeyguardMessageArea)
+ .setMessage(eq(genericMessage), /* animate= */ anyBoolean());
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 538daee..336a97e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -26,6 +26,8 @@
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
+import static android.telephony.SubscriptionManager.PROFILE_CLASS_DEFAULT;
+import static android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
@@ -187,6 +189,11 @@
TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "",
DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID,
TEST_CARRIER_ID, 0);
+ private static final SubscriptionInfo TEST_SUBSCRIPTION_PROVISIONING = new SubscriptionInfo(
+ 1, "", 0,
+ TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_CARRIER_ID, 0xFFFFFF, "",
+ DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID,
+ TEST_CARRIER_ID, PROFILE_CLASS_PROVISIONING);
private static final int FINGERPRINT_SENSOR_ID = 1;
@Mock
@@ -1204,7 +1211,8 @@
assertThat(mKeyguardUpdateMonitor.mSimDatas.get(TEST_SUBSCRIPTION.getSubscriptionId()))
.isNotNull();
- when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(null);
+ when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList())
+ .thenReturn(new ArrayList<>());
mKeyguardUpdateMonitor.mPhoneStateListener.onActiveDataSubscriptionIdChanged(
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
mTestableLooper.processAllMessages();
@@ -1216,6 +1224,37 @@
}
@Test
+ public void testActiveSubscriptionList_filtersProvisioningNetworks() {
+ List<SubscriptionInfo> list = new ArrayList<>();
+ list.add(TEST_SUBSCRIPTION_PROVISIONING);
+ when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(list);
+ mKeyguardUpdateMonitor.mSubscriptionListener.onSubscriptionsChanged();
+
+ assertThat(mKeyguardUpdateMonitor.getSubscriptionInfo(true)).isEmpty();
+
+ SubscriptionInfo.Builder b = new SubscriptionInfo.Builder(TEST_SUBSCRIPTION_PROVISIONING);
+ b.setProfileClass(PROFILE_CLASS_DEFAULT);
+ SubscriptionInfo validInfo = b.build();
+
+ list.clear();
+ list.add(validInfo);
+ mKeyguardUpdateMonitor.mSubscriptionListener.onSubscriptionsChanged();
+
+ assertThat(mKeyguardUpdateMonitor.getSubscriptionInfo(true)).hasSize(1);
+ }
+
+ @Test
+ public void testActiveSubscriptionList_filtersProvisioningNetworks_untilValid() {
+ List<SubscriptionInfo> list = new ArrayList<>();
+ list.add(TEST_SUBSCRIPTION_PROVISIONING);
+ when(mSubscriptionManager.getCompleteActiveSubscriptionInfoList()).thenReturn(list);
+ mKeyguardUpdateMonitor.mSubscriptionListener.onSubscriptionsChanged();
+
+ assertThat(mKeyguardUpdateMonitor.getSubscriptionInfo(true)).isEmpty();
+
+ }
+
+ @Test
public void testIsUserUnlocked() {
// mUserManager will report the user as unlocked on @Before
assertThat(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 2c1a87d..10b86ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -21,8 +21,8 @@
import android.hardware.biometrics.BiometricConstants
import android.hardware.biometrics.BiometricManager
import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT
-import android.hardware.biometrics.Flags.customBiometricPrompt
import android.hardware.biometrics.PromptInfo
+import android.hardware.biometrics.PromptVerticalListContentView
import android.hardware.face.FaceSensorPropertiesInternal
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.os.Handler
@@ -40,7 +40,6 @@
import androidx.test.filters.SmallTest
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.widget.LockPatternUtils
-import com.android.systemui.Flags.FLAG_CONSTRAINT_BP
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
@@ -148,8 +147,6 @@
@Before
fun setup() {
- mSetFlagsRule.disableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
- mSetFlagsRule.disableFlags(FLAG_CONSTRAINT_BP)
displayRepository = FakeDisplayRepository()
displayStateInteractor =
@@ -388,9 +385,10 @@
}
@Test
- fun testShowCredentialUI() {
+ fun testShowCredentialUI_withDescription() {
+ mSetFlagsRule.disableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
)
waitForIdleSync()
@@ -399,16 +397,12 @@
}
@Test
- fun testShowBiometricUIWhenCustomBpEnabledAndNoSensors() {
- mSetFlagsRule.enableFlags(FLAG_CUSTOM_BIOMETRIC_PROMPT)
+ fun testShowCredentialUI_withCustomBp() {
val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
+ isUsingContentView = true
)
- waitForIdleSync()
-
- assertThat(customBiometricPrompt()).isTrue()
- assertThat(container.hasBiometricPrompt()).isTrue()
- assertThat(container.hasCredentialView()).isFalse()
+ checkBpShowsForCredentialAndGoToCredential(container)
}
@Test
@@ -512,11 +506,13 @@
private fun initializeFingerprintContainer(
authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
- addToView: Boolean = true
+ addToView: Boolean = true,
+ isUsingContentView: Boolean = false,
) = initializeContainer(
TestAuthContainerView(
authenticators = authenticators,
- fingerprintProps = fingerprintSensorPropertiesInternal()
+ fingerprintProps = fingerprintSensorPropertiesInternal(),
+ isUsingContentView = isUsingContentView,
),
addToView
)
@@ -549,7 +545,8 @@
private inner class TestAuthContainerView(
authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
fingerprintProps: List<FingerprintSensorPropertiesInternal> = listOf(),
- faceProps: List<FaceSensorPropertiesInternal> = listOf()
+ faceProps: List<FaceSensorPropertiesInternal> = listOf(),
+ isUsingContentView: Boolean = false,
) : AuthContainerView(
Config().apply {
mContext = this@AuthContainerViewTest.context
@@ -559,6 +556,9 @@
mSkipAnimation = true
mPromptInfo = PromptInfo().apply {
this.authenticators = authenticators
+ if (isUsingContentView) {
+ this.contentView = PromptVerticalListContentView.Builder().build()
+ }
}
mOpPackageName = OP_PACKAGE_NAME
},
@@ -614,6 +614,17 @@
val layoutParams = AuthContainerView.getLayoutParams(windowToken, "")
assertThat((layoutParams.fitInsetsTypes and WindowInsets.Type.systemBars()) == 0).isTrue()
}
+
+ private fun checkBpShowsForCredentialAndGoToCredential(container: TestAuthContainerView) {
+ waitForIdleSync()
+ assertThat(container.hasBiometricPrompt()).isTrue()
+ assertThat(container.hasCredentialView()).isFalse()
+
+ container.animateToCredentialUI(false)
+ waitForIdleSync()
+ assertThat(container.hasBiometricPrompt()).isFalse()
+ assertThat(container.hasCredentialView()).isTrue()
+ }
}
private fun AuthContainerView.hasBiometricPrompt() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
index b39e09d..7b972d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/PromptRepositoryImplTest.kt
@@ -16,7 +16,9 @@
package com.android.systemui.biometrics.data.repository
+import android.hardware.biometrics.BiometricManager
import android.hardware.biometrics.PromptInfo
+import android.hardware.biometrics.PromptVerticalListContentView
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
@@ -131,6 +133,52 @@
}
@Test
+ fun showBpWithoutIconForCredential_withCustomBp() =
+ testScope.runTest {
+ for (case in
+ listOf(
+ PromptKind.Biometric(),
+ PromptKind.Pin,
+ PromptKind.Password,
+ PromptKind.Pattern
+ )) {
+ val hasCredentialViewShown = case !is PromptKind.Biometric
+ val promptInfo =
+ PromptInfo().apply {
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ contentView = PromptVerticalListContentView.Builder().build()
+ }
+ repository.setPrompt(promptInfo, USER_ID, CHALLENGE, case, OP_PACKAGE_NAME)
+ repository.setShouldShowBpWithoutIconForCredential(promptInfo)
+
+ assertThat(repository.showBpWithoutIconForCredential.value)
+ .isEqualTo(!hasCredentialViewShown)
+ }
+ }
+
+ @Test
+ fun showBpWithoutIconForCredential_withDescription() =
+ testScope.runTest {
+ for (case in
+ listOf(
+ PromptKind.Biometric(),
+ PromptKind.Pin,
+ PromptKind.Password,
+ PromptKind.Pattern
+ )) {
+ val promptInfo =
+ PromptInfo().apply {
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ description = "description"
+ }
+ repository.setPrompt(promptInfo, USER_ID, CHALLENGE, case, OP_PACKAGE_NAME)
+ repository.setShouldShowBpWithoutIconForCredential(promptInfo)
+
+ assertThat(repository.showBpWithoutIconForCredential.value).isFalse()
+ }
+ }
+
+ @Test
fun setsAndUnsetsPrompt() =
testScope.runTest {
val kind = PromptKind.Pin
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
index 52b4275..2817780 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
@@ -147,7 +147,7 @@
testScope.runTest { useCredentialAndReset(Utils.CREDENTIAL_PIN) }
@Test
- fun usePattermCredentialAndReset() =
+ fun usePatternCredentialAndReset() =
testScope.runTest { useCredentialAndReset(Utils.CREDENTIAL_PATTERN) }
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
index 6d8e7aa..6aebe36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
@@ -126,7 +126,7 @@
keyguardRepository.setKeyguardDismissible(true)
runCurrent()
shadeRepository.setCurrentFling(
- FlingInfo(expand = true) // Not a dismiss fling (expand = true).
+ FlingInfo(expand = false) // Is a dismiss fling upward (expand = false).
)
runCurrent()
@@ -153,4 +153,22 @@
assertThatRepository(transitionRepository).noTransitionsStarted()
}
+
+ @Test
+ @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ fun testDoesNotTransitionToGone_whenDismissFling_emitsNull() =
+ testScope.runTest {
+ underTest.start()
+ verify(transitionRepository, never()).startTransition(any())
+
+ keyguardRepository.setKeyguardDismissible(true)
+ runCurrent()
+
+ // The fling is null when it a) initializes b) ends and in either case we should not
+ // swipe to unlock.
+ shadeRepository.setCurrentFling(null)
+ runCurrent()
+
+ assertThatRepository(transitionRepository).noTransitionsStarted()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index aa53558..1504d4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -92,6 +92,21 @@
@TestableLooper.RunWithLooper
public class KeyguardIndicationControllerTest extends KeyguardIndicationControllerBaseTest {
@Test
+ public void afterFaceLockout_skipShowingFaceNotRecognized() {
+ createController();
+ onFaceLockoutError("lockout");
+ verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE, "lockout");
+ clearInvocations(mRotateTextViewController);
+
+ // WHEN face sends an onBiometricHelp BIOMETRIC_HELP_FACE_NOT_RECOGNIZED (face fail)
+ mKeyguardUpdateMonitorCallback.onBiometricHelp(
+ BIOMETRIC_HELP_FACE_NOT_RECOGNIZED,
+ "Face not recognized",
+ BiometricSourceType.FACE);
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE); // no updated message
+ }
+
+ @Test
public void createController_setIndicationAreaAgain_destroysPreviousRotateTextViewController() {
// GIVEN a controller with a mocked rotate text view controlller
final KeyguardIndicationRotateTextViewController mockedRotateTextViewController =
@@ -593,7 +608,7 @@
mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
"A message", BiometricSourceType.FACE);
- verify(mStatusBarKeyguardViewManager).setKeyguardMessage(eq(message), any());
+ verify(mStatusBarKeyguardViewManager).setKeyguardMessage(eq(message), any(), any());
}
@Test
@@ -608,7 +623,8 @@
mController.getKeyguardCallback().onBiometricError(FACE_ERROR_TIMEOUT,
"A message", BiometricSourceType.FACE);
- verify(mStatusBarKeyguardViewManager, never()).setKeyguardMessage(eq(message), any());
+ verify(mStatusBarKeyguardViewManager, never()).setKeyguardMessage(
+ eq(message), any(), any());
}
@Test
@@ -1242,7 +1258,7 @@
public void onBiometricFailed_resetFaceHelpMessageDeferral() {
createController();
- // WHEN face sends an onBiometricHelp BIOMETRIC_HELP_FACE_NOT_RECOGNIZED
+ // WHEN face sends an onBiometricAuthFailed
mKeyguardUpdateMonitorCallback.onBiometricAuthFailed(BiometricSourceType.FACE);
// THEN face help message deferral is reset
@@ -1331,7 +1347,9 @@
verify(mStatusBarKeyguardViewManager)
.setKeyguardMessage(
eq(mContext.getString(R.string.keyguard_face_unlock_unavailable)),
- any());
+ any(),
+ any()
+ );
}
@Test
@@ -1471,6 +1489,71 @@
mContext.getString(R.string.keyguard_suggest_fingerprint));
}
+ @Test
+ public void faceErrorMessageDroppedBecauseFingerprintMessageShowing() {
+ createController();
+ mController.setVisible(true);
+ mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+ "fp not recognized", BiometricSourceType.FINGERPRINT);
+ clearInvocations(mRotateTextViewController);
+
+ onFaceLockoutError("lockout");
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+ }
+
+ @Test
+ public void faceUnlockedMessageShowsEvenWhenFingerprintMessageShowing() {
+ createController();
+ mController.setVisible(true);
+ mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+ "fp not recognized", BiometricSourceType.FINGERPRINT);
+ clearInvocations(mRotateTextViewController);
+
+ when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true);
+ when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(getCurrentUser()))
+ .thenReturn(true);
+ mController.getKeyguardCallback().onBiometricAuthenticated(0,
+ BiometricSourceType.FACE, false);
+ verifyIndicationMessage(
+ INDICATION_TYPE_BIOMETRIC_MESSAGE,
+ mContext.getString(R.string.keyguard_face_successful_unlock));
+ }
+
+ @Test
+ public void onTrustAgentErrorMessageDroppedBecauseFingerprintMessageShowing() {
+ createController();
+ mController.setVisible(true);
+ mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+ "fp not recognized", BiometricSourceType.FINGERPRINT);
+ clearInvocations(mRotateTextViewController);
+
+ mKeyguardUpdateMonitorCallback.onTrustAgentErrorMessage("testMessage");
+ verifyNoMessage(INDICATION_TYPE_TRUST);
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+ }
+
+ @Test
+ public void trustGrantedMessageShowsEvenWhenFingerprintMessageShowing() {
+ createController();
+ mController.setVisible(true);
+ mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
+ "fp not recognized", BiometricSourceType.FINGERPRINT);
+ clearInvocations(mRotateTextViewController);
+
+ // GIVEN trust is granted
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(true);
+
+ // WHEN the showTrustGranted method is called
+ final String trustGrantedMsg = "testing trust granted message after fp message";
+ mController.getKeyguardCallback().onTrustGrantedForCurrentUser(
+ false, false, new TrustGrantFlags(0), trustGrantedMsg);
+
+ // THEN verify the trust granted message shows
+ verifyIndicationMessage(
+ INDICATION_TYPE_TRUST,
+ trustGrantedMsg);
+ }
+
private void screenIsTurningOn() {
when(mScreenLifecycle.getScreenState()).thenReturn(SCREEN_TURNING_ON);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index 5e8b62e..fd2dead 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -26,6 +26,7 @@
import android.view.MotionEvent
import android.view.PrivacyIndicatorBounds
import android.view.RoundedCorners
+import android.view.View
import android.view.WindowInsets
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
@@ -50,6 +51,8 @@
class PhoneStatusBarViewTest : SysuiTestCase() {
private lateinit var view: PhoneStatusBarView
+ private val systemIconsContainer: View
+ get() = view.requireViewById(R.id.system_icons)
private val contentInsetsProvider = mock<StatusBarContentInsetsProvider>()
private val windowController = mock<StatusBarWindowController>()
@@ -62,6 +65,7 @@
)
mDependency.injectTestDependency(DarkIconDispatcher::class.java, mock<DarkIconDispatcher>())
mDependency.injectTestDependency(StatusBarWindowController::class.java, windowController)
+ context.ensureTestableResources()
view = spy(createStatusBarView())
whenever(view.rootWindowInsets).thenReturn(emptyWindowInsets())
whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
@@ -217,7 +221,7 @@
val newInsets = Insets.NONE
whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(newInsets)
+ .thenReturn(newInsets)
view.onConfigurationChanged(Configuration())
assertThat(view.paddingLeft).isEqualTo(previousInsets.left)
@@ -239,7 +243,7 @@
val newInsets = Insets.NONE
whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(newInsets)
+ .thenReturn(newInsets)
configuration.densityDpi = 456
view.onConfigurationChanged(configuration)
@@ -262,7 +266,7 @@
val newInsets = Insets.NONE
whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
- .thenReturn(newInsets)
+ .thenReturn(newInsets)
configuration.fontScale = 2f
view.onConfigurationChanged(configuration)
@@ -273,6 +277,19 @@
}
@Test
+ fun onConfigurationChanged_systemIconsHeightChanged_containerHeightIsUpdated() {
+ val newHeight = 123456
+ context.orCreateTestableResources.addOverride(
+ R.dimen.status_bar_system_icons_height,
+ newHeight
+ )
+
+ view.onConfigurationChanged(Configuration())
+
+ assertThat(systemIconsContainer.layoutParams.height).isEqualTo(newHeight)
+ }
+
+ @Test
fun onApplyWindowInsets_updatesLeftTopRightPaddingsBasedOnInsets() {
val insets = Insets.of(/* left = */ 90, /* top = */ 10, /* right = */ 45, /* bottom = */ 50)
whenever(contentInsetsProvider.getStatusBarContentInsetsForCurrentRotation())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index bd7406a..3666248 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -97,6 +97,7 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
+import com.android.systemui.util.kotlin.JavaAdapter;
import com.google.common.truth.Truth;
@@ -222,7 +223,8 @@
() -> mock(WindowManagerLockscreenVisibilityInteractor.class),
() -> mock(KeyguardDismissActionInteractor.class),
mSelectedUserInteractor,
- () -> mock(KeyguardSurfaceBehindInteractor.class)) {
+ () -> mock(KeyguardSurfaceBehindInteractor.class),
+ mock(JavaAdapter.class)) {
@Override
public ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
@@ -730,7 +732,8 @@
() -> mock(WindowManagerLockscreenVisibilityInteractor.class),
() -> mock(KeyguardDismissActionInteractor.class),
mSelectedUserInteractor,
- () -> mock(KeyguardSurfaceBehindInteractor.class)) {
+ () -> mock(KeyguardSurfaceBehindInteractor.class),
+ mock(JavaAdapter.class)) {
@Override
public ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index a930860..fbefb0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -609,6 +609,7 @@
mSysUiState,
mFeatureFlags,
mNotifPipelineFlags,
+ syncExecutor,
syncExecutor);
mBubblesManager.addNotifCallback(mNotifCallback);
@@ -651,7 +652,7 @@
}
@Test
- public void dreamingHidesBubbles() throws RemoteException {
+ public void bubblesHiddenWhileDreaming() throws RemoteException {
mBubbleController.updateBubble(mBubbleEntry);
assertTrue(mBubbleController.hasBubbles());
assertThat(mBubbleController.getStackView().getVisibility()).isEqualTo(View.VISIBLE);
@@ -662,7 +663,17 @@
mKeyguardStateControllerCallbackCaptor.getValue();
callback.onKeyguardShowingChanged();
+ // Dreaming should hide bubbles
assertThat(mBubbleController.getStackView().getVisibility()).isEqualTo(View.INVISIBLE);
+
+ // Finish dreaming should show bubbles
+ mNotificationShadeWindowController.setDreaming(false);
+ when(mIDreamManager.isDreamingOrInPreview()).thenReturn(false); // dreaming finished
+
+ // Dreaming updates come through mNotificationShadeWindowController
+ mNotificationShadeWindowController.notifyStateChangedCallbacks();
+
+ assertThat(mBubbleController.getStackView().getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
index f192de2..c3af437 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakePromptRepository.kt
@@ -1,6 +1,8 @@
package com.android.systemui.biometrics.data.repository
+import android.hardware.biometrics.Flags
import android.hardware.biometrics.PromptInfo
+import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.shared.model.PromptKind
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -26,6 +28,9 @@
private val _isConfirmationRequired = MutableStateFlow(false)
override val isConfirmationRequired = _isConfirmationRequired.asStateFlow()
+ private val _showBpWithoutIconForCredential = MutableStateFlow(false)
+ override val showBpWithoutIconForCredential = _showBpWithoutIconForCredential.asStateFlow()
+
private val _opPackageName: MutableStateFlow<String?> = MutableStateFlow(null)
override val opPackageName = _opPackageName.asStateFlow()
@@ -69,6 +74,16 @@
_isConfirmationRequired.value = false
}
+ override fun setShouldShowBpWithoutIconForCredential(promptInfo: PromptInfo) {
+ val hasCredentialViewShown = kind.value !is PromptKind.Biometric
+ val showBpForCredential =
+ Flags.customBiometricPrompt() &&
+ !Utils.isBiometricAllowed(promptInfo) &&
+ Utils.isDeviceCredentialAllowed(promptInfo) &&
+ promptInfo.contentView != null
+ _showBpWithoutIconForCredential.value = showBpForCredential && !hasCredentialViewShown
+ }
+
fun setIsShowing(showing: Boolean) {
_isShowing.value = showing
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index ecf66a2..8ca53e6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -42,6 +42,10 @@
aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
glanceableHubToLockscreenTransitionViewModel = glanceableHubToLockscreenTransitionViewModel,
+ goneToAodTransitionViewModel = goneToAodTransitionViewModel,
+ goneToDozingTransitionViewModel = goneToDozingTransitionViewModel,
+ lockscreenToAodTransitionViewModel = lockscreenToAodTransitionViewModel,
+ lockscreenToDozingTransitionViewModel = lockscreenToDozingTransitionViewModel,
lockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel,
lockscreenToGlanceableHubTransitionViewModel = lockscreenToGlanceableHubTransitionViewModel,
lockscreenToGoneTransitionViewModel = lockscreenToGoneTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
index 96de4ba..8da5dd4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
@@ -20,6 +20,7 @@
import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.policy.splitShadeStateController
val Kosmos.lockscreenContentViewModel by
Kosmos.Fixture {
@@ -28,5 +29,6 @@
interactor = keyguardBlueprintInteractor,
authController = authController,
longPress = keyguardLongPressViewModel,
+ splitShadeStateController = splitShadeStateController,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
new file mode 100644
index 0000000..df6fc41
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettingsKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.settings
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.fakeGlobalSettings: FakeGlobalSettings by Fixture { FakeGlobalSettings() }
diff --git a/proto/src/criticalevents/critical_event_log.proto b/proto/src/criticalevents/critical_event_log.proto
index cffcd09..71d291a 100644
--- a/proto/src/criticalevents/critical_event_log.proto
+++ b/proto/src/criticalevents/critical_event_log.proto
@@ -61,6 +61,12 @@
NativeCrash native_crash = 6;
SystemServerStarted system_server_started = 7;
InstallPackages install_packages = 8;
+ ExcessiveBinderCalls excessive_binder_calls = 9;
+ }
+
+ message ExcessiveBinderCalls {
+ // The uid sending many calls.
+ optional int32 uid = 1;
}
message InstallPackages {}
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index d0eb59d..1dab40e 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -25,12 +25,12 @@
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
-import static com.android.server.companion.MetricUtils.logCreateAssociation;
-import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature;
-import static com.android.server.companion.PermissionsUtils.enforcePermissionsForAssociation;
-import static com.android.server.companion.RolesUtils.addRoleHolderForAssociation;
-import static com.android.server.companion.RolesUtils.isRoleHolder;
-import static com.android.server.companion.Utils.prepareForIpc;
+import static com.android.server.companion.utils.MetricUtils.logCreateAssociation;
+import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature;
+import static com.android.server.companion.utils.PermissionsUtils.enforcePermissionForCreatingAssociation;
+import static com.android.server.companion.utils.RolesUtils.addRoleHolderForAssociation;
+import static com.android.server.companion.utils.RolesUtils.isRoleHolder;
+import static com.android.server.companion.utils.Utils.prepareForIpc;
import static java.util.Objects.requireNonNull;
@@ -59,6 +59,7 @@
import android.util.Slog;
import com.android.internal.R;
+import com.android.server.companion.utils.PackageUtils;
import java.util.List;
@@ -167,7 +168,7 @@
}
// 1. Enforce permissions and other requirements.
- enforcePermissionsForAssociation(mContext, request, packageUid);
+ enforcePermissionForCreatingAssociation(mContext, request, packageUid);
enforceUsesCompanionDeviceFeature(mContext, userId, packageName);
// 2a. Check if association can be created without launching UI (i.e. CDM needs NEITHER
@@ -257,7 +258,7 @@
// 1. Need to check permissions again in case something changed, since we first received
// this request.
try {
- enforcePermissionsForAssociation(mContext, request, packageUid);
+ enforcePermissionForCreatingAssociation(mContext, request, packageUid);
} catch (SecurityException e) {
// Since, at this point the caller is our own UI, we need to catch the exception on
// forward it back to the application via the callback.
@@ -316,6 +317,9 @@
// If it is null, then the operation will succeed without granting any role.
addRoleHolderForAssociation(mService.getContext(), association, success -> {
if (success) {
+ Slog.i(TAG, "Added " + association.getDeviceProfile() + " role to userId="
+ + association.getUserId() + ", packageName="
+ + association.getPackageName());
addAssociationToStore(association);
sendCallbackAndFinish(association, callback, resultReceiver);
} else {
diff --git a/services/companion/java/com/android/server/companion/AssociationRevokeProcessor.java b/services/companion/java/com/android/server/companion/AssociationRevokeProcessor.java
index de6382e..10963ea 100644
--- a/services/companion/java/com/android/server/companion/AssociationRevokeProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRevokeProcessor.java
@@ -20,8 +20,8 @@
import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION;
import static com.android.internal.util.CollectionUtils.any;
-import static com.android.server.companion.MetricUtils.logRemoveAssociation;
-import static com.android.server.companion.RolesUtils.removeRoleHolderForAssociation;
+import static com.android.server.companion.utils.MetricUtils.logRemoveAssociation;
+import static com.android.server.companion.utils.RolesUtils.removeRoleHolderForAssociation;
import static com.android.server.companion.CompanionDeviceManagerService.PerUserAssociationSet;
import android.annotation.NonNull;
@@ -203,7 +203,8 @@
return false;
}
- removeRoleHolderForAssociation(mContext, association);
+ removeRoleHolderForAssociation(mContext, association.getUserId(),
+ association.getPackageName(), association.getDeviceProfile());
return true;
}
diff --git a/services/companion/java/com/android/server/companion/CompanionApplicationController.java b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
index af0777c..559ebbc 100644
--- a/services/companion/java/com/android/server/companion/CompanionApplicationController.java
+++ b/services/companion/java/com/android/server/companion/CompanionApplicationController.java
@@ -38,6 +38,9 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.PerUser;
import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
+import com.android.server.companion.presence.ObservableUuid;
+import com.android.server.companion.presence.ObservableUuidStore;
+import com.android.server.companion.utils.PackageUtils;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -61,7 +64,7 @@
* <ul>
* <li> {@link #bindCompanionApplication(int, String, boolean)}
* <li> {@link #unbindCompanionApplication(int, String)}
- * <li> {@link #notifyCompanionApplicationDevicePresenceEvent(AssociationInfo, int)}
+ * <li> {@link #notifyCompanionDevicePresenceEvent(AssociationInfo, int)}
* <li> {@link #isCompanionApplicationBound(int, String)}
* <li> {@link #isRebindingCompanionApplicationScheduled(int, String)}
* </ul>
@@ -251,7 +254,13 @@
serviceConnector.connect();
}
- void notifyCompanionApplicationDeviceAppeared(AssociationInfo association) {
+ /**
+ * Notify the app that the device appeared.
+ *
+ * @deprecated use {@link #notifyCompanionDevicePresenceEvent(AssociationInfo, int)} instead
+ */
+ @Deprecated
+ public void notifyCompanionApplicationDeviceAppeared(AssociationInfo association) {
final int userId = association.getUserId();
final String packageName = association.getPackageName();
@@ -273,7 +282,13 @@
primaryServiceConnector.postOnDeviceAppeared(association);
}
- void notifyCompanionApplicationDeviceDisappeared(AssociationInfo association) {
+ /**
+ * Notify the app that the device disappeared.
+ *
+ * @deprecated use {@link #notifyCompanionDevicePresenceEvent(AssociationInfo, int)} instead
+ */
+ @Deprecated
+ public void notifyCompanionApplicationDeviceDisappeared(AssociationInfo association) {
final int userId = association.getUserId();
final String packageName = association.getPackageName();
@@ -295,7 +310,10 @@
primaryServiceConnector.postOnDeviceDisappeared(association);
}
- void notifyCompanionApplicationDevicePresenceEvent(AssociationInfo association, int event) {
+ /**
+ * Notify the app that the device appeared.
+ */
+ public void notifyCompanionDevicePresenceEvent(AssociationInfo association, int event) {
final int userId = association.getUserId();
final String packageName = association.getPackageName();
final CompanionDeviceServiceConnector primaryServiceConnector =
@@ -318,7 +336,10 @@
primaryServiceConnector.postOnDevicePresenceEvent(devicePresenceEvent);
}
- void notifyApplicationDevicePresenceEvent(ObservableUuid uuid, int event) {
+ /**
+ * Notify the app that the device disappeared.
+ */
+ public void notifyUuidDevicePresenceEvent(ObservableUuid uuid, int event) {
final int userId = uuid.getUserId();
final ParcelUuid parcelUuid = uuid.getUuid();
final String packageName = uuid.getPackageName();
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 09c7793..a478a3d 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -38,15 +38,15 @@
import static com.android.internal.util.Preconditions.checkState;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.companion.AssociationStore.CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED;
-import static com.android.server.companion.PackageUtils.isRestrictedSettingsAllowed;
-import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature;
-import static com.android.server.companion.PackageUtils.getPackageInfo;
-import static com.android.server.companion.PermissionsUtils.checkCallerCanManageCompanionDevice;
-import static com.android.server.companion.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
-import static com.android.server.companion.PermissionsUtils.enforceCallerCanObservingDevicePresenceByUuid;
-import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOr;
-import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId;
-import static com.android.server.companion.PermissionsUtils.sanitizeWithCallerChecks;
+import static com.android.server.companion.utils.PackageUtils.isRestrictedSettingsAllowed;
+import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature;
+import static com.android.server.companion.utils.PackageUtils.getPackageInfo;
+import static com.android.server.companion.utils.PermissionsUtils.checkCallerCanManageCompanionDevice;
+import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
+import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanObservingDevicePresenceByUuid;
+import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOr;
+import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId;
+import static com.android.server.companion.utils.PermissionsUtils.sanitizeWithCallerChecks;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.DAYS;
@@ -123,6 +123,8 @@
import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncController;
import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncControllerCallback;
import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
+import com.android.server.companion.presence.ObservableUuid;
+import com.android.server.companion.presence.ObservableUuidStore;
import com.android.server.companion.transport.CompanionTransportManager;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -435,7 +437,7 @@
bindApplicationIfNeeded(association);
- mCompanionAppController.notifyCompanionApplicationDevicePresenceEvent(
+ mCompanionAppController.notifyCompanionDevicePresenceEvent(
association, event);
break;
case EVENT_BLE_DISAPPEARED:
@@ -446,7 +448,7 @@
return;
}
if (association.shouldBindWhenPresent()) {
- mCompanionAppController.notifyCompanionApplicationDevicePresenceEvent(
+ mCompanionAppController.notifyCompanionDevicePresenceEvent(
association, event);
}
// Check if there are other devices associated to the app that are present.
@@ -475,7 +477,7 @@
Log.i(TAG, "u" + userId + "\\" + packageName + " is already bound");
}
- mCompanionAppController.notifyApplicationDevicePresenceEvent(uuid, event);
+ mCompanionAppController.notifyUuidDevicePresenceEvent(uuid, event);
break;
case EVENT_BT_DISCONNECTED:
@@ -484,7 +486,7 @@
return;
}
- mCompanionAppController.notifyApplicationDevicePresenceEvent(uuid, event);
+ mCompanionAppController.notifyUuidDevicePresenceEvent(uuid, event);
// Check if there are other devices associated to the app or the UUID to be
// observed are present.
if (shouldBindPackage(userId, packageName)) return;
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index de4f2b6..74b4cab 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -18,7 +18,7 @@
import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_CONTEXT_SYNC;
-import static com.android.server.companion.PermissionsUtils.sanitizeWithCallerChecks;
+import static com.android.server.companion.utils.PermissionsUtils.sanitizeWithCallerChecks;
import android.companion.AssociationInfo;
import android.companion.ContextSyncMessage;
@@ -36,6 +36,7 @@
import com.android.server.companion.datatransfer.contextsync.BitmapUtils;
import com.android.server.companion.datatransfer.contextsync.CrossDeviceSyncController;
import com.android.server.companion.presence.CompanionDevicePresenceMonitor;
+import com.android.server.companion.presence.ObservableUuid;
import com.android.server.companion.transport.CompanionTransportManager;
import java.io.PrintWriter;
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index 1ebe65c..7527efb 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -27,11 +27,11 @@
import static com.android.internal.util.XmlUtils.writeStringAttribute;
import static com.android.server.companion.CompanionDeviceManagerService.getFirstAssociationIdForUser;
import static com.android.server.companion.CompanionDeviceManagerService.getLastAssociationIdForUser;
-import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
-import static com.android.server.companion.DataStoreUtils.fileToByteArray;
-import static com.android.server.companion.DataStoreUtils.isEndOfTag;
-import static com.android.server.companion.DataStoreUtils.isStartOfTag;
-import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
+import static com.android.server.companion.utils.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.utils.DataStoreUtils.fileToByteArray;
+import static com.android.server.companion.utils.DataStoreUtils.isEndOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.isStartOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.writeToFileSafely;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -51,6 +51,7 @@
import com.android.internal.util.XmlUtils;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
+import com.android.server.companion.utils.DataStoreUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index 260b21f..74236a4 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -23,7 +23,7 @@
import static android.content.ComponentName.createRelative;
import static android.content.pm.PackageManager.FEATURE_WATCH;
-import static com.android.server.companion.Utils.prepareForIpc;
+import static com.android.server.companion.utils.Utils.prepareForIpc;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -54,9 +54,9 @@
import com.android.internal.R;
import com.android.server.companion.AssociationStore;
import com.android.server.companion.CompanionDeviceManagerService;
-import com.android.server.companion.PackageUtils;
-import com.android.server.companion.PermissionsUtils;
import com.android.server.companion.transport.CompanionTransportManager;
+import com.android.server.companion.utils.PackageUtils;
+import com.android.server.companion.utils.PermissionsUtils;
import java.util.List;
import java.util.concurrent.ExecutorService;
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
index c4c80f9..ee816a0 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferRequestStore.java
@@ -22,11 +22,11 @@
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
-import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
-import static com.android.server.companion.DataStoreUtils.fileToByteArray;
-import static com.android.server.companion.DataStoreUtils.isEndOfTag;
-import static com.android.server.companion.DataStoreUtils.isStartOfTag;
-import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
+import static com.android.server.companion.utils.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.utils.DataStoreUtils.fileToByteArray;
+import static com.android.server.companion.utils.DataStoreUtils.isEndOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.isStartOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.writeToFileSafely;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
index 7e30790..6792434 100644
--- a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
+++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
@@ -34,7 +34,7 @@
import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_POWER;
import static com.android.server.companion.presence.CompanionDevicePresenceMonitor.DEBUG;
-import static com.android.server.companion.presence.Utils.btDeviceToString;
+import static com.android.server.companion.utils.Utils.btDeviceToString;
import static java.util.Objects.requireNonNull;
diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
index c514f3e..0287f62 100644
--- a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
+++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
@@ -20,7 +20,7 @@
import static android.companion.DevicePresenceEvent.EVENT_BT_DISCONNECTED;
import static com.android.server.companion.presence.CompanionDevicePresenceMonitor.DEBUG;
-import static com.android.server.companion.presence.Utils.btDeviceToString;
+import static com.android.server.companion.utils.Utils.btDeviceToString;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
@@ -40,8 +40,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.server.companion.AssociationStore;
-import com.android.server.companion.ObservableUuid;
-import com.android.server.companion.ObservableUuidStore;
import java.util.Arrays;
import java.util.Collections;
diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
index 54a4692..caca48d 100644
--- a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
+++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
@@ -43,8 +43,6 @@
import android.util.SparseArray;
import com.android.server.companion.AssociationStore;
-import com.android.server.companion.ObservableUuid;
-import com.android.server.companion.ObservableUuidStore;
import java.io.PrintWriter;
import java.util.HashSet;
diff --git a/services/companion/java/com/android/server/companion/ObservableUuid.java b/services/companion/java/com/android/server/companion/presence/ObservableUuid.java
similarity index 96%
rename from services/companion/java/com/android/server/companion/ObservableUuid.java
rename to services/companion/java/com/android/server/companion/presence/ObservableUuid.java
index 6ab3188..9cfa270 100644
--- a/services/companion/java/com/android/server/companion/ObservableUuid.java
+++ b/services/companion/java/com/android/server/companion/presence/ObservableUuid.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.presence;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
diff --git a/services/companion/java/com/android/server/companion/ObservableUuidStore.java b/services/companion/java/com/android/server/companion/presence/ObservableUuidStore.java
similarity index 93%
rename from services/companion/java/com/android/server/companion/ObservableUuidStore.java
rename to services/companion/java/com/android/server/companion/presence/ObservableUuidStore.java
index 94be22a..ee8b106 100644
--- a/services/companion/java/com/android/server/companion/ObservableUuidStore.java
+++ b/services/companion/java/com/android/server/companion/presence/ObservableUuidStore.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.presence;
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
@@ -22,10 +22,10 @@
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
-import static com.android.server.companion.DataStoreUtils.createStorageFileForUser;
-import static com.android.server.companion.DataStoreUtils.isEndOfTag;
-import static com.android.server.companion.DataStoreUtils.isStartOfTag;
-import static com.android.server.companion.DataStoreUtils.writeToFileSafely;
+import static com.android.server.companion.utils.DataStoreUtils.createStorageFileForUser;
+import static com.android.server.companion.utils.DataStoreUtils.isEndOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.isStartOfTag;
+import static com.android.server.companion.utils.DataStoreUtils.writeToFileSafely;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -57,6 +57,9 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+/**
+ * This store manages the cache and disk data for observable uuids.
+ */
public class ObservableUuidStore {
private static final String TAG = "CDM_ObservableUuidStore";
private static final String FILE_NAME = "observing_uuids_presence.xml";
@@ -84,9 +87,9 @@
}
/**
- * Remove the observable uuid from the disk.
+ * Remove the observable uuid.
*/
- void removeObservableUuid(@UserIdInt int userId, ParcelUuid uuid, String packageName) {
+ public void removeObservableUuid(@UserIdInt int userId, ParcelUuid uuid, String packageName) {
List<ObservableUuid> cachedObservableUuids;
synchronized (mLock) {
@@ -101,7 +104,10 @@
mExecutor.execute(() -> writeObservableUuidToStore(userId, cachedObservableUuids));
}
- void writeObservableUuid(@UserIdInt int userId, ObservableUuid uuid) {
+ /**
+ * Write the observable uuid.
+ */
+ public void writeObservableUuid(@UserIdInt int userId, ObservableUuid uuid) {
Slog.i(TAG, "Writing uuid=" + uuid.getUuid() + " to store.");
List<ObservableUuid> cachedObservableUuids;
diff --git a/services/companion/java/com/android/server/companion/presence/Utils.java b/services/companion/java/com/android/server/companion/presence/Utils.java
deleted file mode 100644
index 583b443..0000000
--- a/services/companion/java/com/android/server/companion/presence/Utils.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.companion.presence;
-
-import android.annotation.NonNull;
-import android.bluetooth.BluetoothDevice;
-
-/** Utilities for working with Bluetooth and BLE devices. */
-class Utils {
-
- /**
- * @return short String representation of {@link BluetoothDevice}.
- */
- static String btDeviceToString(@NonNull BluetoothDevice btDevice) {
- final StringBuilder sb = new StringBuilder(btDevice.getAddress());
-
- sb.append(" [name=");
- final String name = btDevice.getName();
- if (name != null) {
- sb.append('\'').append(name).append('\'');
- } else {
- sb.append("null");
- }
-
- final String alias = btDevice.getAlias();
- if (alias != null) {
- sb.append(", alias='").append(alias).append("'");
- }
-
- return sb.append(']').toString();
- }
-
- private Utils() {
- }
-}
diff --git a/services/companion/java/com/android/server/companion/DataStoreUtils.java b/services/companion/java/com/android/server/companion/utils/DataStoreUtils.java
similarity index 97%
rename from services/companion/java/com/android/server/companion/DataStoreUtils.java
rename to services/companion/java/com/android/server/companion/utils/DataStoreUtils.java
index 04ce1f6..c75b1a5 100644
--- a/services/companion/java/com/android/server/companion/DataStoreUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/DataStoreUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.utils;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -80,7 +80,7 @@
/**
* Writing to file could fail, for example, if the user has been recently removed and so was
- * their DE (/data/system_de/<user-id>/) directory.
+ * their DE (/data/system_de/[user-id]/) directory.
*/
public static void writeToFileSafely(
@NonNull AtomicFile file, @NonNull ThrowingConsumer<FileOutputStream> consumer) {
diff --git a/services/companion/java/com/android/server/companion/MetricUtils.java b/services/companion/java/com/android/server/companion/utils/MetricUtils.java
similarity index 92%
rename from services/companion/java/com/android/server/companion/MetricUtils.java
rename to services/companion/java/com/android/server/companion/utils/MetricUtils.java
index cf867b6..8ea5c89 100644
--- a/services/companion/java/com/android/server/companion/MetricUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/MetricUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.utils;
import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING;
import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION;
@@ -41,7 +41,7 @@
import java.util.Map;
-final class MetricUtils {
+public final class MetricUtils {
private static final Map<String, Integer> METRIC_DEVICE_PROFILE;
static {
@@ -75,13 +75,19 @@
METRIC_DEVICE_PROFILE = unmodifiableMap(map);
}
- static void logCreateAssociation(String profile) {
+ /**
+ * Log association creation
+ */
+ public static void logCreateAssociation(String profile) {
write(CDM_ASSOCIATION_ACTION,
CDM_ASSOCIATION_ACTION__ACTION__CREATED,
METRIC_DEVICE_PROFILE.get(profile));
}
- static void logRemoveAssociation(String profile) {
+ /**
+ * Log association removal
+ */
+ public static void logRemoveAssociation(String profile) {
write(CDM_ASSOCIATION_ACTION,
CDM_ASSOCIATION_ACTION__ACTION__REMOVED,
METRIC_DEVICE_PROFILE.get(profile));
diff --git a/services/companion/java/com/android/server/companion/PackageUtils.java b/services/companion/java/com/android/server/companion/utils/PackageUtils.java
similarity index 86%
rename from services/companion/java/com/android/server/companion/PackageUtils.java
rename to services/companion/java/com/android/server/companion/utils/PackageUtils.java
index 3aae1ec..d38590e 100644
--- a/services/companion/java/com/android/server/companion/PackageUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/PackageUtils.java
@@ -14,16 +14,13 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.utils;
import static android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP;
import static android.content.pm.PackageManager.GET_CONFIGURATIONS;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.os.Binder.getCallingUid;
-import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
-import static com.android.server.companion.CompanionDeviceManagerService.TAG;
-
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -44,13 +41,11 @@
import android.content.pm.Signature;
import android.os.Binder;
import android.os.Process;
-import android.util.Log;
import android.util.Slog;
import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -58,16 +53,22 @@
import java.util.Set;
/**
- * Utility methods for working with {@link PackageInfo}-s.
+ * Utility methods for working with {@link PackageInfo}.
*/
public final class PackageUtils {
+
+ private static final String TAG = "CDM_PackageUtils";
+
private static final Intent COMPANION_SERVICE_INTENT =
new Intent(CompanionDeviceService.SERVICE_INTERFACE);
private static final String PROPERTY_PRIMARY_TAG =
"android.companion.PROPERTY_PRIMARY_COMPANION_DEVICE_SERVICE";
+ /**
+ * Get package info
+ */
@Nullable
- static PackageInfo getPackageInfo(@NonNull Context context,
+ public static PackageInfo getPackageInfo(@NonNull Context context,
@UserIdInt int userId, @NonNull String packageName) {
final PackageManager pm = context.getPackageManager();
final PackageInfoFlags flags = PackageInfoFlags.of(GET_PERMISSIONS | GET_CONFIGURATIONS);
@@ -81,26 +82,32 @@
});
}
- static void enforceUsesCompanionDeviceFeature(@NonNull Context context,
+ /**
+ * Require the app to declare the companion device feature.
+ */
+ public static void enforceUsesCompanionDeviceFeature(@NonNull Context context,
@UserIdInt int userId, @NonNull String packageName) {
// Allow system server to create CDM associations without FEATURE_COMPANION_DEVICE_SETUP
if (getCallingUid() == Process.SYSTEM_UID) {
return;
}
- String requiredFeature = FEATURE_COMPANION_DEVICE_SETUP;
+ PackageInfo packageInfo = getPackageInfo(context, userId, packageName);
+ if (packageInfo == null) {
+ throw new IllegalArgumentException("Package " + packageName + " doesn't exist.");
+ }
- FeatureInfo[] requestedFeatures = getPackageInfo(context, userId, packageName).reqFeatures;
+ FeatureInfo[] requestedFeatures = packageInfo.reqFeatures;
if (requestedFeatures != null) {
- for (int i = 0; i < requestedFeatures.length; i++) {
- if (requiredFeature.equals(requestedFeatures[i].name)) {
+ for (FeatureInfo requestedFeature : requestedFeatures) {
+ if (FEATURE_COMPANION_DEVICE_SETUP.equals(requestedFeature.name)) {
return;
}
}
}
throw new IllegalStateException("Must declare uses-feature "
- + requiredFeature
+ + FEATURE_COMPANION_DEVICE_SETUP
+ " in manifest to use this API");
}
@@ -109,7 +116,7 @@
* Services marked as "primary" would always appear at the head of the lists, *before*
* all non-primary services.
*/
- static @NonNull Map<String, List<ComponentName>> getCompanionServicesForUser(
+ public static @NonNull Map<String, List<ComponentName>> getCompanionServicesForUser(
@NonNull Context context, @UserIdInt int userId) {
final PackageManager pm = context.getPackageManager();
final List<ResolveInfo> companionServices = pm.queryIntentServicesAsUser(
@@ -179,9 +186,7 @@
final String[] allowlistedPackages = context.getResources()
.getStringArray(com.android.internal.R.array.config_companionDevicePackages);
if (!ArrayUtils.contains(allowlistedPackages, packageName)) {
- if (DEBUG) {
- Log.d(TAG, packageName + " is not allowlisted.");
- }
+ Slog.d(TAG, packageName + " is not allowlisted.");
return false;
}
@@ -212,13 +217,6 @@
if (!requestingPackageSignatureAllowlisted) {
Slog.w(TAG, "Certificate mismatch for allowlisted package " + packageName);
- if (DEBUG) {
- Log.d(TAG, " > allowlisted signatures for " + packageName + ": ["
- + String.join(", ", allowlistedSignatureDigestsForRequestingPackage)
- + "]");
- Log.d(TAG, " > actual signatures for " + packageName + ": "
- + Arrays.toString(requestingPackageSignatureDigests));
- }
}
return requestingPackageSignatureAllowlisted;
diff --git a/services/companion/java/com/android/server/companion/PermissionsUtils.java b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
similarity index 80%
rename from services/companion/java/com/android/server/companion/PermissionsUtils.java
rename to services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
index 15bebba..2cf1f46 100644
--- a/services/companion/java/com/android/server/companion/PermissionsUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.utils;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_COMPANION_DEVICES;
@@ -75,16 +75,22 @@
DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map);
}
- static void enforcePermissionsForAssociation(@NonNull Context context,
+ /**
+ * Require the app to declare necessary permission for creating association.
+ */
+ public static void enforcePermissionForCreatingAssociation(@NonNull Context context,
@NonNull AssociationRequest request, int packageUid) {
- enforceRequestDeviceProfilePermissions(context, request.getDeviceProfile(), packageUid);
+ enforcePermissionForRequestingProfile(context, request.getDeviceProfile(), packageUid);
if (request.isSelfManaged()) {
- enforceRequestSelfManagedPermission(context, packageUid);
+ enforcePermissionForRequestingSelfManaged(context, packageUid);
}
}
- static void enforceRequestDeviceProfilePermissions(
+ /**
+ * Require the app to declare necessary permission for creating association with profile.
+ */
+ public static void enforcePermissionForRequestingProfile(
@NonNull Context context, @Nullable String deviceProfile, int packageUid) {
// Device profile can be null.
if (deviceProfile == null) return;
@@ -101,7 +107,11 @@
}
}
- static void enforceRequestSelfManagedPermission(@NonNull Context context, int packageUid) {
+ /**
+ * Require the app to declare necessary permission for creating self-managed association.
+ */
+ public static void enforcePermissionForRequestingSelfManaged(@NonNull Context context,
+ int packageUid) {
if (context.checkPermission(REQUEST_COMPANION_SELF_MANAGED, getCallingPid(), packageUid)
!= PERMISSION_GRANTED) {
throw new SecurityException("Application does not hold "
@@ -109,25 +119,39 @@
}
}
- static boolean checkCallerCanInteractWithUserId(@NonNull Context context, int userId) {
+ /**
+ * Check if the caller can interact with the user.
+ */
+ public static boolean checkCallerCanInteractWithUserId(@NonNull Context context, int userId) {
if (getCallingUserId() == userId) return true;
return context.checkCallingPermission(INTERACT_ACROSS_USERS) == PERMISSION_GRANTED;
}
- static void enforceCallerCanInteractWithUserId(@NonNull Context context, int userId) {
+ /**
+ * Require the caller to be able to interact with the user.
+ */
+ public static void enforceCallerCanInteractWithUserId(@NonNull Context context, int userId) {
if (getCallingUserId() == userId) return;
context.enforceCallingPermission(INTERACT_ACROSS_USERS, null);
}
- static void enforceCallerIsSystemOrCanInteractWithUserId(@NonNull Context context, int userId) {
+ /**
+ * Require the caller to be system UID or to be able to interact with the user.
+ */
+ public static void enforceCallerIsSystemOrCanInteractWithUserId(@NonNull Context context,
+ int userId) {
if (getCallingUid() == SYSTEM_UID) return;
enforceCallerCanInteractWithUserId(context, userId);
}
- static boolean checkCallerIsSystemOr(@UserIdInt int userId, @NonNull String packageName) {
+ /**
+ * Check if the caller is system UID or the provided user.
+ */
+ public static boolean checkCallerIsSystemOr(@UserIdInt int userId,
+ @NonNull String packageName) {
final int callingUid = getCallingUid();
if (callingUid == SYSTEM_UID) return true;
@@ -158,13 +182,19 @@
}
}
- static boolean checkCallerCanManageCompanionDevice(@NonNull Context context) {
+ /**
+ * Check if the caller holds the necessary permission to manage companion devices.
+ */
+ public static boolean checkCallerCanManageCompanionDevice(@NonNull Context context) {
if (getCallingUid() == SYSTEM_UID) return true;
return context.checkCallingPermission(MANAGE_COMPANION_DEVICES) == PERMISSION_GRANTED;
}
- static void enforceCallerCanManageAssociationsForPackage(@NonNull Context context,
+ /**
+ * Require the caller to be able to manage the associations for the package.
+ */
+ public static void enforceCallerCanManageAssociationsForPackage(@NonNull Context context,
@UserIdInt int userId, @NonNull String packageName,
@Nullable String actionDescription) {
if (checkCallerCanManageAssociationsForPackage(context, userId, packageName)) return;
@@ -175,7 +205,10 @@
+ " for u" + userId + "/" + packageName);
}
- static void enforceCallerCanObservingDevicePresenceByUuid(@NonNull Context context) {
+ /**
+ * Require the caller to hold necessary permission to observe device presence by UUID.
+ */
+ public static void enforceCallerCanObservingDevicePresenceByUuid(@NonNull Context context) {
if (context.checkCallingPermission(REQUEST_OBSERVE_DEVICE_UUID_PRESENCE)
!= PERMISSION_GRANTED) {
throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have "
@@ -193,7 +226,7 @@
* </ul>
* @return whether the caller is one of the above.
*/
- static boolean checkCallerCanManageAssociationsForPackage(@NonNull Context context,
+ public static boolean checkCallerCanManageAssociationsForPackage(@NonNull Context context,
@UserIdInt int userId, @NonNull String packageName) {
if (checkCallerIsSystemOr(userId, packageName)) return true;
diff --git a/services/companion/java/com/android/server/companion/RolesUtils.java b/services/companion/java/com/android/server/companion/utils/RolesUtils.java
similarity index 70%
rename from services/companion/java/com/android/server/companion/RolesUtils.java
rename to services/companion/java/com/android/server/companion/utils/RolesUtils.java
index af9d2d7..f798e21 100644
--- a/services/companion/java/com/android/server/companion/RolesUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/RolesUtils.java
@@ -14,13 +14,10 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.utils;
import static android.app.role.RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP;
-import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
-import static com.android.server.companion.CompanionDeviceManagerService.TAG;
-
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
@@ -29,7 +26,6 @@
import android.content.Context;
import android.os.Binder;
import android.os.UserHandle;
-import android.util.Log;
import android.util.Slog;
import java.util.List;
@@ -37,9 +33,14 @@
/** Utility methods for accessing {@link RoleManager} APIs. */
@SuppressLint("LongLogTag")
-final class RolesUtils {
+public final class RolesUtils {
- static boolean isRoleHolder(@NonNull Context context, @UserIdInt int userId,
+ private static final String TAG = "CDM_RolesUtils";
+
+ /**
+ * Check if the package holds the role.
+ */
+ public static boolean isRoleHolder(@NonNull Context context, @UserIdInt int userId,
@NonNull String packageName, @NonNull String role) {
final RoleManager roleManager = context.getSystemService(RoleManager.class);
final List<String> roleHolders = roleManager.getRoleHoldersAsUser(
@@ -58,13 +59,9 @@
* false if failed. If the association does not have any device profile
* specified, then the operation will always be successful as a no-op.
*/
- static void addRoleHolderForAssociation(
+ public static void addRoleHolderForAssociation(
@NonNull Context context, @NonNull AssociationInfo associationInfo,
@NonNull Consumer<Boolean> roleGrantResult) {
- if (DEBUG) {
- Log.d(TAG, "addRoleHolderForAssociation() associationInfo=" + associationInfo);
- }
-
final String deviceProfile = associationInfo.getDeviceProfile();
if (deviceProfile == null) {
// If no device profile is specified, then no-op and resolve callback with success.
@@ -83,33 +80,30 @@
roleGrantResult);
}
- static void removeRoleHolderForAssociation(
- @NonNull Context context, @NonNull AssociationInfo associationInfo) {
- if (DEBUG) {
- Log.d(TAG, "removeRoleHolderForAssociation() associationInfo=" + associationInfo);
- }
-
- final String deviceProfile = associationInfo.getDeviceProfile();
+ /**
+ * Remove the role for the package association.
+ */
+ public static void removeRoleHolderForAssociation(
+ @NonNull Context context, int userId, String packageName, String deviceProfile) {
if (deviceProfile == null) return;
final RoleManager roleManager = context.getSystemService(RoleManager.class);
- final String packageName = associationInfo.getPackageName();
- final int userId = associationInfo.getUserId();
final UserHandle userHandle = UserHandle.of(userId);
- Slog.i(TAG, "Removing CDM role holder, role=" + deviceProfile
- + ", package=u" + userId + "\\" + packageName);
+ Slog.i(TAG, "Removing CDM role=" + deviceProfile
+ + " for userId=" + userId + ", packageName=" + packageName);
final long identity = Binder.clearCallingIdentity();
try {
roleManager.removeRoleHolderAsUser(deviceProfile, packageName,
- MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
- success -> {
- if (!success) {
- Slog.e(TAG, "Failed to remove u" + userId + "\\" + packageName
- + " from the list of " + deviceProfile + " holders.");
- }
- });
+ MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
+ success -> {
+ if (!success) {
+ Slog.e(TAG, "Failed to remove userId=" + userId + ", packageName="
+ + packageName + " from the list of " + deviceProfile
+ + " holders.");
+ }
+ });
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/services/companion/java/com/android/server/companion/Utils.java b/services/companion/java/com/android/server/companion/utils/Utils.java
similarity index 64%
rename from services/companion/java/com/android/server/companion/Utils.java
rename to services/companion/java/com/android/server/companion/utils/Utils.java
index b9f61ec..8302997 100644
--- a/services/companion/java/com/android/server/companion/Utils.java
+++ b/services/companion/java/com/android/server/companion/utils/Utils.java
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-package com.android.server.companion;
+package com.android.server.companion.utils;
+import android.annotation.NonNull;
+import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.ResultReceiver;
@@ -43,5 +45,27 @@
return ipcFriendly;
}
+ /**
+ * Return a human-readable string for the BluetoothDevice.
+ */
+ public static String btDeviceToString(@NonNull BluetoothDevice btDevice) {
+ final StringBuilder sb = new StringBuilder(btDevice.getAddress());
+
+ sb.append(" [name=");
+ final String name = btDevice.getName();
+ if (name != null) {
+ sb.append('\'').append(name).append('\'');
+ } else {
+ sb.append("null");
+ }
+
+ final String alias = btDevice.getAlias();
+ if (alias != null) {
+ sb.append(", alias='").append(alias).append("'");
+ }
+
+ return sb.append(']').toString();
+ }
+
private Utils() {}
}
diff --git a/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java b/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java
index 3312be2..96fee929 100644
--- a/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java
+++ b/services/core/java/com/android/server/adaptiveauth/AdaptiveAuthService.java
@@ -32,8 +32,10 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
+import android.util.SparseLongArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockSettingsInternal;
import com.android.internal.widget.LockSettingsStateListener;
@@ -57,6 +59,8 @@
private static final int MSG_REPORT_BIOMETRIC_AUTH_ATTEMPT = 2;
private static final int AUTH_SUCCESS = 1;
private static final int AUTH_FAILURE = 0;
+ private static final int TYPE_PRIMARY_AUTH = 0;
+ private static final int TYPE_BIOMETRIC_AUTH = 1;
private final LockPatternUtils mLockPatternUtils;
private final LockSettingsInternal mLockSettings;
@@ -67,6 +71,7 @@
private final UserManagerInternal mUserManager;
@VisibleForTesting
final SparseIntArray mFailedAttemptsForUser = new SparseIntArray();
+ private final SparseLongArray mLastLockedTimestamp = new SparseLongArray();
public AdaptiveAuthService(Context context) {
this(context, new LockPatternUtils(context));
@@ -170,7 +175,7 @@
Slog.d(TAG, "handleReportPrimaryAuthAttempt: success=" + success
+ ", userId=" + userId);
}
- reportAuthAttempt(success, userId);
+ reportAuthAttempt(TYPE_PRIMARY_AUTH, success, userId);
}
private void handleReportBiometricAuthAttempt(boolean success, int userId) {
@@ -178,13 +183,24 @@
Slog.d(TAG, "handleReportBiometricAuthAttempt: success=" + success
+ ", userId=" + userId);
}
- reportAuthAttempt(success, userId);
+ reportAuthAttempt(TYPE_BIOMETRIC_AUTH, success, userId);
}
- private void reportAuthAttempt(boolean success, int userId) {
+ private void reportAuthAttempt(int authType, boolean success, int userId) {
if (success) {
// Deleting the entry effectively resets the counter of failed attempts for the user
mFailedAttemptsForUser.delete(userId);
+
+ // Collect metrics if the device was locked by adaptive auth before
+ if (mLastLockedTimestamp.indexOfKey(userId) >= 0) {
+ final long lastLockedTime = mLastLockedTimestamp.get(userId);
+ collectTimeElapsedSinceLastLocked(
+ lastLockedTime, SystemClock.elapsedRealtime(), authType);
+
+ // Remove the entry for the last locked time because a successful auth just happened
+ // and metrics have been collected
+ mLastLockedTimestamp.delete(userId);
+ }
return;
}
@@ -210,6 +226,34 @@
lockDevice(userId);
}
+ private static void collectTimeElapsedSinceLastLocked(long lastLockedTime, long authTime,
+ int authType) {
+ final int unlockType = switch (authType) {
+ case TYPE_PRIMARY_AUTH -> FrameworkStatsLog
+ .ADAPTIVE_AUTH_UNLOCK_AFTER_LOCK_REPORTED__UNLOCK_TYPE__PRIMARY_AUTH;
+ case TYPE_BIOMETRIC_AUTH -> FrameworkStatsLog
+ .ADAPTIVE_AUTH_UNLOCK_AFTER_LOCK_REPORTED__UNLOCK_TYPE__BIOMETRIC_AUTH;
+ default -> FrameworkStatsLog
+ .ADAPTIVE_AUTH_UNLOCK_AFTER_LOCK_REPORTED__UNLOCK_TYPE__UNKNOWN;
+ };
+
+ if (DEBUG) {
+ Slog.d(TAG, "collectTimeElapsedSinceLastLockedForUser: "
+ + "lastLockedTime=" + lastLockedTime
+ + ", authTime=" + authTime
+ + ", unlockType=" + unlockType);
+ }
+
+ // This usually shouldn't happen, and just check out of an abundance of caution
+ if (lastLockedTime > authTime) {
+ return;
+ }
+
+ // Log to statsd
+ FrameworkStatsLog.write(FrameworkStatsLog.ADAPTIVE_AUTH_UNLOCK_AFTER_LOCK_REPORTED,
+ lastLockedTime, authTime, unlockType);
+ }
+
/**
* Locks the device and requires primary auth or biometric auth for unlocking
*/
@@ -234,5 +278,9 @@
// Lock the device
mWindowManager.lockNow();
+
+ // Record the time that the device is locked by adaptive auth to collect metrics when the
+ // next successful primary or biometric auth happens
+ mLastLockedTimestamp.put(userId, SystemClock.elapsedRealtime());
}
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b8f6b3f..b8e09cc 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -4133,7 +4133,7 @@
|| (callerApp.mState.getCurProcState() <= PROCESS_STATE_TOP
&& c.hasFlag(Context.BIND_TREAT_LIKE_ACTIVITY)),
b.client);
- if ((serviceBindingOomAdjPolicy
+ if (!s.mOomAdjBumpedInExec && (serviceBindingOomAdjPolicy
& SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CONNECT) == 0) {
needOomAdj = true;
mAm.enqueueOomAdjTargetLocked(s.app);
@@ -4277,7 +4277,7 @@
}
serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false, false,
- !Flags.serviceBindingOomAdjPolicy() || b == null || !b.mSkippedOomAdj
+ !Flags.serviceBindingOomAdjPolicy() || r.mOomAdjBumpedInExec
? OOM_ADJ_REASON_EXECUTING_SERVICE : OOM_ADJ_REASON_NONE);
}
} finally {
@@ -4398,7 +4398,6 @@
+ (b != null ? b.apps.size() : 0));
boolean inDestroying = mDestroyingServices.contains(r);
- boolean skipOomAdj = false;
if (b != null) {
if (b.apps.size() > 0 && !inDestroying) {
// Applications have already bound since the last
@@ -4423,11 +4422,11 @@
// a new client.
b.doRebind = true;
}
- skipOomAdj = Flags.serviceBindingOomAdjPolicy() && b.mSkippedOomAdj;
}
serviceDoneExecutingLocked(r, inDestroying, false, false,
- skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE);
+ !Flags.serviceBindingOomAdjPolicy() || r.mOomAdjBumpedInExec
+ ? OOM_ADJ_REASON_UNBIND_SERVICE : OOM_ADJ_REASON_NONE);
}
} finally {
mAm.mInjector.restoreCallingIdentity(origId);
@@ -4905,9 +4904,8 @@
* Bump the given service record into executing state.
* @param oomAdjReason The caller requests it to perform the oomAdjUpdate not {@link
* ActivityManagerInternal#OOM_ADJ_REASON_NONE}.
- * @return {@code true} if it performed oomAdjUpdate.
*/
- private boolean bumpServiceExecutingLocked(
+ private void bumpServiceExecutingLocked(
ServiceRecord r, boolean fg, String why, @OomAdjReason int oomAdjReason,
boolean skipTimeoutIfPossible) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, ">>> EXECUTING "
@@ -4972,19 +4970,17 @@
}
}
}
- boolean oomAdjusted = false;
if (oomAdjReason != OOM_ADJ_REASON_NONE && r.app != null
&& r.app.mState.getCurProcState() > ActivityManager.PROCESS_STATE_SERVICE) {
// Force an immediate oomAdjUpdate, so the client app could be in the correct process
// state before doing any service related transactions
mAm.enqueueOomAdjTargetLocked(r.app);
mAm.updateOomAdjPendingTargetsLocked(oomAdjReason);
- oomAdjusted = true;
+ r.mOomAdjBumpedInExec = true;
}
r.executeFg |= fg;
r.executeNesting++;
r.executingStart = SystemClock.uptimeMillis();
- return oomAdjusted;
}
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
@@ -5001,7 +4997,7 @@
& SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_BIND) != 0;
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
- i.mSkippedOomAdj = !bumpServiceExecutingLocked(r, execInFg, "bind",
+ bumpServiceExecutingLocked(r, execInFg, "bind",
skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_BIND_SERVICE,
skipOomAdj /* skipTimeoutIfPossible */);
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
@@ -5020,14 +5016,16 @@
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r, e);
final boolean inDestroying = mDestroyingServices.contains(r);
serviceDoneExecutingLocked(r, inDestroying, inDestroying, false,
- skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE);
+ !Flags.serviceBindingOomAdjPolicy() || r.mOomAdjBumpedInExec
+ ? OOM_ADJ_REASON_UNBIND_SERVICE : OOM_ADJ_REASON_NONE);
throw e;
} catch (RemoteException e) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r);
// Keep the executeNesting count accurate.
final boolean inDestroying = mDestroyingServices.contains(r);
serviceDoneExecutingLocked(r, inDestroying, inDestroying, false,
- skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE);
+ !Flags.serviceBindingOomAdjPolicy() || r.mOomAdjBumpedInExec
+ ? OOM_ADJ_REASON_UNBIND_SERVICE : OOM_ADJ_REASON_NONE);
return false;
}
}
@@ -5823,6 +5821,7 @@
// process state before doing any service related transactions
mAm.enqueueOomAdjTargetLocked(app);
mAm.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_SERVICE);
+ r.mOomAdjBumpedInExec = true;
} else {
// Since we skipped the oom adj update, the Service#onCreate() might be running in
// the cached state, if the service process drops into the cached state after the call.
@@ -5863,7 +5862,8 @@
// Keep the executeNesting count accurate.
final boolean inDestroying = mDestroyingServices.contains(r);
serviceDoneExecutingLocked(r, inDestroying, inDestroying, false,
- skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_STOP_SERVICE);
+ !Flags.serviceBindingOomAdjPolicy() || r.mOomAdjBumpedInExec
+ ? OOM_ADJ_REASON_STOP_SERVICE : OOM_ADJ_REASON_NONE);
// Cleanup.
if (newService) {
@@ -5898,7 +5898,7 @@
null, null, 0, null, null, ActivityManager.PROCESS_STATE_UNKNOWN));
}
- sendServiceArgsLocked(r, execInFg, true);
+ sendServiceArgsLocked(r, execInFg, r.mOomAdjBumpedInExec);
if (r.delayed) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (new proc): " + r);
@@ -6085,7 +6085,8 @@
}
}
- boolean oomAdjusted = false;
+ boolean oomAdjusted = Flags.serviceBindingOomAdjPolicy() && r.mOomAdjBumpedInExec;
+
// Tell the service that it has been unbound.
if (r.app != null && r.app.isThreadReady()) {
for (int i = r.bindings.size() - 1; i >= 0; i--) {
@@ -6094,9 +6095,10 @@
+ ": hasBound=" + ibr.hasBound);
if (ibr.hasBound) {
try {
- oomAdjusted |= bumpServiceExecutingLocked(r, false, "bring down unbind",
- OOM_ADJ_REASON_UNBIND_SERVICE,
- false /* skipTimeoutIfPossible */);
+ bumpServiceExecutingLocked(r, false, "bring down unbind",
+ oomAdjusted ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE,
+ oomAdjusted /* skipTimeoutIfPossible */);
+ oomAdjusted |= r.mOomAdjBumpedInExec;
ibr.hasBound = false;
ibr.requested = false;
r.app.getThread().scheduleUnbindService(r,
@@ -6252,10 +6254,11 @@
}
} else {
try {
- oomAdjusted |= bumpServiceExecutingLocked(r, false, "destroy",
- oomAdjusted ? 0 : OOM_ADJ_REASON_STOP_SERVICE,
- false /* skipTimeoutIfPossible */);
+ bumpServiceExecutingLocked(r, false, "destroy",
+ oomAdjusted ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE,
+ oomAdjusted /* skipTimeoutIfPossible */);
mDestroyingServices.add(r);
+ oomAdjusted |= r.mOomAdjBumpedInExec;
r.destroying = true;
r.app.getThread().scheduleStopService(r);
} catch (Exception e) {
@@ -6411,7 +6414,7 @@
final boolean skipOomAdj = (serviceBindingOomAdjPolicy
& SERVICE_BIND_OOMADJ_POLICY_SKIP_OOM_UPDATE_ON_CONNECT) != 0;
try {
- b.intent.mSkippedOomAdj = !bumpServiceExecutingLocked(s, false, "unbind",
+ bumpServiceExecutingLocked(s, false, "unbind",
skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_UNBIND_SERVICE,
skipOomAdj /* skipTimeoutIfPossible */);
if (b.client != s.app && c.notHasFlag(Context.BIND_WAIVE_PRIORITY)
@@ -6461,6 +6464,7 @@
boolean inDestroying = mDestroyingServices.contains(r);
if (r != null) {
boolean skipOomAdj = false;
+ boolean needOomAdj = false;
if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {
// This is a call from a service start... take care of
// book-keeping.
@@ -6536,15 +6540,13 @@
// Fake it to keep from ANR due to orphaned entry.
r.executeNesting = 1;
}
- } else if (type == ActivityThread.SERVICE_DONE_EXECUTING_REBIND
- || type == ActivityThread.SERVICE_DONE_EXECUTING_UNBIND) {
- final Intent.FilterComparison filter = new Intent.FilterComparison(intent);
- final IntentBindRecord b = r.bindings.get(filter);
- skipOomAdj = Flags.serviceBindingOomAdjPolicy() && b != null && b.mSkippedOomAdj;
+ // The service is done, force an oom adj update.
+ needOomAdj = true;
}
final long origId = mAm.mInjector.clearCallingIdentity();
serviceDoneExecutingLocked(r, inDestroying, inDestroying, enqueueOomAdj,
- skipOomAdj ? OOM_ADJ_REASON_NONE : OOM_ADJ_REASON_EXECUTING_SERVICE);
+ !Flags.serviceBindingOomAdjPolicy() || r.mOomAdjBumpedInExec || needOomAdj
+ ? OOM_ADJ_REASON_EXECUTING_SERVICE : OOM_ADJ_REASON_NONE);
mAm.mInjector.restoreCallingIdentity(origId);
} else {
Slog.w(TAG, "Done executing unknown service from pid "
@@ -6600,18 +6602,16 @@
mDestroyingServices.remove(r);
r.bindings.clear();
}
- boolean oomAdjusted = false;
if (oomAdjReason != OOM_ADJ_REASON_NONE) {
if (enqueueOomAdj) {
mAm.enqueueOomAdjTargetLocked(r.app);
} else {
mAm.updateOomAdjLocked(r.app, oomAdjReason);
}
- oomAdjusted = true;
} else {
// Skip oom adj if it wasn't bumped during the bumpServiceExecutingLocked()
- oomAdjusted = false;
}
+ r.mOomAdjBumpedInExec = false;
}
r.executeFg = false;
if (r.tracker != null) {
@@ -6995,6 +6995,7 @@
sr.setProcess(null, null, 0, null);
sr.isolationHostProc = null;
sr.executeNesting = 0;
+ sr.mOomAdjBumpedInExec = false;
synchronized (mAm.mProcessStats.mLock) {
sr.forceClearTracker();
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e222878..663ba8a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -9028,6 +9028,7 @@
Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid "
+ Process.myUid());
BinderProxy.dumpProxyDebugInfo();
+ CriticalEventLog.getInstance().logExcessiveBinderCalls(uid);
if (uid == Process.SYSTEM_UID) {
Slog.i(TAG, "Skipping kill (uid is SYSTEM)");
} else {
diff --git a/services/core/java/com/android/server/am/IntentBindRecord.java b/services/core/java/com/android/server/am/IntentBindRecord.java
index db47e3f..1a3652e 100644
--- a/services/core/java/com/android/server/am/IntentBindRecord.java
+++ b/services/core/java/com/android/server/am/IntentBindRecord.java
@@ -49,14 +49,6 @@
String stringName; // caching of toString
- /**
- * Mark if we've skipped oom adj update before calling into the {@link Service#onBind()}
- * or {@link Service#onUnbind()}.
- *
- * <p>If it's true, we'll skip the oom adj update too during the serviceDoneExecuting.
- */
- boolean mSkippedOomAdj;
-
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("service="); pw.println(service);
dumpInService(pw, prefix);
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 3c8d7fc..e3aac02 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -267,6 +267,11 @@
int mAllowStart_byBindings = REASON_DENIED;
/**
+ * Whether or not we've bumped its oom adj scores during its execution.
+ */
+ boolean mOomAdjBumpedInExec;
+
+ /**
* Whether to use the new "while-in-use permission" logic for FGS start
*/
private boolean useNewWiuLogic_forStart() {
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index 16dbe18..c06bdf9 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -53,3 +53,10 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ namespace: "backstage_power"
+ name: "defer_outgoing_bcasts"
+ description: "Defer outgoing broadcasts from processes in freezable state"
+ bug: "327496592"
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index 007b746..fb826c8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -24,6 +24,7 @@
import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ComponentInfoInternal;
@@ -39,6 +40,7 @@
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -64,6 +66,7 @@
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
+import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
@@ -359,6 +362,17 @@
null /* callback */);
}
+ if (Build.isDebuggable()) {
+ BiometricUtils<Face> utils = FaceUtils.getInstance(
+ mFaceSensors.keyAt(0));
+ for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
+ List<Face> enrollments = utils.getBiometricsForUser(mContext, user.id);
+ Slog.d(getTag(), "Expecting enrollments for user " + user.id + ": "
+ + enrollments.stream().map(
+ BiometricAuthenticator.Identifier::getBiometricId).toList());
+ }
+ }
+
return mDaemon;
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
index a104cf4..c04c47e 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java
@@ -27,6 +27,7 @@
import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.TypedArray;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IInvalidationCallback;
@@ -46,6 +47,7 @@
import android.hardware.fingerprint.ISidefpsController;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Binder;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -71,6 +73,7 @@
import com.android.server.biometrics.sensors.BaseClientMonitor;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.BiometricStateCallback;
+import com.android.server.biometrics.sensors.BiometricUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback;
@@ -382,6 +385,17 @@
null /* callback */);
}
+ if (Build.isDebuggable()) {
+ BiometricUtils<Fingerprint> utils = FingerprintUtils.getInstance(
+ mFingerprintSensors.keyAt(0));
+ for (UserInfo user : UserManager.get(mContext).getAliveUsers()) {
+ List<Fingerprint> enrollments = utils.getBiometricsForUser(mContext, user.id);
+ Slog.d(getTag(), "Expecting enrollments for user " + user.id + ": "
+ + enrollments.stream().map(
+ BiometricAuthenticator.Identifier::getBiometricId).toList());
+ }
+ }
+
return mDaemon;
}
diff --git a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
index 816c349..036284f 100644
--- a/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
+++ b/services/core/java/com/android/server/criticalevents/CriticalEventLog.java
@@ -30,6 +30,7 @@
import com.android.server.criticalevents.nano.CriticalEventProto.AppNotResponding;
import com.android.server.criticalevents.nano.CriticalEventProto.HalfWatchdog;
import com.android.server.criticalevents.nano.CriticalEventProto.InstallPackages;
+import com.android.server.criticalevents.nano.CriticalEventProto.ExcessiveBinderCalls;
import com.android.server.criticalevents.nano.CriticalEventProto.JavaCrash;
import com.android.server.criticalevents.nano.CriticalEventProto.NativeCrash;
import com.android.server.criticalevents.nano.CriticalEventProto.SystemServerStarted;
@@ -143,6 +144,15 @@
return System.currentTimeMillis();
}
+ /** Logs when a uid sends an excessive number of binder calls. */
+ public void logExcessiveBinderCalls(int uid) {
+ CriticalEventProto event = new CriticalEventProto();
+ ExcessiveBinderCalls calls = new ExcessiveBinderCalls();
+ calls.uid = uid;
+ event.setExcessiveBinderCalls(calls);
+ log(event);
+ }
+
/** Logs when one or more packages are installed. */
public void logInstallPackagesStarted() {
CriticalEventProto event = new CriticalEventProto();
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index dbe85ea..76f3035 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1474,6 +1474,7 @@
&& mFlags.isDisplayOffloadEnabled()
&& mPowerRequest.policy == POLICY_DOZE
&& mDisplayOffloadSession != null
+ && mAutomaticBrightnessController != null
&& mAutomaticBrightnessStrategy.shouldUseAutoBrightness()) {
rawBrightnessState = mAutomaticBrightnessController
.getAutomaticScreenBrightnessBasedOnLastObservedLux(mTempBrightnessEvent);
@@ -1724,6 +1725,7 @@
mTempBrightnessEvent.setBrightness(brightnessState);
mTempBrightnessEvent.setPhysicalDisplayId(mUniqueDisplayId);
mTempBrightnessEvent.setDisplayState(state);
+ mTempBrightnessEvent.setDisplayPolicy(mPowerRequest.policy);
mTempBrightnessEvent.setReason(mBrightnessReason);
mTempBrightnessEvent.setHbmMax(mBrightnessRangeController.getCurrentBrightnessMax());
mTempBrightnessEvent.setHbmMode(mBrightnessRangeController.getHighBrightnessMode());
diff --git a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
index 5423b74..82b401a 100644
--- a/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
+++ b/services/core/java/com/android/server/display/brightness/BrightnessEvent.java
@@ -16,6 +16,9 @@
package com.android.server.display.brightness;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_OFF;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.policyToString;
+
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_DEFAULT;
import static com.android.server.display.config.DisplayBrightnessMappingConfig.autoBrightnessModeToString;
@@ -46,6 +49,7 @@
private int mDisplayId;
private String mPhysicalDisplayId;
private int mDisplayState;
+ private int mDisplayPolicy;
private long mTime;
private float mLux;
private float mPreThresholdLux;
@@ -85,6 +89,7 @@
mDisplayId = that.getDisplayId();
mPhysicalDisplayId = that.getPhysicalDisplayId();
mDisplayState = that.mDisplayState;
+ mDisplayPolicy = that.mDisplayPolicy;
mTime = that.getTime();
// Lux values
mLux = that.getLux();
@@ -117,6 +122,7 @@
mTime = SystemClock.uptimeMillis();
mPhysicalDisplayId = "";
mDisplayState = Display.STATE_UNKNOWN;
+ mDisplayPolicy = POLICY_OFF;
// Lux values
mLux = 0;
mPreThresholdLux = 0;
@@ -155,6 +161,7 @@
&& mDisplayId == that.mDisplayId
&& mPhysicalDisplayId.equals(that.mPhysicalDisplayId)
&& mDisplayState == that.mDisplayState
+ && mDisplayPolicy == that.mDisplayPolicy
&& Float.floatToRawIntBits(mLux) == Float.floatToRawIntBits(that.mLux)
&& Float.floatToRawIntBits(mPreThresholdLux)
== Float.floatToRawIntBits(that.mPreThresholdLux)
@@ -191,6 +198,7 @@
+ "disp=" + mDisplayId
+ ", physDisp=" + mPhysicalDisplayId
+ ", displayState=" + Display.stateToString(mDisplayState)
+ + ", displayPolicy=" + policyToString(mDisplayPolicy)
+ ", brt=" + mBrightness + ((mFlags & FLAG_USER_SET) != 0 ? "(user_set)" : "")
+ ", initBrt=" + mInitialBrightness
+ ", rcmdBrt=" + mRecommendedBrightness
@@ -251,6 +259,10 @@
mDisplayState = state;
}
+ public void setDisplayPolicy(int policy) {
+ mDisplayPolicy = policy;
+ }
+
public float getLux() {
return mLux;
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 9a76ebd..29ea071 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -108,7 +108,6 @@
import android.provider.Settings;
import android.security.AndroidKeyStoreMaintenance;
import android.security.Authorization;
-import android.security.KeyStore;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import android.security.keystore.recovery.KeyChainProtectionParams;
@@ -169,6 +168,7 @@
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
+import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
@@ -292,7 +292,7 @@
private final IActivityManager mActivityManager;
private final SyntheticPasswordManager mSpManager;
- private final java.security.KeyStore mJavaKeyStore;
+ private final KeyStore mKeyStore;
private final RecoverableKeyStoreManager mRecoverableKeyStoreManager;
private final UnifiedProfilePasswordCache mUnifiedProfilePasswordCache;
@@ -564,10 +564,6 @@
return DeviceStateCache.getInstance();
}
- public KeyStore getKeyStore() {
- return KeyStore.getInstance();
- }
-
public RecoverableKeyStoreManager getRecoverableKeyStoreManager() {
return RecoverableKeyStoreManager.getInstance(mContext);
}
@@ -619,9 +615,9 @@
return (BiometricManager) mContext.getSystemService(Context.BIOMETRIC_SERVICE);
}
- public java.security.KeyStore getJavaKeyStore() {
+ public KeyStore getKeyStore() {
try {
- java.security.KeyStore ks = java.security.KeyStore.getInstance(
+ KeyStore ks = KeyStore.getInstance(
SyntheticPasswordCrypto.androidKeystoreProviderName());
ks.load(new AndroidKeyStoreLoadStoreParameter(
SyntheticPasswordCrypto.keyNamespace()));
@@ -631,8 +627,7 @@
}
}
- public @NonNull UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(
- java.security.KeyStore ks) {
+ public @NonNull UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(KeyStore ks) {
return new UnifiedProfilePasswordCache(ks);
}
@@ -654,7 +649,7 @@
protected LockSettingsService(Injector injector) {
mInjector = injector;
mContext = injector.getContext();
- mJavaKeyStore = injector.getJavaKeyStore();
+ mKeyStore = injector.getKeyStore();
mRecoverableKeyStoreManager = injector.getRecoverableKeyStoreManager();
mHandler = injector.getHandler(injector.getServiceThread());
mStrongAuth = injector.getStrongAuth();
@@ -676,7 +671,7 @@
mGatekeeperPasswords = new LongSparseArray<>();
mSpManager = injector.getSyntheticPasswordManager(mStorage);
- mUnifiedProfilePasswordCache = injector.getUnifiedProfilePasswordCache(mJavaKeyStore);
+ mUnifiedProfilePasswordCache = injector.getUnifiedProfilePasswordCache(mKeyStore);
mBiometricDeferredQueue = new BiometricDeferredQueue(mSpManager, mHandler);
mRebootEscrowManager = injector.getRebootEscrowManager(new RebootEscrowCallbacks(),
@@ -1482,7 +1477,7 @@
byte[] encryptedPassword = Arrays.copyOfRange(storedData, PROFILE_KEY_IV_SIZE,
storedData.length);
byte[] decryptionResult;
- SecretKey decryptionKey = (SecretKey) mJavaKeyStore.getKey(
+ SecretKey decryptionKey = (SecretKey) mKeyStore.getKey(
PROFILE_KEY_NAME_DECRYPT + userId, null);
Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
@@ -1875,9 +1870,10 @@
}
}
- private void onPostPasswordChanged(LockscreenCredential newCredential, int userHandle) {
- updatePasswordHistory(newCredential, userHandle);
- mContext.getSystemService(TrustManager.class).reportEnabledTrustAgentsChanged(userHandle);
+ private void onPostPasswordChanged(LockscreenCredential newCredential, int userId) {
+ updatePasswordHistory(newCredential, userId);
+ mContext.getSystemService(TrustManager.class).reportEnabledTrustAgentsChanged(userId);
+ sendMainUserCredentialChangedNotificationIfNeeded(userId);
}
/**
@@ -2076,16 +2072,16 @@
keyGenerator.init(new SecureRandom());
SecretKey secretKey = keyGenerator.generateKey();
try {
- mJavaKeyStore.setEntry(
+ mKeyStore.setEntry(
PROFILE_KEY_NAME_ENCRYPT + profileUserId,
- new java.security.KeyStore.SecretKeyEntry(secretKey),
+ new KeyStore.SecretKeyEntry(secretKey),
new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build());
- mJavaKeyStore.setEntry(
+ mKeyStore.setEntry(
PROFILE_KEY_NAME_DECRYPT + profileUserId,
- new java.security.KeyStore.SecretKeyEntry(secretKey),
+ new KeyStore.SecretKeyEntry(secretKey),
new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
@@ -2094,7 +2090,7 @@
.setUserAuthenticationValidityDurationSeconds(30)
.build());
// Key imported, obtain a reference to it.
- SecretKey keyStoreEncryptionKey = (SecretKey) mJavaKeyStore.getKey(
+ SecretKey keyStoreEncryptionKey = (SecretKey) mKeyStore.getKey(
PROFILE_KEY_NAME_ENCRYPT + profileUserId, null);
Cipher cipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
@@ -2104,7 +2100,7 @@
iv = cipher.getIV();
} finally {
// The original key can now be discarded.
- mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + profileUserId);
+ mKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + profileUserId);
}
} catch (UnrecoverableKeyException
| BadPaddingException | IllegalBlockSizeException | KeyStoreException
@@ -2556,11 +2552,10 @@
final String encryptAlias = PROFILE_KEY_NAME_ENCRYPT + targetUserId;
final String decryptAlias = PROFILE_KEY_NAME_DECRYPT + targetUserId;
try {
- if (mJavaKeyStore.containsAlias(encryptAlias) ||
- mJavaKeyStore.containsAlias(decryptAlias)) {
+ if (mKeyStore.containsAlias(encryptAlias) || mKeyStore.containsAlias(decryptAlias)) {
Slogf.i(TAG, "Removing keystore profile key for user %d", targetUserId);
- mJavaKeyStore.deleteEntry(encryptAlias);
- mJavaKeyStore.deleteEntry(decryptAlias);
+ mKeyStore.deleteEntry(encryptAlias);
+ mKeyStore.deleteEntry(decryptAlias);
}
} catch (KeyStoreException e) {
// We have tried our best to remove the key.
@@ -3062,7 +3057,6 @@
setCurrentLskfBasedProtectorId(newProtectorId, userId);
LockPatternUtils.invalidateCredentialTypeCache();
synchronizeUnifiedChallengeForProfiles(userId, profilePasswords);
- sendMainUserCredentialChangedNotificationIfNeeded(userId);
setUserPasswordMetrics(credential, userId);
mUnifiedProfilePasswordCache.removePassword(userId);
@@ -3457,7 +3451,7 @@
private void dumpKeystoreKeys(IndentingPrintWriter pw) {
try {
- final Enumeration<String> aliases = mJavaKeyStore.aliases();
+ final Enumeration<String> aliases = mKeyStore.aliases();
while (aliases.hasMoreElements()) {
pw.println(aliases.nextElement());
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 69f7906..fe8030b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1541,6 +1541,19 @@
return;
}
+ // Initialize all necessary settings for archival installation.
+ pkgSetting
+ // No package.
+ .setPkg(null)
+ // Mark for later restore.
+ .setPendingRestore(true);
+ for (int userId : userIds) {
+ // Unmark "installed" for all users.
+ pkgSetting
+ .modifyUserState(userId)
+ .setInstalled(false);
+ }
+
String responsibleInstallerPackage = PackageArchiver.getResponsibleInstallerPackage(
pkgSetting);
// TODO(b/278553670) Check if responsibleInstallerPackage supports unarchival.
@@ -1551,16 +1564,11 @@
for (int userId : userIds) {
var archiveState = mInstallerService.mPackageArchiver.createArchiveState(
archivePackage, userId, responsibleInstallerPackage);
- if (archiveState == null) {
- continue;
- }
- pkgSetting
- .setPkg(null)
- // This package was installed as archived. Need to mark it for later restore.
- .setPendingRestore(true)
+ if (archiveState != null) {
+ pkgSetting
.modifyUserState(userId)
- .setInstalled(false)
.setArchiveState(archiveState);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index ed5df5f..981c4c0 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -506,7 +506,8 @@
// keep backwards compatibility we remove the task from recents when finishing
// task with root activity.
mTaskSupervisor.removeTask(tr, false /*killProcess*/,
- finishWithRootActivity, "finish-activity", r.getUid(), r.info.name);
+ finishWithRootActivity, "finish-activity", r.getUid(), r.getPid(),
+ r.info.name);
res = true;
// Explicitly dismissing the activity so reset its relaunch flag.
r.mRelaunchReason = RELAUNCH_REASON_NONE;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 90cff39..92fde18 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1476,7 +1476,8 @@
}
}
- private void scheduleConfigurationChanged(Configuration config) {
+ private void scheduleConfigurationChanged(@NonNull Configuration config,
+ @NonNull ActivityWindowInfo activityWindowInfo) {
if (!attachedToProcess()) {
ProtoLog.w(WM_DEBUG_CONFIGURATION, "Can't report activity configuration "
+ "update - client not running, activityRecord=%s", this);
@@ -1487,7 +1488,7 @@
+ "config: %s", this, config);
mAtmService.getLifecycleManager().scheduleTransactionItem(app.getThread(),
- ActivityConfigurationChangeItem.obtain(token, config));
+ ActivityConfigurationChangeItem.obtain(token, config, activityWindowInfo));
} catch (RemoteException e) {
// If process died, whatever.
}
@@ -9785,7 +9786,11 @@
// configurations because there are cases (like moving a task to the root pinned task) where
// the combine configurations are equal, but would otherwise differ in the override config
mTmpConfig.setTo(mLastReportedConfiguration.getMergedConfiguration());
- if (getConfiguration().equals(mTmpConfig) && !displayChanged) {
+ final ActivityWindowInfo newActivityWindowInfo = getActivityWindowInfo();
+ final boolean isActivityWindowInfoChanged = Flags.activityWindowInfoFlag()
+ && !mLastReportedActivityWindowInfo.equals(newActivityWindowInfo);
+ if (!displayChanged && !isActivityWindowInfoChanged
+ && getConfiguration().equals(mTmpConfig)) {
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration & display "
+ "unchanged in %s", this);
return true;
@@ -9800,7 +9805,6 @@
// Update last reported values.
final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
- final ActivityWindowInfo newActivityWindowInfo = getActivityWindowInfo();
setLastReportedConfiguration(getProcessGlobalConfiguration(), newMergedOverrideConfig);
setLastReportedActivityWindowInfo(newActivityWindowInfo);
@@ -9823,7 +9827,7 @@
scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig,
newActivityWindowInfo);
} else {
- scheduleConfigurationChanged(newMergedOverrideConfig);
+ scheduleConfigurationChanged(newMergedOverrideConfig, newActivityWindowInfo);
}
notifyDisplayCompatPolicyAboutConfigurationChange(
mLastReportedConfiguration.getMergedConfiguration(), mTmpConfig);
@@ -9891,7 +9895,7 @@
scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig,
newActivityWindowInfo);
} else {
- scheduleConfigurationChanged(newMergedOverrideConfig);
+ scheduleConfigurationChanged(newMergedOverrideConfig, newActivityWindowInfo);
}
notifyDisplayCompatPolicyAboutConfigurationChange(
mLastReportedConfiguration.getMergedConfiguration(), mTmpConfig);
diff --git a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
index 01d077a..4149bd9 100644
--- a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
+++ b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
@@ -41,7 +41,7 @@
static final String DOC_LINK = "go/android-asm";
/** Used to determine which version of the ASM logic was used in logs while we iterate */
- static final int ASM_VERSION = 9;
+ static final int ASM_VERSION = 10;
private static final String NAMESPACE = NAMESPACE_WINDOW_MANAGER;
private static final String KEY_ASM_PREFIX = "ActivitySecurity__";
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c137c54..6ad056f 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2089,7 +2089,7 @@
if (!mSupervisor.getBackgroundActivityLaunchController().checkActivityAllowedToStart(
mSourceRecord, r, newTask, avoidMoveToFront(), targetTask, mLaunchFlags, mBalCode,
- mCallingUid, mRealCallingUid)) {
+ mCallingUid, mRealCallingUid, mPreferredTaskDisplayArea)) {
return START_ABORTED;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index aefa777..d1d498d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -42,6 +42,7 @@
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
+import static android.os.Process.INVALID_PID;
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
@@ -1652,11 +1653,11 @@
* @return Returns true if the given task was found and removed.
*/
boolean removeTaskById(int taskId, boolean killProcess, boolean removeFromRecents,
- String reason, int callingUid) {
+ String reason, int callingUid, int callingPid) {
final Task task =
mRootWindowContainer.anyTaskForId(taskId, MATCH_ATTACHED_TASK_OR_RECENT_TASKS);
if (task != null) {
- removeTask(task, killProcess, removeFromRecents, reason, callingUid, null);
+ removeTask(task, killProcess, removeFromRecents, reason, callingUid, callingPid, null);
return true;
}
Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
@@ -1664,11 +1665,11 @@
}
void removeTask(Task task, boolean killProcess, boolean removeFromRecents, String reason) {
- removeTask(task, killProcess, removeFromRecents, reason, SYSTEM_UID, null);
+ removeTask(task, killProcess, removeFromRecents, reason, SYSTEM_UID, INVALID_PID, null);
}
void removeTask(Task task, boolean killProcess, boolean removeFromRecents, String reason,
- int callingUid, String callerActivityClassName) {
+ int callingUid, int callingPid, String callerActivityClassName) {
if (task.mInRemoveTask) {
// Prevent recursion.
return;
@@ -1705,8 +1706,8 @@
if (task.isPersistable) {
mService.notifyTaskPersisterLocked(null, true);
}
- mBalController
- .checkActivityAllowedToClearTask(task, callingUid, callerActivityClassName);
+ mBalController.checkActivityAllowedToClearTask(
+ task, callingUid, callingPid, callerActivityClassName);
} finally {
task.mInRemoveTask = false;
}
@@ -1874,7 +1875,7 @@
// Task was trimmed from the recent tasks list -- remove the active task record as well
// since the user won't really be able to go back to it
removeTaskById(task.mTaskId, killProcess, false /* removeFromRecents */,
- "recent-task-trimmed", SYSTEM_UID);
+ "recent-task-trimmed", SYSTEM_UID, INVALID_PID);
}
task.removedFromRecents();
}
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index 50de0b0..d699af8 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -77,11 +77,13 @@
synchronized (mService.mGlobalLock) {
int origCallingUid = Binder.getCallingUid();
+ int origCallingPid = Binder.getCallingPid();
final long callingIdentity = Binder.clearCallingIdentity();
try {
// We remove the task from recents to preserve backwards
if (!mService.mTaskSupervisor.removeTaskById(mTaskId, false,
- REMOVE_FROM_RECENTS, "finish-and-remove-task", origCallingUid)) {
+ REMOVE_FROM_RECENTS, "finish-and-remove-task", origCallingUid,
+ origCallingPid)) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
} finally {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 4e28384..071f403 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -25,9 +25,12 @@
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
+import static android.os.Process.INVALID_PID;
+import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER;
+import static com.android.server.wm.ActivityStarter.ASM_RESTRICTIONS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -67,6 +70,7 @@
import android.util.Slog;
import android.widget.Toast;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FrameworkStatsLog;
@@ -75,6 +79,7 @@
import com.android.server.am.PendingIntentRecord;
import java.lang.annotation.Retention;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.StringJoiner;
import java.util.function.Consumer;
@@ -1022,7 +1027,7 @@
}
/**
- * Log activity starts which violate one of the following rules of the
+ * Check activity starts which violate one of the following rules of the
* activity security model (ASM):
* See go/activity-security for rationale behind the rules.
* 1. Within a task, only an activity matching a top UID of the task can start activities
@@ -1032,7 +1037,7 @@
boolean checkActivityAllowedToStart(@Nullable ActivityRecord sourceRecord,
@NonNull ActivityRecord targetRecord, boolean newTask, boolean avoidMoveTaskToFront,
@Nullable Task targetTask, int launchFlags, int balCode, int callingUid,
- int realCallingUid) {
+ int realCallingUid, TaskDisplayArea preferredTaskDisplayArea) {
// BAL Exception allowed in all cases
if (balCode == BAL_ALLOW_ALLOWLISTED_UID) {
return true;
@@ -1055,68 +1060,46 @@
}
}
- if (balCode == BAL_ALLOW_GRACE_PERIOD) {
- // Allow if launching into new task, and caller matches most recently finished activity
- if (taskToFront && mTopFinishedActivity != null
- && mTopFinishedActivity.mUid == callingUid) {
- return true;
- }
-
- // Launching into existing task - allow if matches most recently finished activity
- // within the task.
- // We can reach here multiple ways:
- // 1. activity in fg fires intent (taskToFront = false, sourceRecord is available)
- // 2. activity in bg fires intent (taskToFront = false, sourceRecord is available)
- // 3. activity in bg fires intent with NEW_FLAG (taskToFront = true,
- // avoidMoveTaskToFront = true, sourceRecord is available)
- // 4. activity in bg fires PI (taskToFront = true, avoidMoveTaskToFront = true,
- // sourceRecord is not available, targetTask may be available)
- if (!taskToFront || avoidMoveTaskToFront) {
- if (targetTask != null) {
- FinishedActivityEntry finishedEntry =
- mTaskIdToFinishedActivity.get(targetTask.mTaskId);
- if (finishedEntry != null && finishedEntry.mUid == callingUid) {
- return true;
- }
- }
-
- if (sourceRecord != null) {
- FinishedActivityEntry finishedEntry =
- mTaskIdToFinishedActivity.get(sourceRecord.getTask().mTaskId);
- if (finishedEntry != null && finishedEntry.mUid == callingUid) {
- return true;
- }
- }
- }
- }
-
- BlockActivityStart bas = null;
+ BlockActivityStart bas = new BlockActivityStart();
if (sourceRecord != null) {
- boolean passesAsmChecks = true;
Task sourceTask = sourceRecord.getTask();
+ Task taskToCheck = taskToFront ? sourceTask : targetTask;
+ bas = checkTopActivityForAsm(taskToCheck, sourceRecord.getUid(),
+ sourceRecord, bas);
+
// Allow launching into a new task (or a task matching the launched activity's
// affinity) only if the current task is foreground or mutating its own task.
// The latter can happen eg. if caller uses NEW_TASK flag and the activity being
// launched matches affinity of source task.
- if (taskToFront) {
- passesAsmChecks = sourceTask != null
- && (sourceTask.isVisible() || sourceTask == targetTask);
- }
-
- if (passesAsmChecks) {
- Task taskToCheck = taskToFront ? sourceTask : targetTask;
- bas = isTopActivityMatchingUidAbsentForAsm(taskToCheck, sourceRecord.getUid(),
- sourceRecord);
+ if (taskToFront && bas.mTopActivityMatchesSource) {
+ bas.mTopActivityMatchesSource = (sourceTask != null
+ && (sourceTask.isVisible() || sourceTask == targetTask));
}
} else if (targetTask != null && (!taskToFront || avoidMoveTaskToFront)) {
// We don't have a sourceRecord, and we're launching into an existing task.
// Allow if callingUid is top of stack.
- bas = isTopActivityMatchingUidAbsentForAsm(targetTask, callingUid,
- /*sourceRecord*/null);
+ bas = checkTopActivityForAsm(targetTask, callingUid,
+ /*sourceRecord*/null, bas);
+ } else {
+ // We're launching from a non-visible activity. Has any visible app opted in?
+ TaskDisplayArea displayArea = targetTask != null && targetTask.getDisplayArea() != null
+ ? targetTask.getDisplayArea()
+ : preferredTaskDisplayArea;
+ if (displayArea != null) {
+ ArrayList<Task> visibleTasks = displayArea.getVisibleTasks();
+ for (int i = 0; i < visibleTasks.size(); i++) {
+ Task task = visibleTasks.get(i);
+ if (visibleTasks.size() == 1 && task.isActivityTypeHomeOrRecents()) {
+ bas.optedIn(task.getTopMostActivity());
+ } else {
+ bas = checkTopActivityForAsm(task, callingUid, /*sourceRecord*/null, bas);
+ }
+ }
+ }
}
- if (bas != null && !bas.mWouldBlockActivityStartIgnoringFlag) {
+ if (bas.mTopActivityMatchesSource) {
return true;
}
@@ -1140,13 +1123,16 @@
? FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_SAME_TASK
: FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_DIFFERENT_TASK);
- boolean blockActivityStartAndFeatureEnabled = ActivitySecurityModelFeatureFlags
- .shouldRestrictActivitySwitch(callingUid)
- && (bas == null || bas.mBlockActivityStartIfFlagEnabled);
+ boolean enforceBlock = bas.mTopActivityOptedIn
+ && ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(callingUid);
+
+ boolean allowedByGracePeriod = allowedByAsmGracePeriod(callingUid, sourceRecord, targetTask,
+ balCode, taskToFront, avoidMoveTaskToFront);
String asmDebugInfo = getDebugInfoForActivitySecurity("Launch", sourceRecord,
targetRecord, targetTask, targetTopActivity, realCallingUid, balCode,
- blockActivityStartAndFeatureEnabled, taskToFront, avoidMoveTaskToFront);
+ enforceBlock, taskToFront, avoidMoveTaskToFront, allowedByGracePeriod,
+ bas.mActivityOptedIn);
FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED,
/* caller_uid */
@@ -1184,7 +1170,7 @@
String launchedFromPackageName = targetRecord.launchedFromPackage;
if (ActivitySecurityModelFeatureFlags.shouldShowToast(callingUid)) {
String toastText = ActivitySecurityModelFeatureFlags.DOC_LINK
- + (blockActivityStartAndFeatureEnabled ? " blocked " : " would block ")
+ + (enforceBlock ? " blocked " : " would block ")
+ getApplicationLabel(mService.mContext.getPackageManager(),
launchedFromPackageName);
showToast(toastText);
@@ -1192,7 +1178,7 @@
Slog.i(TAG, asmDebugInfo);
}
- if (blockActivityStartAndFeatureEnabled) {
+ if (enforceBlock) {
Slog.e(TAG, "[ASM] Abort Launching r: " + targetRecord
+ " as source: "
+ (sourceRecord != null ? sourceRecord : launchedFromPackageName)
@@ -1251,18 +1237,18 @@
// Find the first activity which matches a safe UID and is not finishing. Clear everything
// above it
+ int[] finishCount = new int[1];
boolean shouldBlockActivityStart = ActivitySecurityModelFeatureFlags
.shouldRestrictActivitySwitch(callingUid);
- int[] finishCount = new int[0];
- if (shouldBlockActivityStart
- && blockCrossUidActivitySwitchFromBelowForActivity(targetTaskTop)) {
+ BlockActivityStart bas = checkCrossUidActivitySwitchFromBelow(
+ targetTaskTop, callingUid, new BlockActivityStart());
+ if (shouldBlockActivityStart && bas.mTopActivityOptedIn) {
ActivityRecord activity = targetTask.getActivity(isLaunchingOrLaunched);
if (activity == null) {
// mStartActivity is not in task, so clear everything
activity = targetRecord;
}
- finishCount = new int[1];
targetTask.performClearTop(activity, launchFlags, finishCount);
if (finishCount[0] > 0) {
Slog.w(TAG, "Cleared top n: " + finishCount[0] + " activities from task t: "
@@ -1279,7 +1265,8 @@
Slog.i(TAG, getDebugInfoForActivitySecurity("Clear Top", sourceRecord, targetRecord,
targetTask, targetTaskTop, realCallingUid, balCode, shouldBlockActivityStart,
- /* taskToFront */ true, /* avoidMoveTaskToFront */ false));
+ /* taskToFront */ true, /* avoidMoveTaskToFront */ false,
+ /* allowedByAsmGracePeriod */ false, bas.mActivityOptedIn));
}
}
@@ -1287,7 +1274,7 @@
* Returns home if the passed in callingUid is not top of the stack, rather than returning to
* previous task.
*/
- void checkActivityAllowedToClearTask(@NonNull Task task, int callingUid,
+ void checkActivityAllowedToClearTask(@NonNull Task task, int callingUid, int callingPid,
@NonNull String callerActivityClassName) {
// We may have already checked that the callingUid has additional clearTask privileges, and
// cleared the calling identify. If so, we infer we do not need further restrictions here.
@@ -1295,14 +1282,28 @@
return;
}
+ String packageName = mService.mContext.getPackageManager().getNameForUid(callingUid);
+ BalState state = new BalState(callingUid, callingPid, packageName, INVALID_UID,
+ INVALID_PID, null, null, null, null, null, ActivityOptions.makeBasic());
+ @BalCode int balCode = checkBackgroundActivityStartAllowedByCaller(state).mCode;
+ if (balCode == BAL_ALLOW_ALLOWLISTED_UID
+ || balCode == BAL_ALLOW_ALLOWLISTED_COMPONENT
+ || balCode == BAL_ALLOW_PERMISSION
+ || balCode == BAL_ALLOW_SAW_PERMISSION
+ || balCode == BAL_ALLOW_VISIBLE_WINDOW
+ || balCode == BAL_ALLOW_NON_APP_VISIBLE_WINDOW) {
+ return;
+ }
+
TaskDisplayArea displayArea = task.getTaskDisplayArea();
if (displayArea == null) {
// If there is no associated display area, we can not return home.
return;
}
- BlockActivityStart bas = isTopActivityMatchingUidAbsentForAsm(task, callingUid, null);
- if (!bas.mWouldBlockActivityStartIgnoringFlag) {
+ BlockActivityStart bas = checkTopActivityForAsm(task, callingUid, null,
+ new BlockActivityStart());
+ if (bas.mTopActivityMatchesSource) {
return;
}
@@ -1339,8 +1340,7 @@
);
boolean restrictActivitySwitch = ActivitySecurityModelFeatureFlags
- .shouldRestrictActivitySwitch(callingUid)
- && bas.mBlockActivityStartIfFlagEnabled;
+ .shouldRestrictActivitySwitch(callingUid) && bas.mTopActivityOptedIn;
PackageManager pm = mService.mContext.getPackageManager();
String callingPackage = pm.getNameForUid(callingUid);
@@ -1381,32 +1381,30 @@
* <p>
* The 'sourceRecord' can be considered top even if it is 'finishing'
* <p>
- * Returns a class where the elements are:
- * <pre>
- * shouldBlockActivityStart: {@code true} if we should actually block the transition (takes into
- * consideration feature flag and targetSdk).
- * wouldBlockActivityStartIgnoringFlags: {@code true} if we should warn about the transition via
- * toasts. This happens if the transition would be blocked in case both the app was targeting V+
- * and the feature was enabled.
- * </pre>
*/
- private BlockActivityStart isTopActivityMatchingUidAbsentForAsm(@NonNull Task task,
- int uid, @Nullable ActivityRecord sourceRecord) {
+ private BlockActivityStart checkTopActivityForAsm(@NonNull Task task,
+ int uid, @Nullable ActivityRecord sourceRecord, BlockActivityStart bas) {
// If the source is visible, consider it 'top'.
if (sourceRecord != null && sourceRecord.isVisibleRequested()) {
- return BlockActivityStart.ACTIVITY_START_ALLOWED;
+ return bas.matchesSource();
}
- // Always allow actual top activity to clear task
+ // Always allow actual top activity
ActivityRecord topActivity = task.getTopMostActivity();
- if (topActivity != null && topActivity.isUid(uid)) {
- return BlockActivityStart.ACTIVITY_START_ALLOWED;
+ if (topActivity == null) {
+ Slog.wtf(TAG, "Activities for task: " + task + " not found.");
+ return bas.optedIn(topActivity);
+ }
+
+ bas = checkCrossUidActivitySwitchFromBelow(topActivity, uid, bas);
+ if (bas.mTopActivityMatchesSource) {
+ return bas;
}
// If UID is visible in target task, allow launch
if (task.forAllActivities((Predicate<ActivityRecord>)
ar -> ar.isUid(uid) && ar.isVisibleRequested())) {
- return BlockActivityStart.ACTIVITY_START_ALLOWED;
+ return bas.matchesSource();
}
// Consider the source activity, whether or not it is finishing. Do not consider any other
@@ -1417,82 +1415,91 @@
// Check top of stack (or the first task fragment for embedding).
topActivity = task.getActivity(topOfStackPredicate);
if (topActivity == null) {
- return new BlockActivityStart(true, true);
+ return bas;
}
- BlockActivityStart pair = blockCrossUidActivitySwitchFromBelow(topActivity, uid);
- if (!pair.mBlockActivityStartIfFlagEnabled) {
- return pair;
+ bas = checkCrossUidActivitySwitchFromBelow(topActivity, uid, bas);
+ if (bas.mTopActivityMatchesSource) {
+ return bas;
}
// Even if the top activity is not a match, we may be in an embedded activity scenario with
// an adjacent task fragment. Get the second fragment.
TaskFragment taskFragment = topActivity.getTaskFragment();
if (taskFragment == null) {
- return pair;
+ return bas;
}
TaskFragment adjacentTaskFragment = taskFragment.getAdjacentTaskFragment();
if (adjacentTaskFragment == null) {
- return pair;
+ return bas;
}
// Check the second fragment.
topActivity = adjacentTaskFragment.getActivity(topOfStackPredicate);
if (topActivity == null) {
- return new BlockActivityStart(true, true);
+ return bas;
}
- return blockCrossUidActivitySwitchFromBelow(topActivity, uid);
+ return checkCrossUidActivitySwitchFromBelow(topActivity, uid, bas);
}
/**
* Determines if a source is allowed to add or remove activities from the task,
* if the current ActivityRecord is above it in the stack
* <p>
- * A transition is blocked ({@code false} returned) if all of the following are met:
+ * A transition is blocked if all of the following are met:
* <pre>
* 1. The source activity and the current activity record belong to different apps
* (i.e, have different UIDs).
- * 2. Both the source activity and the current activity target U+
- * 3. The current activity has not set
+ * 2. The current activity target V+
+ * 3. The current app has set
+ * {@link R.styleable#AndroidManifestApplication_allowCrossUidActivitySwitchFromBelow}
+ * to {@code false}
+ * 4. The current activity has not set
* {@link ActivityRecord#setAllowCrossUidActivitySwitchFromBelow(boolean)} to {@code true}
* </pre>
*
- * Returns a class where the elements are:
- * <pre>
- * shouldBlockActivityStart: {@code true} if we should actually block the transition (takes into
- * consideration feature flag and targetSdk).
- * wouldBlockActivityStartIgnoringFlags: {@code true} if we should warn about the transition via
- * toasts. This happens if the transition would be blocked in case both the app was targeting V+
- * and the feature was enabled.
- * </pre>
*
* @param sourceUid The source (s) activity performing the state change
*/
- private BlockActivityStart blockCrossUidActivitySwitchFromBelow(ActivityRecord ar,
- int sourceUid) {
+ private BlockActivityStart checkCrossUidActivitySwitchFromBelow(ActivityRecord ar,
+ int sourceUid, BlockActivityStart bas) {
if (ar.isUid(sourceUid)) {
- return BlockActivityStart.ACTIVITY_START_ALLOWED;
+ return bas.matchesSource();
}
- if (!blockCrossUidActivitySwitchFromBelowForActivity(ar)) {
- return BlockActivityStart.ACTIVITY_START_ALLOWED;
+ // We don't need to check package level if activity has opted out.
+ if (ar.mAllowCrossUidActivitySwitchFromBelow) {
+ bas.mTopActivityOptedIn = false;
+ return bas.matchesSource();
}
- // At this point, we would block if the feature is launched and both apps were V+
- // Since we have a feature flag, we need to check that too
- // TODO(b/258792202) Replace with CompatChanges and replace Pair with boolean once feature
- // flag is removed
- boolean restrictActivitySwitch =
- ActivitySecurityModelFeatureFlags.shouldRestrictActivitySwitch(ar.getUid())
- && ActivitySecurityModelFeatureFlags
- .shouldRestrictActivitySwitch(sourceUid);
- if (restrictActivitySwitch) {
- return BlockActivityStart.BLOCK;
- } else {
- return BlockActivityStart.LOG_ONLY;
+ if (!CompatChanges.isChangeEnabled(ASM_RESTRICTIONS, ar.getUid())) {
+ return bas;
}
+
+ if (ar.isUid(SYSTEM_UID)) {
+ return bas.optedIn(ar);
+ }
+
+ String packageName = ar.packageName;
+ if (packageName == null) {
+ Slog.wtf(TAG, "Package name: " + ar + " not found.");
+ return bas.optedIn(ar);
+ }
+
+ PackageManager pm = mService.mContext.getPackageManager();
+ ApplicationInfo applicationInfo;
+
+ try {
+ applicationInfo = pm.getApplicationInfo(packageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.wtf(TAG, "Package name: " + packageName + " not found.");
+ return bas.optedIn(ar);
+ }
+
+ return applicationInfo.allowCrossUidActivitySwitchFromBelow ? bas : bas.optedIn(ar);
}
/**
@@ -1502,8 +1509,9 @@
@Nullable ActivityRecord sourceRecord, @NonNull ActivityRecord targetRecord,
@Nullable Task targetTask, @Nullable ActivityRecord targetTopActivity,
int realCallingUid, @BalCode int balCode,
- boolean blockActivityStartAndFeatureEnabled, boolean taskToFront,
- boolean avoidMoveTaskToFront) {
+ boolean enforceBlock, boolean taskToFront,
+ boolean avoidMoveTaskToFront, boolean allowedByGracePeriod,
+ ActivityRecord activityOptedIn) {
final String prefix = "[ASM] ";
Function<ActivityRecord, String> recordToString = (ar) -> {
if (ar == null) {
@@ -1519,9 +1527,16 @@
StringJoiner joiner = new StringJoiner("\n");
joiner.add(prefix + "------ Activity Security " + action + " Debug Logging Start ------");
- joiner.add(prefix + "Block Enabled: " + blockActivityStartAndFeatureEnabled);
+ joiner.add(prefix + "Block Enabled: " + enforceBlock);
+ if (!enforceBlock) {
+ joiner.add(prefix + "Feature Flag Enabled: " + android.security
+ .Flags.asmRestrictionsEnabled());
+ joiner.add(prefix + "Mendel Override: " + ActivitySecurityModelFeatureFlags
+ .asmRestrictionsEnabledForAll());
+ }
joiner.add(prefix + "ASM Version: " + ActivitySecurityModelFeatureFlags.ASM_VERSION);
joiner.add(prefix + "System Time: " + SystemClock.uptimeMillis());
+ joiner.add(prefix + "Activity Opted In: " + recordToString.apply(activityOptedIn));
boolean targetTaskMatchesSourceTask = targetTask != null
&& sourceRecord != null && sourceRecord.getTask() == targetTask;
@@ -1561,6 +1576,7 @@
joiner.add(prefix + "TaskToFront: " + taskToFront);
joiner.add(prefix + "AvoidMoveToFront: " + avoidMoveTaskToFront);
joiner.add(prefix + "BalCode: " + balCodeToString(balCode));
+ joiner.add(prefix + "Allowed By Grace Period: " + allowedByGracePeriod);
joiner.add(prefix + "LastResumedActivity: "
+ recordToString.apply(mService.mLastResumedActivity));
@@ -1588,6 +1604,44 @@
return joiner.toString();
}
+ private boolean allowedByAsmGracePeriod(int callingUid, @Nullable ActivityRecord sourceRecord,
+ @Nullable Task targetTask, @BalCode int balCode, boolean taskToFront,
+ boolean avoidMoveTaskToFront) {
+ if (balCode == BAL_ALLOW_GRACE_PERIOD) {
+ // Allow if launching into new task, and caller matches most recently finished activity
+ if (taskToFront && mTopFinishedActivity != null
+ && mTopFinishedActivity.mUid == callingUid) {
+ return true;
+ }
+
+ // Launching into existing task - allow if matches most recently finished activity
+ // within the task.
+ // We can reach here multiple ways:
+ // 1. activity in fg fires intent (taskToFront = false, sourceRecord is available)
+ // 2. activity in bg fires intent (taskToFront = false, sourceRecord is available)
+ // 3. activity in bg fires intent with NEW_FLAG (taskToFront = true,
+ // avoidMoveTaskToFront = true, sourceRecord is available)
+ // 4. activity in bg fires PI (taskToFront = true, avoidMoveTaskToFront = true,
+ // sourceRecord is not available, targetTask may be available)
+ if (!taskToFront || avoidMoveTaskToFront) {
+ if (targetTask != null) {
+ FinishedActivityEntry finishedEntry =
+ mTaskIdToFinishedActivity.get(targetTask.mTaskId);
+ if (finishedEntry != null && finishedEntry.mUid == callingUid) {
+ return true;
+ }
+ }
+
+ if (sourceRecord != null) {
+ FinishedActivityEntry finishedEntry =
+ mTaskIdToFinishedActivity.get(sourceRecord.getTask().mTaskId);
+ return finishedEntry != null && finishedEntry.mUid == callingUid;
+ }
+ }
+ }
+ return false;
+ }
+
private static boolean isSystemExemptFlagEnabled() {
return DeviceConfig.getBoolean(
NAMESPACE_WINDOW_MANAGER,
@@ -1698,55 +1752,22 @@
}
}
- /**
- * Activity level allowCrossUidActivitySwitchFromBelow defaults to false.
- * Package level defaults to true.
- * We block the launch if dev has explicitly set package level to false, and activity level has
- * not opted out
- */
- private boolean blockCrossUidActivitySwitchFromBelowForActivity(@NonNull ActivityRecord ar) {
- // We don't need to check package level if activity has opted out.
- if (ar.mAllowCrossUidActivitySwitchFromBelow) {
- return false;
- }
-
- if (ActivitySecurityModelFeatureFlags.asmRestrictionsEnabledForAll()) {
- return true;
- }
-
- String packageName = ar.packageName;
- if (packageName == null) {
- return false;
- }
-
- PackageManager pm = mService.mContext.getPackageManager();
- ApplicationInfo applicationInfo;
-
- try {
- applicationInfo = pm.getApplicationInfo(packageName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.wtf(TAG, "Package name: " + packageName + " not found.");
- return false;
- }
-
- return !applicationInfo.allowCrossUidActivitySwitchFromBelow;
- }
-
private static class BlockActivityStart {
- private static final BlockActivityStart ACTIVITY_START_ALLOWED =
- new BlockActivityStart(false, false);
- private static final BlockActivityStart LOG_ONLY = new BlockActivityStart(false, true);
- private static final BlockActivityStart BLOCK = new BlockActivityStart(true, true);
- // We should block if feature flag is enabled
- private final boolean mBlockActivityStartIfFlagEnabled;
- // Used for logging/toasts. Would we block if target sdk was V and feature was
- // enabled?
- private final boolean mWouldBlockActivityStartIgnoringFlag;
+ private boolean mTopActivityOptedIn;
+ private boolean mTopActivityMatchesSource;
+ private ActivityRecord mActivityOptedIn;
- private BlockActivityStart(boolean shouldBlockActivityStart,
- boolean wouldBlockActivityStartIgnoringFlags) {
- this.mBlockActivityStartIfFlagEnabled = shouldBlockActivityStart;
- this.mWouldBlockActivityStartIgnoringFlag = wouldBlockActivityStartIgnoringFlags;
+ BlockActivityStart optedIn(ActivityRecord activity) {
+ mTopActivityOptedIn = true;
+ if (mActivityOptedIn == null) {
+ mActivityOptedIn = activity;
+ }
+ return this;
+ }
+
+ BlockActivityStart matchesSource() {
+ mTopActivityMatchesSource = true;
+ return this;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c93cc07..7e061298 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -6706,7 +6706,7 @@
private void dumpLogStatus(PrintWriter pw) {
pw.println("WINDOW MANAGER LOGGING (dumpsys window logging)");
- if (android.tracing.Flags.perfettoProtolog()) {
+ if (android.tracing.Flags.perfettoProtologTracing()) {
pw.println("Deprecated legacy command. Use Perfetto commands instead.");
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a7a28c2..18ac0e7 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2041,6 +2041,13 @@
if (!isVisible()) {
return;
}
+ final WallpaperWindowToken wallpaperToken = mToken.asWallpaperToken();
+ if (wallpaperToken != null) {
+ if (wallpaperToken.hasVisibleNotDrawnWallpaper()) {
+ outWaitingForDrawn.add(this);
+ }
+ return;
+ }
if (mActivityRecord != null) {
if (!mActivityRecord.isVisibleRequested()) return;
if (mActivityRecord.allDrawn) {
diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java
index 424d504..6d5fc80 100644
--- a/services/core/java/com/android/server/wm/WindowTracing.java
+++ b/services/core/java/com/android/server/wm/WindowTracing.java
@@ -155,13 +155,13 @@
logAndPrintln(pw, "Stop tracing to " + mTraceFile + ". Waiting for traces to flush.");
writeTraceToFileLocked();
logAndPrintln(pw, "Trace written to " + mTraceFile + ".");
- if (!android.tracing.Flags.perfettoProtolog()) {
+ if (!android.tracing.Flags.perfettoProtologTracing()) {
((LegacyProtoLogImpl) mProtoLog).stopProtoLog(pw, true);
}
logAndPrintln(pw, "Start tracing to " + mTraceFile + ".");
mBuffer.resetBuffer();
mEnabled = mEnabledLockFree = true;
- if (!android.tracing.Flags.perfettoProtolog()) {
+ if (!android.tracing.Flags.perfettoProtologTracing()) {
((LegacyProtoLogImpl) mProtoLog).startProtoLog(pw);
}
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
index b9d89c2..28889de 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionFlags.kt
@@ -347,7 +347,8 @@
}
fun isAppOpGranted(flags: Int): Boolean =
- isPermissionGranted(flags) && !flags.hasBits(APP_OP_REVOKED)
+ isPermissionGranted(flags) && !flags.hasBits(RESTRICTION_REVOKED) &&
+ !flags.hasBits(APP_OP_REVOKED)
fun toApiFlags(flags: Int): Int {
var apiFlags = 0
diff --git a/services/tests/VpnTests/Android.bp b/services/tests/VpnTests/Android.bp
new file mode 100644
index 0000000..6ad27fc
--- /dev/null
+++ b/services/tests/VpnTests/Android.bp
@@ -0,0 +1,22 @@
+//########################################################################
+// Build FrameworksVpnTests package
+//########################################################################
+package {
+ default_team: "trendy_team_fwk_core_networking",
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "Android-Apache-2.0"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "FrameworksVpnTests",
+ srcs: [
+ "java/**/*.java",
+ "java/**/*.kt",
+ ],
+
+ test_suites: ["device-tests"],
+}
diff --git a/services/tests/VpnTests/AndroidManifest.xml b/services/tests/VpnTests/AndroidManifest.xml
new file mode 100644
index 0000000..d884084
--- /dev/null
+++ b/services/tests/VpnTests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.frameworks.tests.vpn">
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.tests.vpn"
+ android:label="Frameworks VPN Tests" />
+</manifest>
\ No newline at end of file
diff --git a/services/tests/VpnTests/AndroidTest.xml b/services/tests/VpnTests/AndroidTest.xml
new file mode 100644
index 0000000..ebeeac7
--- /dev/null
+++ b/services/tests/VpnTests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<configuration description="Runs VPN Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="FrameworksVpnTests.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="FrameworksVpnTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.tests.vpn" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/services/tests/VpnTests/OWNERS b/services/tests/VpnTests/OWNERS
new file mode 100644
index 0000000..45ea251
--- /dev/null
+++ b/services/tests/VpnTests/OWNERS
@@ -0,0 +1,2 @@
+set noparent
+file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index acddc9d..fcee70f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1694,6 +1694,39 @@
/* ignoreAnimationLimits= */ anyBoolean());
}
+ @Test
+ public void testInitialDozeBrightness_AbcIsNull() {
+ when(mDisplayManagerFlagsMock.areAutoBrightnessModesEnabled()).thenReturn(true);
+ when(mDisplayManagerFlagsMock.isDisplayOffloadEnabled()).thenReturn(true);
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, false);
+ mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ true,
+ /* isAutoBrightnessAvailable= */ false);
+ mHolder.dpc.setDisplayOffloadSession(mDisplayOffloadSession);
+ float brightness = 0.277f;
+ when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f);
+ when(mHolder.automaticBrightnessController
+ .getAutomaticScreenBrightnessBasedOnLastObservedLux(any(BrightnessEvent.class)))
+ .thenReturn(brightness);
+ when(mHolder.hbmController.getCurrentBrightnessMax())
+ .thenReturn(PowerManager.BRIGHTNESS_MAX);
+ when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_DOZE);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_DOZE;
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ // Automatic Brightness Controller is null so no initial doze brightness should be set and
+ // we should not crash
+ verify(mHolder.animator, never()).animateTo(eq(brightness),
+ /* linearSecondTarget= */ anyFloat(), /* rate= */ anyFloat(),
+ /* ignoreAnimationLimits= */ anyBoolean());
+ }
+
/**
* Creates a mock and registers it to {@link LocalServices}.
*/
@@ -1778,6 +1811,12 @@
private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
String uniqueId, boolean isEnabled) {
+ return createDisplayPowerController(displayId, uniqueId, isEnabled,
+ /* isAutoBrightnessAvailable= */ true);
+ }
+
+ private DisplayPowerControllerHolder createDisplayPowerController(int displayId,
+ String uniqueId, boolean isEnabled, boolean isAutoBrightnessAvailable) {
final DisplayPowerState displayPowerState = mock(DisplayPowerState.class);
final DualRampAnimator<DisplayPowerState> animator = mock(DualRampAnimator.class);
final AutomaticBrightnessController automaticBrightnessController =
@@ -1812,6 +1851,7 @@
final DisplayDeviceConfig config = mock(DisplayDeviceConfig.class);
setUpDisplay(displayId, uniqueId, display, device, config, isEnabled);
+ when(config.isAutoBrightnessAvailable()).thenReturn(isAutoBrightnessAvailable);
final DisplayPowerController dpc = new DisplayPowerController(
mContext, injector, mDisplayPowerCallbacksMock, mHandler,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
index 060f99b..397d77c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
@@ -16,6 +16,8 @@
package com.android.server.display.brightness;
+import static android.hardware.display.DisplayManagerInternal.DisplayPowerRequest.POLICY_BRIGHT;
+
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_MODE_IDLE;
import static org.junit.Assert.assertEquals;
@@ -43,6 +45,7 @@
getReason(BrightnessReason.REASON_DOZE, BrightnessReason.MODIFIER_LOW_POWER));
mBrightnessEvent.setPhysicalDisplayId("test");
mBrightnessEvent.setDisplayState(Display.STATE_ON);
+ mBrightnessEvent.setDisplayPolicy(POLICY_BRIGHT);
mBrightnessEvent.setLux(100.0f);
mBrightnessEvent.setPreThresholdLux(150.0f);
mBrightnessEvent.setTime(System.currentTimeMillis());
@@ -74,11 +77,12 @@
public void testToStringWorksAsExpected() {
String actualString = mBrightnessEvent.toString(false);
String expectedString =
- "BrightnessEvent: disp=1, physDisp=test, displayState=ON, brt=0.6, initBrt=25.0,"
- + " rcmdBrt=0.6, preBrt=NaN, lux=100.0, preLux=150.0, hbmMax=0.62, hbmMode=off,"
- + " rbcStrength=-1, thrmMax=0.65, powerFactor=0.2, wasShortTermModelActive=true,"
- + " flags=, reason=doze [ low_pwr ], autoBrightness=true, strategy="
- + DISPLAY_BRIGHTNESS_STRATEGY_NAME + ", autoBrightnessMode=idle";
+ "BrightnessEvent: disp=1, physDisp=test, displayState=ON, displayPolicy=BRIGHT,"
+ + " brt=0.6, initBrt=25.0, rcmdBrt=0.6, preBrt=NaN, lux=100.0, preLux=150.0,"
+ + " hbmMax=0.62, hbmMode=off, rbcStrength=-1, thrmMax=0.65, powerFactor=0.2,"
+ + " wasShortTermModelActive=true, flags=, reason=doze [ low_pwr ],"
+ + " autoBrightness=true, strategy=" + DISPLAY_BRIGHTNESS_STRATEGY_NAME
+ + ", autoBrightnessMode=idle";
assertEquals(expectedString, actualString);
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java b/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java
index 01159b1..bcb4877 100644
--- a/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/utils/PackageUtilsTest.java
@@ -32,7 +32,6 @@
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.server.companion.PackageUtils;
import com.android.server.pm.pkg.AndroidPackage;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index e59b5ea..2ba3969 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -48,7 +48,6 @@
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
import android.provider.Settings;
-import android.security.KeyStore;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
@@ -102,7 +101,6 @@
IActivityManager mActivityManager;
DevicePolicyManager mDevicePolicyManager;
DevicePolicyManagerInternal mDevicePolicyManagerInternal;
- KeyStore mKeyStore;
MockSyntheticPasswordManager mSpManager;
IAuthSecret mAuthSecretService;
WindowManagerInternal mMockWindowManager;
@@ -165,7 +163,6 @@
new LockSettingsServiceTestable.MockInjector(
mContext,
mStorage,
- mKeyStore,
mActivityManager,
setUpStorageManagerMock(),
mSpManager,
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 296d2cb..f9077c4 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -30,7 +30,6 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.storage.IStorageManager;
-import android.security.KeyStore;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.service.gatekeeper.IGateKeeperService;
@@ -41,6 +40,7 @@
import com.android.server.pm.UserManagerInternal;
import java.io.FileNotFoundException;
+import java.security.KeyStore;
public class LockSettingsServiceTestable extends LockSettingsService {
private Intent mSavedFrpNotificationIntent = null;
@@ -50,7 +50,6 @@
public static class MockInjector extends LockSettingsService.Injector {
private LockSettingsStorage mLockSettingsStorage;
- private KeyStore mKeyStore;
private IActivityManager mActivityManager;
private IStorageManager mStorageManager;
private SyntheticPasswordManager mSpManager;
@@ -62,14 +61,13 @@
public boolean mIsHeadlessSystemUserMode = false;
public boolean mIsMainUserPermanentAdmin = false;
- public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore,
- IActivityManager activityManager,
- IStorageManager storageManager, SyntheticPasswordManager spManager,
- FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager,
+ public MockInjector(Context context, LockSettingsStorage storage,
+ IActivityManager activityManager, IStorageManager storageManager,
+ SyntheticPasswordManager spManager, FakeGsiService gsiService,
+ RecoverableKeyStoreManager recoverableKeyStoreManager,
UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache) {
super(context);
mLockSettingsStorage = storage;
- mKeyStore = keyStore;
mActivityManager = activityManager;
mStorageManager = storageManager;
mSpManager = spManager;
@@ -110,11 +108,6 @@
}
@Override
- public KeyStore getKeyStore() {
- return mKeyStore;
- }
-
- @Override
public IStorageManager getStorageManager() {
return mStorageManager;
}
@@ -145,8 +138,7 @@
}
@Override
- public UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(
- java.security.KeyStore ks) {
+ public UnifiedProfilePasswordCache getUnifiedProfilePasswordCache(KeyStore ks) {
return mock(UnifiedProfilePasswordCache.class);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 67c528c..09e7b91 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -527,7 +527,8 @@
// The configuration change is still sent to the activity, even if it doesn't relaunch.
final ActivityConfigurationChangeItem expected =
- ActivityConfigurationChangeItem.obtain(activity.token, newConfig);
+ ActivityConfigurationChangeItem.obtain(activity.token, newConfig,
+ activity.getActivityWindowInfo());
verify(mClientLifecycleManager).scheduleTransactionItem(
eq(activity.app.getThread()), eq(expected));
}
@@ -599,7 +600,8 @@
final Configuration currentConfig = activity.getConfiguration();
assertEquals(expectedOrientation, currentConfig.orientation);
final ActivityConfigurationChangeItem expected =
- ActivityConfigurationChangeItem.obtain(activity.token, currentConfig);
+ ActivityConfigurationChangeItem.obtain(activity.token, currentConfig,
+ activity.getActivityWindowInfo());
verify(mClientLifecycleManager).scheduleTransactionItem(activity.app.getThread(), expected);
verify(displayRotation).onSetRequestedOrientation();
}
@@ -818,7 +820,7 @@
final ActivityConfigurationChangeItem expected =
ActivityConfigurationChangeItem.obtain(activity.token,
- activity.getConfiguration());
+ activity.getConfiguration(), activity.getActivityWindowInfo());
verify(mClientLifecycleManager).scheduleTransactionItem(
activity.app.getThread(), expected);
} finally {
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 530a39e..0f2c62d 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -53,11 +53,14 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.service.usb.UsbServiceDumpProto;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
@@ -147,6 +150,7 @@
private final UsbSettingsManager mSettingsManager;
private final UsbPermissionManager mPermissionManager;
+ static final int PACKAGE_MONITOR_OPERATION_ID = 1;
/**
* The user id of the current user. There might be several profiles (with separate user ids)
* per user.
@@ -156,6 +160,10 @@
private final Object mLock = new Object();
+ // Key: USB port id
+ // Value: A set of UIDs of requesters who request disabling usb data
+ private final ArrayMap<String, ArraySet<Integer>> mUsbDisableRequesters = new ArrayMap<>();
+
/**
* @return the {@link UsbUserSettingsManager} for the given userId
*/
@@ -261,6 +269,10 @@
if (mDeviceManager != null) {
mDeviceManager.bootCompleted();
}
+ if (android.hardware.usb.flags.Flags.enableUsbDataSignalStaking()) {
+ new PackageUninstallMonitor()
+ .register(mContext, UserHandle.ALL, BackgroundThread.getHandler());
+ }
}
/** Called when a user is unlocked. */
@@ -873,6 +885,11 @@
Objects.requireNonNull(callback, "enableUsbData: callback must not be null. opId:"
+ operationId);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ if (android.hardware.usb.flags.Flags.enableUsbDataSignalStaking()) {
+ if (!shouldUpdateUsbSignaling(portId, enable, Binder.getCallingUid())) return false;
+ }
+
final long ident = Binder.clearCallingIdentity();
boolean wait;
try {
@@ -892,6 +909,31 @@
return wait;
}
+ /**
+ * If enable = true, exclude UID from update list.
+ * If enable = false, include UID in update list.
+ * Return false if enable = true and the list is empty (no updates).
+ * Return true otherwise (let downstream decide on updates).
+ */
+ private boolean shouldUpdateUsbSignaling(String portId, boolean enable, int uid) {
+ synchronized (mUsbDisableRequesters) {
+ if (!mUsbDisableRequesters.containsKey(portId)) {
+ mUsbDisableRequesters.put(portId, new ArraySet<>());
+ }
+
+ ArraySet<Integer> uidsOfDisableRequesters = mUsbDisableRequesters.get(portId);
+
+ if (enable) {
+ uidsOfDisableRequesters.remove(uid);
+ // re-enable USB port (return true) if there are no other disable requesters
+ return uidsOfDisableRequesters.isEmpty();
+ } else {
+ uidsOfDisableRequesters.add(uid);
+ }
+ }
+ return true;
+ }
+
@Override
public void enableUsbDataWhileDocked(String portId, int operationId,
IUsbOperationInternal callback) {
@@ -1344,4 +1386,26 @@
private static String removeLastChar(String value) {
return value.substring(0, value.length() - 1);
}
+
+ /**
+ * Upon app removal, clear associated UIDs from the mUsbDisableRequesters list
+ * and re-enable USB data signaling if no remaining apps require USB disabling.
+ */
+ private class PackageUninstallMonitor extends PackageMonitor {
+ @Override
+ public void onUidRemoved(int uid) {
+ synchronized (mUsbDisableRequesters) {
+ for (String portId : mUsbDisableRequesters.keySet()) {
+ ArraySet<Integer> disabledUid = mUsbDisableRequesters.get(portId);
+ if (disabledUid != null) {
+ disabledUid.remove(uid);
+ if (disabledUid.isEmpty()) {
+ enableUsbData(portId, true, PACKAGE_MONITOR_OPERATION_ID,
+ new IUsbOperationInternal.Default());
+ }
+ }
+ }
+ }
+ }
+ }
}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index aef7158..1e1dd00 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -181,10 +181,8 @@
LocalServices.getService(ActivityManagerInternal.class));
mAtmInternal = Objects.requireNonNull(
LocalServices.getService(ActivityTaskManagerInternal.class));
- mWmInternal = Objects.requireNonNull(
- LocalServices.getService(WindowManagerInternal.class));
- mDpmInternal = Objects.requireNonNull(
- LocalServices.getService(DevicePolicyManagerInternal.class));
+ mWmInternal = LocalServices.getService(WindowManagerInternal.class);
+ mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
LegacyPermissionManagerInternal permissionManagerInternal = LocalServices.getService(
LegacyPermissionManagerInternal.class);
permissionManagerInternal.setVoiceInteractionPackagesProvider(
@@ -2750,11 +2748,17 @@
if (isAssistDataAllowed) {
visiblePackageNames.add(record.getComponentName().getPackageName());
}
- if (mDpmInternal.isUserOrganizationManaged(record.getUserId())) {
+ if (mDpmInternal != null
+ && mDpmInternal.isUserOrganizationManaged(record.getUserId())) {
isManagedProfileVisible = true;
}
}
- final ScreenCapture.ScreenshotHardwareBuffer shb = mWmInternal.takeAssistScreenshot();
+ final ScreenCapture.ScreenshotHardwareBuffer shb;
+ if (mWmInternal != null) {
+ shb = mWmInternal.takeAssistScreenshot();
+ } else {
+ shb = null;
+ }
final Bitmap bm = shb != null ? shb.asBitmap() : null;
// Now that everything is fetched, putting it in the launchIntent.
if (bm != null) {
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 82ed340..58488d1 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -573,7 +573,7 @@
* Returns the number of this subscription.
*
* Starting with API level 30, returns the number of this subscription if the calling app meets
- * one of the following requirements:
+ * at least one of the following requirements:
* <ul>
* <li>If the calling app's target SDK is API level 29 or lower and the app has been granted
* the READ_PHONE_STATE permission.
@@ -584,8 +584,8 @@
* <li>If the calling app is the default SMS role holder.
* </ul>
*
- * @return the number of this subscription, or an empty string if one of these requirements is
- * not met
+ * @return the number of this subscription, or an empty string if none of the requirements
+ * are met.
* @deprecated use {@link SubscriptionManager#getPhoneNumber(int)} instead, which takes a
* {@link #getSubscriptionId() subscription ID}.
*/
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 9789082..3f02ae9 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -549,13 +549,19 @@
public static final int CAPABILITY_TYPE_SMS = 1 << 3;
/**
- * This MmTelFeature supports Call Composer (section 2.4 of RC.20)
+ * This MmTelFeature supports Call Composer (section 2.4 of RC.20). This is the superset
+ * Call Composer, meaning that all subset types of Call Composers must be enabled when this
+ * capability is enabled
*/
public static final int CAPABILITY_TYPE_CALL_COMPOSER = 1 << 4;
/**
- * This MmTelFeature supports Business-only Call Composer
+ * This MmTelFeature supports Business-only Call Composer. This is a subset of
+ * {@code CAPABILITY_TYPE_CALL_COMPOSER} that only supports business related
+ * information for calling (e.g. information to signal if the call is a business call) in
+ * the SIP header. When enabling {@code CAPABILITY_TYPE_CALL_COMPOSER}, the
+ * {@code CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY} capability must also be enabled.
*/
@FlaggedApi(Flags.FLAG_BUSINESS_CALL_COMPOSER)
public static final int CAPABILITY_TYPE_CALL_COMPOSER_BUSINESS_ONLY = 1 << 5;
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java
index b9f1738..a963890 100644
--- a/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/PerfettoDataSourceTest.java
@@ -37,7 +37,7 @@
public class PerfettoDataSourceTest {
@Before
public void before() {
- assumeTrue(android.tracing.Flags.perfettoProtolog());
+ assumeTrue(android.tracing.Flags.perfettoProtologTracing());
}
@Test
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
index cee29dc..1dec6ab 100755
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
@@ -46,6 +46,7 @@
class TestWithGoldenOutput(unittest.TestCase):
# Test to check the generated jar files to the golden output.
+ @unittest.skip("Disabled until JDK 21 is merged and the golden files updated")
def test_compare_to_golden(self):
files = os.listdir(GOLDEN_DIR)
files.sort()
diff --git a/tools/protologtool/src/com/android/protolog/tool/Constants.kt b/tools/protologtool/src/com/android/protolog/tool/Constants.kt
index aa3e00f..4a93de9 100644
--- a/tools/protologtool/src/com/android/protolog/tool/Constants.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/Constants.kt
@@ -18,7 +18,7 @@
object Constants {
const val NAME = "protologtool"
- const val VERSION = "1.0.0"
+ const val VERSION = "2.0.0"
const val IS_ENABLED_METHOD = "isEnabled"
const val ENUM_VALUES_METHOD = "values"
}