summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java14
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java23
-rw-r--r--core/api/current.txt1
-rw-r--r--core/java/android/app/INotificationManager.aidl1
-rw-r--r--core/java/android/app/NotificationManager.java4
-rw-r--r--core/java/android/companion/AssociatedDevice.java133
-rw-r--r--core/java/android/companion/AssociationInfo.java36
-rw-r--r--core/java/android/companion/AssociationRequest.java52
-rw-r--r--core/java/android/companion/CompanionDeviceManager.java2
-rw-r--r--core/java/android/credentials/ui/Entry.java116
-rw-r--r--core/java/android/credentials/ui/ProviderData.java52
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java2
-rw-r--r--core/java/android/os/BatteryStats.java2
-rw-r--r--core/java/android/preference/SeekBarVolumizer.java18
-rw-r--r--core/java/com/android/internal/widget/ResolverDrawerLayout.java60
-rw-r--r--core/jni/android_os_Parcel.cpp11
-rw-r--r--core/jni/android_util_Binder.cpp6
-rw-r--r--core/res/res/layout/resolver_list.xml1
-rw-r--r--core/res/res/values/attrs.xml6
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/res/res/xml/irq_device_map.xml50
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java2
-rw-r--r--native/android/surface_control.cpp27
-rw-r--r--packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java68
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java66
-rw-r--r--packages/SystemUI/OWNERS29
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java23
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java376
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt210
-rw-r--r--packages/SystemUI/src/com/android/systemui/ChooserSelector.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/OWNERS5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java80
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java225
-rw-r--r--services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java3
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java11
-rw-r--r--services/companion/java/com/android/server/companion/PersistentDataStore.java16
-rw-r--r--services/core/java/android/os/BatteryStatsInternal.java31
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java2
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java29
-rw-r--r--services/core/java/com/android/server/am/UserController.java78
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java7
-rw-r--r--services/core/java/com/android/server/power/stats/CpuWakeupStats.java455
-rw-r--r--services/core/java/com/android/server/power/stats/IrqDeviceMap.java137
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java2
-rw-r--r--services/tests/servicestests/res/xml/irq_device_map_1.xml28
-rw-r--r--services/tests/servicestests/res/xml/irq_device_map_2.xml59
-rw-r--r--services/tests/servicestests/res/xml/irq_device_map_3.xml26
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java175
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/IrqDeviceMapTest.java98
60 files changed, 2215 insertions, 694 deletions
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 901796be9064..bedfa7f99d99 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -87,6 +87,7 @@ import android.content.pm.PackageManagerInternal;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryManager;
+import android.os.BatteryStatsInternal;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -116,6 +117,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
+import android.util.IntArray;
import android.util.Log;
import android.util.LongArrayQueue;
import android.util.Pair;
@@ -249,6 +251,7 @@ public class AlarmManagerService extends SystemService {
private ActivityManagerInternal mActivityManagerInternal;
private final EconomyManagerInternal mEconomyManagerInternal;
private PackageManagerInternal mPackageManagerInternal;
+ private BatteryStatsInternal mBatteryStatsInternal;
private RoleManager mRoleManager;
private volatile PermissionManagerServiceInternal mLocalPermissionManager;
@@ -2113,6 +2116,8 @@ public class AlarmManagerService extends SystemService {
LocalServices.getService(AppStandbyInternal.class);
appStandbyInternal.addListener(new AppStandbyTracker());
+ mBatteryStatsInternal = LocalServices.getService(BatteryStatsInternal.class);
+
mRoleManager = getContext().getSystemService(RoleManager.class);
mMetricsHelper.registerPuller(() -> mAlarmStore);
@@ -4783,8 +4788,12 @@ public class AlarmManagerService extends SystemService {
}
final ArraySet<Pair<String, Integer>> triggerPackages =
new ArraySet<>();
+ final IntArray wakeupUids = new IntArray();
for (int i = 0; i < triggerList.size(); i++) {
final Alarm a = triggerList.get(i);
+ if (a.wakeup) {
+ wakeupUids.add(a.uid);
+ }
if (mConstants.USE_TARE_POLICY) {
if (!isExemptFromTare(a)) {
triggerPackages.add(Pair.create(
@@ -4796,6 +4805,11 @@ public class AlarmManagerService extends SystemService {
a.sourcePackage, UserHandle.getUserId(a.creatorUid)));
}
}
+ if (wakeupUids.size() > 0 && mBatteryStatsInternal != null) {
+ mBatteryStatsInternal.noteCpuWakingActivity(
+ BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM, nowELAPSED,
+ wakeupUids.toArray());
+ }
deliverAlarmsLocked(triggerList, nowELAPSED);
mTemporaryQuotaReserve.cleanUpExpiredQuotas(nowELAPSED);
if (mConstants.USE_TARE_POLICY) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
index a1a541f92b38..b2ca3a051e36 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TareController.java
@@ -337,7 +337,6 @@ public class TareController extends StateController {
removeJobFromBillList(jobStatus, billToJobMap.keyAt(i));
}
}
- addJobToBillList(jobStatus, getRunningBill(jobStatus));
final int uid = jobStatus.getSourceUid();
if (mService.getUidBias(uid) == JobInfo.BIAS_TOP_APP) {
@@ -347,6 +346,7 @@ public class TareController extends StateController {
mTopStartedJobs.add(jobStatus);
// Top jobs won't count towards quota so there's no need to involve the EconomyManager.
} else {
+ addJobToBillList(jobStatus, getRunningBill(jobStatus));
mEconomyManagerInternal.noteOngoingEventStarted(userId, pkgName,
getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
}
@@ -357,9 +357,14 @@ public class TareController extends StateController {
public void unprepareFromExecutionLocked(JobStatus jobStatus) {
final int userId = jobStatus.getSourceUserId();
final String pkgName = jobStatus.getSourcePackageName();
- mEconomyManagerInternal.noteOngoingEventStopped(userId, pkgName,
- getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
- mTopStartedJobs.remove(jobStatus);
+ // If this method is called, then jobStatus.madeActive was never updated, so don't use it
+ // to determine if the EconomyManager was notified.
+ if (!mTopStartedJobs.remove(jobStatus)) {
+ // If the job was started while the app was top, then the EconomyManager wasn't notified
+ // of the job start.
+ mEconomyManagerInternal.noteOngoingEventStopped(userId, pkgName,
+ getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
+ }
final ArraySet<ActionBill> bills = getPossibleStartBills(jobStatus);
ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap =
@@ -382,9 +387,13 @@ public class TareController extends StateController {
boolean forUpdate) {
final int userId = jobStatus.getSourceUserId();
final String pkgName = jobStatus.getSourcePackageName();
- mEconomyManagerInternal.noteOngoingEventStopped(userId, pkgName,
- getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
- mTopStartedJobs.remove(jobStatus);
+ if (!mTopStartedJobs.remove(jobStatus) && jobStatus.madeActive > 0) {
+ // Only note the job stop if we previously told the EconomyManager that the job started.
+ // If the job was started while the app was top, then the EconomyManager wasn't notified
+ // of the job start.
+ mEconomyManagerInternal.noteOngoingEventStopped(userId, pkgName,
+ getRunningActionId(jobStatus), String.valueOf(jobStatus.getJobId()));
+ }
ArrayMap<ActionBill, ArraySet<JobStatus>> billToJobMap =
mRegisteredBillsAndJobs.get(userId, pkgName);
if (billToJobMap != null) {
diff --git a/core/api/current.txt b/core/api/current.txt
index 815ccfe0b1c6..967645158d23 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8947,6 +8947,7 @@ package android.companion {
public final class AssociationInfo implements android.os.Parcelable {
method public int describeContents();
+ method @Nullable public android.os.Parcelable getAssociatedDevice();
method @Nullable public android.net.MacAddress getDeviceMacAddress();
method @Nullable public String getDeviceProfile();
method @Nullable public CharSequence getDisplayName();
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index ddfbc6847ed0..302d1469e1fb 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -159,6 +159,7 @@ interface INotificationManager
void clearRequestedListenerHints(in INotificationListener token);
void requestHintsFromListener(in INotificationListener token, int hints);
int getHintsFromListener(in INotificationListener token);
+ int getHintsFromListenerNoToken();
void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter);
int getInterruptionFilterFromListener(in INotificationListener token);
void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 392f52a08fb5..f6d27ad08b00 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -66,9 +66,9 @@ import java.util.Objects;
/**
* Class to notify the user of events that happen. This is how you tell
- * the user that something has happened in the background. {@more}
+ * the user that something has happened in the background.
*
- * Notifications can take different forms:
+ * <p>Notifications can take different forms:
* <ul>
* <li>A persistent icon that goes in the status bar and is accessible
* through the launcher, (when the user selects it, a designated Intent
diff --git a/core/java/android/companion/AssociatedDevice.java b/core/java/android/companion/AssociatedDevice.java
new file mode 100644
index 000000000000..3758cdb680b1
--- /dev/null
+++ b/core/java/android/companion/AssociatedDevice.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.companion;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Loose wrapper around device parcelable. Device can be one of three types:
+ *
+ * <ul>
+ * <li>for classic Bluetooth - {@link android.bluetooth.BluetoothDevice}</li>
+ * <li>for Bluetooth LE - {@link android.bluetooth.le.ScanResult}</li>
+ * <li>for WiFi - {@link android.net.wifi.ScanResult}</li>
+ * </ul>
+ *
+ * This class serves as temporary wrapper to deliver a loosely-typed parcelable object from
+ * {@link com.android.companiondevicemanager.CompanionDeviceActivity} to the Companion app,
+ * and should only be used internally.
+ *
+ * @hide
+ */
+public final class AssociatedDevice implements Parcelable {
+ private static final int CLASSIC_BLUETOOTH = 0;
+ private static final int BLUETOOTH_LE = 1;
+ private static final int WIFI = 2;
+
+ @NonNull private final Parcelable mDevice;
+
+ public AssociatedDevice(@NonNull Parcelable device) {
+ mDevice = device;
+ }
+
+ private AssociatedDevice(Parcel in) {
+ Creator<? extends Parcelable> creator = getDeviceCreator(in.readInt());
+ mDevice = creator.createFromParcel(in);
+ }
+
+ /**
+ * Return device info. Cast to expected device type.
+ */
+ @NonNull
+ public Parcelable getDevice() {
+ return mDevice;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ // Parcel device type with int for efficiency
+ dest.writeInt(getDeviceType());
+ mDevice.writeToParcel(dest, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ private int getDeviceType() {
+ if (mDevice instanceof android.bluetooth.BluetoothDevice) return CLASSIC_BLUETOOTH;
+ if (mDevice instanceof android.bluetooth.le.ScanResult) return BLUETOOTH_LE;
+ if (mDevice instanceof android.net.wifi.ScanResult) return WIFI;
+ throw new UnsupportedOperationException("Unsupported device type.");
+ }
+
+ private static Creator<? extends Parcelable> getDeviceCreator(int deviceType) {
+ switch (deviceType) {
+ case CLASSIC_BLUETOOTH: return android.bluetooth.BluetoothDevice.CREATOR;
+ case BLUETOOTH_LE: return android.bluetooth.le.ScanResult.CREATOR;
+ case WIFI: return android.net.wifi.ScanResult.CREATOR;
+ default: throw new UnsupportedOperationException("Unsupported device type.");
+ }
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<AssociatedDevice> CREATOR =
+ new Parcelable.Creator<AssociatedDevice>() {
+ @Override
+ public AssociatedDevice[] newArray(int size) {
+ return new AssociatedDevice[size];
+ }
+
+ @Override
+ public AssociatedDevice createFromParcel(@NonNull Parcel in) {
+ return new AssociatedDevice(in);
+ }
+ };
+
+ @Override
+ public String toString() {
+ return "AssociatedDevice { "
+ + "device = " + mDevice
+ + " }";
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ AssociatedDevice that = (AssociatedDevice) o;
+ if (getDeviceType() != that.getDeviceType()) return false;
+
+ // TODO(b/31972115): Take out this whole part ¯\_(ツ)_/¯
+ if (mDevice instanceof android.bluetooth.le.ScanResult
+ || mDevice instanceof android.net.wifi.ScanResult) {
+ return mDevice.toString().equals(that.mDevice.toString());
+ }
+
+ return java.util.Objects.equals(mDevice, that.mDevice);
+ }
+
+ @Override
+ public int hashCode() {
+ return java.util.Objects.hash(mDevice);
+ }
+}
diff --git a/core/java/android/companion/AssociationInfo.java b/core/java/android/companion/AssociationInfo.java
index 93748f81ffa1..93964b3f4180 100644
--- a/core/java/android/companion/AssociationInfo.java
+++ b/core/java/android/companion/AssociationInfo.java
@@ -52,6 +52,7 @@ public final class AssociationInfo implements Parcelable {
private final @Nullable MacAddress mDeviceMacAddress;
private final @Nullable CharSequence mDisplayName;
private final @Nullable String mDeviceProfile;
+ private final @Nullable AssociatedDevice mAssociatedDevice;
private final boolean mSelfManaged;
private final boolean mNotifyOnDeviceNearby;
@@ -78,8 +79,9 @@ public final class AssociationInfo implements Parcelable {
*/
public AssociationInfo(int id, @UserIdInt int userId, @NonNull String packageName,
@Nullable MacAddress macAddress, @Nullable CharSequence displayName,
- @Nullable String deviceProfile, boolean selfManaged, boolean notifyOnDeviceNearby,
- boolean revoked, long timeApprovedMs, long lastTimeConnectedMs) {
+ @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice,
+ boolean selfManaged, boolean notifyOnDeviceNearby, boolean revoked,
+ long timeApprovedMs, long lastTimeConnectedMs) {
if (id <= 0) {
throw new IllegalArgumentException("Association ID should be greater than 0");
}
@@ -96,6 +98,7 @@ public final class AssociationInfo implements Parcelable {
mDeviceMacAddress = macAddress;
mDisplayName = displayName;
mDeviceProfile = deviceProfile;
+ mAssociatedDevice = associatedDevice;
mSelfManaged = selfManaged;
mNotifyOnDeviceNearby = notifyOnDeviceNearby;
@@ -160,6 +163,24 @@ public final class AssociationInfo implements Parcelable {
}
/**
+ * Companion device that was associated. Note that this field is not persisted across sessions.
+ *
+ * Cast to expected device type before use:
+ *
+ * <ul>
+ * <li>for classic Bluetooth - {@link android.bluetooth.BluetoothDevice}</li>
+ * <li>for Bluetooth LE - {@link android.bluetooth.le.ScanResult}</li>
+ * <li>for WiFi - {@link android.net.wifi.ScanResult}</li>
+ * </ul>
+ *
+ * @return the companion device that was associated, or {@code null} if the device is
+ * self-managed.
+ */
+ public @Nullable Parcelable getAssociatedDevice() {
+ return mAssociatedDevice == null ? null : mAssociatedDevice.getDevice();
+ }
+
+ /**
* @return whether the association is managed by the companion application it belongs to.
* @see AssociationRequest.Builder#setSelfManaged(boolean)
* @hide
@@ -260,6 +281,7 @@ public final class AssociationInfo implements Parcelable {
+ ", mDisplayName='" + mDisplayName + '\''
+ ", mDeviceProfile='" + mDeviceProfile + '\''
+ ", mSelfManaged=" + mSelfManaged
+ + ", mAssociatedDevice=" + mAssociatedDevice
+ ", mNotifyOnDeviceNearby=" + mNotifyOnDeviceNearby
+ ", mRevoked=" + mRevoked
+ ", mTimeApprovedMs=" + new Date(mTimeApprovedMs)
@@ -284,14 +306,15 @@ public final class AssociationInfo implements Parcelable {
&& Objects.equals(mPackageName, that.mPackageName)
&& Objects.equals(mDeviceMacAddress, that.mDeviceMacAddress)
&& Objects.equals(mDisplayName, that.mDisplayName)
- && Objects.equals(mDeviceProfile, that.mDeviceProfile);
+ && Objects.equals(mDeviceProfile, that.mDeviceProfile)
+ && Objects.equals(mAssociatedDevice, that.mAssociatedDevice);
}
@Override
public int hashCode() {
return Objects.hash(mId, mUserId, mPackageName, mDeviceMacAddress, mDisplayName,
- mDeviceProfile, mSelfManaged, mNotifyOnDeviceNearby, mRevoked, mTimeApprovedMs,
- mLastTimeConnectedMs);
+ mDeviceProfile, mAssociatedDevice, mSelfManaged, mNotifyOnDeviceNearby, mRevoked,
+ mTimeApprovedMs, mLastTimeConnectedMs);
}
@Override
@@ -309,6 +332,7 @@ public final class AssociationInfo implements Parcelable {
dest.writeTypedObject(mDeviceMacAddress, 0);
dest.writeCharSequence(mDisplayName);
dest.writeString(mDeviceProfile);
+ dest.writeTypedObject(mAssociatedDevice, 0);
dest.writeBoolean(mSelfManaged);
dest.writeBoolean(mNotifyOnDeviceNearby);
@@ -326,6 +350,7 @@ public final class AssociationInfo implements Parcelable {
mDeviceMacAddress = in.readTypedObject(MacAddress.CREATOR);
mDisplayName = in.readCharSequence();
mDeviceProfile = in.readString();
+ mAssociatedDevice = in.readTypedObject(AssociatedDevice.CREATOR);
mSelfManaged = in.readBoolean();
mNotifyOnDeviceNearby = in.readBoolean();
@@ -433,6 +458,7 @@ public final class AssociationInfo implements Parcelable {
mOriginalInfo.mDeviceMacAddress,
mOriginalInfo.mDisplayName,
mOriginalInfo.mDeviceProfile,
+ mOriginalInfo.mAssociatedDevice,
mOriginalInfo.mSelfManaged,
mNotifyOnDeviceNearby,
mRevoked,
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 75ab11531595..a2277e97c043 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -154,6 +154,11 @@ public final class AssociationRequest implements Parcelable {
private @Nullable CharSequence mDisplayName;
/**
+ * The device that was associated. Will be null for "self-managed" association.
+ */
+ private @Nullable AssociatedDevice mAssociatedDevice;
+
+ /**
* Whether the association is to be managed by the companion application.
*/
private final boolean mSelfManaged;
@@ -307,6 +312,11 @@ public final class AssociationRequest implements Parcelable {
}
/** @hide */
+ public void setAssociatedDevice(AssociatedDevice associatedDevice) {
+ mAssociatedDevice = associatedDevice;
+ }
+
+ /** @hide */
@NonNull
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public List<DeviceFilter<?>> getDeviceFilters() {
@@ -439,6 +449,16 @@ public final class AssociationRequest implements Parcelable {
/**
+ * The device that was associated. Will be null for "self-managed" association.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @Nullable AssociatedDevice getAssociatedDevice() {
+ return mAssociatedDevice;
+ }
+
+ /**
* The app package name of the application the association will belong to.
* Populated by the system.
*
@@ -503,6 +523,7 @@ public final class AssociationRequest implements Parcelable {
"deviceFilters = " + mDeviceFilters + ", " +
"deviceProfile = " + mDeviceProfile + ", " +
"displayName = " + mDisplayName + ", " +
+ "associatedDevice = " + mAssociatedDevice + ", " +
"selfManaged = " + mSelfManaged + ", " +
"forceConfirmation = " + mForceConfirmation + ", " +
"packageName = " + mPackageName + ", " +
@@ -530,6 +551,7 @@ public final class AssociationRequest implements Parcelable {
&& Objects.equals(mDeviceFilters, that.mDeviceFilters)
&& Objects.equals(mDeviceProfile, that.mDeviceProfile)
&& Objects.equals(mDisplayName, that.mDisplayName)
+ && Objects.equals(mAssociatedDevice, that.mAssociatedDevice)
&& mSelfManaged == that.mSelfManaged
&& mForceConfirmation == that.mForceConfirmation
&& Objects.equals(mPackageName, that.mPackageName)
@@ -550,6 +572,7 @@ public final class AssociationRequest implements Parcelable {
_hash = 31 * _hash + Objects.hashCode(mDeviceFilters);
_hash = 31 * _hash + Objects.hashCode(mDeviceProfile);
_hash = 31 * _hash + Objects.hashCode(mDisplayName);
+ _hash = 31 * _hash + Objects.hashCode(mAssociatedDevice);
_hash = 31 * _hash + Boolean.hashCode(mSelfManaged);
_hash = 31 * _hash + Boolean.hashCode(mForceConfirmation);
_hash = 31 * _hash + Objects.hashCode(mPackageName);
@@ -568,17 +591,19 @@ public final class AssociationRequest implements Parcelable {
int flg = 0;
if (mSingleDevice) flg |= 0x1;
- if (mSelfManaged) flg |= 0x10;
- if (mForceConfirmation) flg |= 0x20;
- if (mSkipPrompt) flg |= 0x400;
+ if (mSelfManaged) flg |= 0x20;
+ if (mForceConfirmation) flg |= 0x40;
+ if (mSkipPrompt) flg |= 0x800;
if (mDeviceProfile != null) flg |= 0x4;
if (mDisplayName != null) flg |= 0x8;
- if (mPackageName != null) flg |= 0x40;
- if (mDeviceProfilePrivilegesDescription != null) flg |= 0x100;
+ if (mAssociatedDevice != null) flg |= 0x10;
+ if (mPackageName != null) flg |= 0x80;
+ if (mDeviceProfilePrivilegesDescription != null) flg |= 0x200;
dest.writeInt(flg);
dest.writeParcelableList(mDeviceFilters, flags);
if (mDeviceProfile != null) dest.writeString(mDeviceProfile);
if (mDisplayName != null) dest.writeCharSequence(mDisplayName);
+ if (mAssociatedDevice != null) dest.writeTypedObject(mAssociatedDevice, flags);
if (mPackageName != null) dest.writeString(mPackageName);
dest.writeInt(mUserId);
if (mDeviceProfilePrivilegesDescription != null) dest.writeString(mDeviceProfilePrivilegesDescription);
@@ -598,18 +623,20 @@ public final class AssociationRequest implements Parcelable {
int flg = in.readInt();
boolean singleDevice = (flg & 0x1) != 0;
- boolean selfManaged = (flg & 0x10) != 0;
- boolean forceConfirmation = (flg & 0x20) != 0;
- boolean skipPrompt = (flg & 0x400) != 0;
+ boolean selfManaged = (flg & 0x20) != 0;
+ boolean forceConfirmation = (flg & 0x40) != 0;
+ boolean skipPrompt = (flg & 0x800) != 0;
List<DeviceFilter<?>> deviceFilters = new ArrayList<>();
in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader(),
(Class<android.companion.DeviceFilter<?>>) (Class<?>)
android.companion.DeviceFilter.class);
String deviceProfile = (flg & 0x4) == 0 ? null : in.readString();
CharSequence displayName = (flg & 0x8) == 0 ? null : (CharSequence) in.readCharSequence();
- String packageName = (flg & 0x40) == 0 ? null : in.readString();
+ AssociatedDevice associatedDevice = (flg & 0x10) == 0
+ ? null : (AssociatedDevice) in.readTypedObject(AssociatedDevice.CREATOR);
+ String packageName = (flg & 0x80) == 0 ? null : in.readString();
int userId = in.readInt();
- String deviceProfilePrivilegesDescription = (flg & 0x100) == 0 ? null : in.readString();
+ String deviceProfilePrivilegesDescription = (flg & 0x200) == 0 ? null : in.readString();
long creationTime = in.readLong();
this.mSingleDevice = singleDevice;
@@ -620,6 +647,7 @@ public final class AssociationRequest implements Parcelable {
com.android.internal.util.AnnotationValidations.validate(
DeviceProfile.class, null, mDeviceProfile);
this.mDisplayName = displayName;
+ this.mAssociatedDevice = associatedDevice;
this.mSelfManaged = selfManaged;
this.mForceConfirmation = forceConfirmation;
this.mPackageName = packageName;
@@ -648,10 +676,10 @@ public final class AssociationRequest implements Parcelable {
};
@DataClass.Generated(
- time = 1649179640045L,
+ time = 1663088980513L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java",
- inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_COMPUTER\nprivate final boolean mSingleDevice\nprivate final @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate final @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate final boolean mSelfManaged\nprivate final boolean mForceConfirmation\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.UserIdInt int mUserId\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate final long mCreationTime\nprivate boolean mSkipPrompt\npublic @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String getDeviceProfile()\npublic @android.annotation.Nullable java.lang.CharSequence getDisplayName()\npublic boolean isSelfManaged()\npublic boolean isForceConfirmation()\npublic boolean isSingleDevice()\npublic void setPackageName(java.lang.String)\npublic void setUserId(int)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic void setDisplayName(java.lang.CharSequence)\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate boolean mSelfManaged\nprivate boolean mForceConfirmation\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDisplayName(java.lang.CharSequence)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setSelfManaged(boolean)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setForceConfirmation(boolean)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genConstDefs=false)")
+ inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\npublic static final @android.annotation.RequiresPermission java.lang.String DEVICE_PROFILE_COMPUTER\nprivate final boolean mSingleDevice\nprivate final @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate final @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate @android.annotation.Nullable android.companion.AssociatedDevice mAssociatedDevice\nprivate final boolean mSelfManaged\nprivate final boolean mForceConfirmation\nprivate @android.annotation.Nullable java.lang.String mPackageName\nprivate @android.annotation.UserIdInt int mUserId\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate final long mCreationTime\nprivate boolean mSkipPrompt\npublic @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String getDeviceProfile()\npublic @android.annotation.Nullable java.lang.CharSequence getDisplayName()\npublic boolean isSelfManaged()\npublic boolean isForceConfirmation()\npublic boolean isSingleDevice()\npublic void setPackageName(java.lang.String)\npublic void setUserId(int)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic void setDisplayName(java.lang.CharSequence)\npublic void setAssociatedDevice(android.companion.AssociatedDevice)\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate boolean mSelfManaged\nprivate boolean mForceConfirmation\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDisplayName(java.lang.CharSequence)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setSelfManaged(boolean)\npublic @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setForceConfirmation(boolean)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 4142bceb2c45..90973c307a20 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -145,7 +145,7 @@ public final class CompanionDeviceManager {
* <li>for WiFi - {@link android.net.wifi.ScanResult}</li>
* </ul>
*
- * @deprecated use {@link #EXTRA_ASSOCIATION} instead.
+ * @deprecated use {@link AssociationInfo#getAssociatedDevice()} instead.
*/
@Deprecated
public static final String EXTRA_DEVICE = "android.companion.extra.DEVICE";
diff --git a/core/java/android/credentials/ui/Entry.java b/core/java/android/credentials/ui/Entry.java
new file mode 100644
index 000000000000..122c54ad8144
--- /dev/null
+++ b/core/java/android/credentials/ui/Entry.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.credentials.ui;
+
+import android.annotation.NonNull;
+import android.app.slice.Slice;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.AnnotationValidations;
+
+/**
+ * A credential, save, or action entry to be rendered.
+ *
+ * @hide
+ */
+public class Entry implements Parcelable {
+ // TODO: move to jetpack.
+ public static final String VERSION = "v1";
+ public static final Uri CREDENTIAL_MANAGER_ENTRY_URI = Uri.parse("credentialmanager.slice");
+ public static final String HINT_TITLE = "hint_title";
+ public static final String HINT_SUBTITLE = "hint_subtitle";
+ public static final String HINT_ICON = "hint_icon";
+
+ /**
+ * The intent extra key for the action chip {@code Entry} list when launching the UX activities.
+ */
+ public static final String EXTRA_ENTRY_LIST_ACTION_CHIP =
+ "android.credentials.ui.extra.ENTRY_LIST_ACTION_CHIP";
+ /**
+ * The intent extra key for the credential / save {@code Entry} list when launching the UX
+ * activities.
+ */
+ public static final String EXTRA_ENTRY_LIST_CREDENTIAL =
+ "android.credentials.ui.extra.ENTRY_LIST_CREDENTIAL";
+ /**
+ * The intent extra key for the authentication action {@code Entry} when launching the UX
+ * activities.
+ */
+ public static final String EXTRA_ENTRY_AUTHENTICATION_ACTION =
+ "android.credentials.ui.extra.ENTRY_AUTHENTICATION_ACTION";
+
+ // TODO: may be changed to other type depending on the service implementation.
+ private final int mId;
+
+ @NonNull
+ private final Slice mSlice;
+
+ protected Entry(@NonNull Parcel in) {
+ int entryId = in.readInt();
+ Slice slice = Slice.CREATOR.createFromParcel(in);
+
+ mId = entryId;
+ mSlice = slice;
+ AnnotationValidations.validate(NonNull.class, null, mSlice);
+ }
+
+ public Entry(int id, @NonNull Slice slice) {
+ mId = id;
+ mSlice = slice;
+ }
+
+ /**
+ * Returns the id of this entry that's unique within the context of the CredentialManager
+ * request.
+ */
+ public int getEntryId() {
+ return mId;
+ }
+
+ /**
+ * Returns the Slice to be rendered.
+ */
+ @NonNull
+ public Slice getSlice() {
+ return mSlice;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mId);
+ mSlice.writeToParcel(dest, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Creator<Entry> CREATOR = new Creator<Entry>() {
+ @Override
+ public Entry createFromParcel(@NonNull Parcel in) {
+ return new Entry(in);
+ }
+
+ @Override
+ public Entry[] newArray(int size) {
+ return new Entry[size];
+ }
+ };
+}
diff --git a/core/java/android/credentials/ui/ProviderData.java b/core/java/android/credentials/ui/ProviderData.java
index 49e5e49c2450..18e6ba430589 100644
--- a/core/java/android/credentials/ui/ProviderData.java
+++ b/core/java/android/credentials/ui/ProviderData.java
@@ -17,11 +17,15 @@
package android.credentials.ui;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.util.AnnotationValidations;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Holds metadata and credential entries for a single provider.
*
@@ -36,13 +40,24 @@ public class ProviderData implements Parcelable {
public static final String EXTRA_PROVIDER_DATA_LIST =
"android.credentials.ui.extra.PROVIDER_DATA_LIST";
- // TODO: add entry data.
-
@NonNull
private final String mPackageName;
+ @NonNull
+ private final List<Entry> mCredentialEntries;
+ @NonNull
+ private final List<Entry> mActionChips;
+ @Nullable
+ private final Entry mAuthenticationEntry;
- public ProviderData(@NonNull String packageName) {
+ public ProviderData(
+ @NonNull String packageName,
+ @NonNull List<Entry> credentialEntries,
+ @NonNull List<Entry> actionChips,
+ @Nullable Entry authenticationEntry) {
mPackageName = packageName;
+ mCredentialEntries = credentialEntries;
+ mActionChips = actionChips;
+ mAuthenticationEntry = authenticationEntry;
}
/** Returns the provider package name. */
@@ -51,15 +66,46 @@ public class ProviderData implements Parcelable {
return mPackageName;
}
+ @NonNull
+ public List<Entry> getCredentialEntries() {
+ return mCredentialEntries;
+ }
+
+ @NonNull
+ public List<Entry> getActionChips() {
+ return mActionChips;
+ }
+
+ @Nullable
+ public Entry getAuthenticationEntry() {
+ return mAuthenticationEntry;
+ }
+
protected ProviderData(@NonNull Parcel in) {
String packageName = in.readString8();
mPackageName = packageName;
AnnotationValidations.validate(NonNull.class, null, mPackageName);
+
+ List<Entry> credentialEntries = new ArrayList<>();
+ in.readTypedList(credentialEntries, Entry.CREATOR);
+ mCredentialEntries = credentialEntries;
+ AnnotationValidations.validate(NonNull.class, null, mCredentialEntries);
+
+ List<Entry> actionChips = new ArrayList<>();
+ in.readTypedList(actionChips, Entry.CREATOR);
+ mActionChips = actionChips;
+ AnnotationValidations.validate(NonNull.class, null, mActionChips);
+
+ Entry authenticationEntry = in.readTypedObject(Entry.CREATOR);
+ mAuthenticationEntry = authenticationEntry;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString8(mPackageName);
+ dest.writeTypedList(mCredentialEntries);
+ dest.writeTypedList(mActionChips);
+ dest.writeTypedObject(mAuthenticationEntry, flags);
}
@Override
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index 830584314039..bc6368639baa 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -299,7 +299,7 @@ public final class SQLiteDatabaseConfiguration {
if (isLegacyCompatibilityWalEnabled()) {
return SQLiteCompatibilityWalFlags.getWALSyncMode();
} else {
- return SQLiteGlobal.getDefaultSyncMode();
+ return SQLiteGlobal.getWALSyncMode();
}
} else {
return SQLiteGlobal.getDefaultSyncMode();
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index adeb722833fa..7a55a5c4ef72 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2017,7 +2017,7 @@ public abstract class BatteryStats {
public static final int EVENT_PACKAGE_INSTALLED = 0x000b;
// Event for a package being uninstalled.
public static final int EVENT_PACKAGE_UNINSTALLED = 0x000c;
- // Event for a package being uninstalled.
+ // Event for an alarm being sent out to an app.
public static final int EVENT_ALARM = 0x000d;
// Record that we have decided we need to collect new stats data.
public static final int EVENT_COLLECT_EXTERNAL_STATS = 0x000e;
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 2c0be870836a..3bf9ca044141 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -115,6 +115,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
private final int mMaxStreamVolume;
private boolean mAffectedByRingerMode;
private boolean mNotificationOrRing;
+ private final boolean mNotifAliasRing;
private final Receiver mReceiver = new Receiver();
private Handler mHandler;
@@ -179,6 +180,8 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
if (mNotificationOrRing) {
mRingerMode = mAudioManager.getRingerModeInternal();
}
+ mNotifAliasRing = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_alias_ring_notif_stream_types);
mZenMode = mNotificationManager.getZenMode();
if (hasAudioProductStrategies()) {
@@ -280,7 +283,15 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
if (zenMuted) {
mSeekBar.setProgress(mLastAudibleStreamVolume, true);
} else if (mNotificationOrRing && mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
- mSeekBar.setProgress(0, true);
+ /**
+ * the first variable above is preserved and the conditions below are made explicit
+ * so that when user attempts to slide the notification seekbar out of vibrate the
+ * seekbar doesn't wrongly snap back to 0 when the streams aren't aliased
+ */
+ if (mNotifAliasRing || mStreamType == AudioManager.STREAM_RING
+ || (mStreamType == AudioManager.STREAM_NOTIFICATION && mMuted)) {
+ mSeekBar.setProgress(0, true);
+ }
} else if (mMuted) {
mSeekBar.setProgress(0, true);
} else {
@@ -354,6 +365,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
// set the time of stop volume
if ((mStreamType == AudioManager.STREAM_VOICE_CALL
|| mStreamType == AudioManager.STREAM_RING
+ || (!mNotifAliasRing && mStreamType == AudioManager.STREAM_NOTIFICATION)
|| mStreamType == AudioManager.STREAM_ALARM)) {
sStopVolumeTime = java.lang.System.currentTimeMillis();
}
@@ -632,8 +644,8 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
}
private void updateVolumeSlider(int streamType, int streamValue) {
- final boolean streamMatch = mNotificationOrRing ? isNotificationOrRing(streamType)
- : (streamType == mStreamType);
+ final boolean streamMatch = mNotifAliasRing && mNotificationOrRing
+ ? isNotificationOrRing(streamType) : streamType == mStreamType;
if (mSeekBar != null && streamMatch && streamValue != -1) {
final boolean muted = mAudioManager.isStreamMute(mStreamType)
|| streamValue == 0;
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index af9c5a5cc0d5..52ffc984c41e 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -17,6 +17,9 @@
package com.android.internal.widget;
+import static android.content.res.Resources.ID_NULL;
+
+import android.annotation.IdRes;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
@@ -96,6 +99,8 @@ public class ResolverDrawerLayout extends ViewGroup {
private int mTopOffset;
private boolean mShowAtTop;
+ @IdRes
+ private int mIgnoreOffsetTopLimitViewId = ID_NULL;
private boolean mIsDragging;
private boolean mOpenOnClick;
@@ -156,6 +161,10 @@ public class ResolverDrawerLayout extends ViewGroup {
mIsMaxCollapsedHeightSmallExplicit =
a.hasValue(R.styleable.ResolverDrawerLayout_maxCollapsedHeightSmall);
mShowAtTop = a.getBoolean(R.styleable.ResolverDrawerLayout_showAtTop, false);
+ if (a.hasValue(R.styleable.ResolverDrawerLayout_ignoreOffsetTopLimit)) {
+ mIgnoreOffsetTopLimitViewId = a.getResourceId(
+ R.styleable.ResolverDrawerLayout_ignoreOffsetTopLimit, ID_NULL);
+ }
a.recycle();
mScrollIndicatorDrawable = mContext.getDrawable(R.drawable.scroll_indicator_material);
@@ -577,12 +586,32 @@ public class ResolverDrawerLayout extends ViewGroup {
dy -= 1.0f;
}
+ boolean isIgnoreOffsetLimitSet = false;
+ int ignoreOffsetLimit = 0;
+ View ignoreOffsetLimitView = findIgnoreOffsetLimitView();
+ if (ignoreOffsetLimitView != null) {
+ LayoutParams lp = (LayoutParams) ignoreOffsetLimitView.getLayoutParams();
+ ignoreOffsetLimit = ignoreOffsetLimitView.getBottom() + lp.bottomMargin;
+ isIgnoreOffsetLimitSet = true;
+ }
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
+ if (child.getVisibility() == View.GONE) {
+ continue;
+ }
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.ignoreOffset) {
child.offsetTopAndBottom((int) dy);
+ } else if (isIgnoreOffsetLimitSet) {
+ int top = child.getTop();
+ int targetTop = Math.max(
+ (int) (ignoreOffsetLimit + lp.topMargin + dy),
+ lp.mFixedTop);
+ if (top != targetTop) {
+ child.offsetTopAndBottom(targetTop - top);
+ }
+ ignoreOffsetLimit = child.getBottom() + lp.bottomMargin;
}
}
final boolean isCollapsedOld = mCollapseOffset != 0;
@@ -1024,6 +1053,8 @@ public class ResolverDrawerLayout extends ViewGroup {
final int rightEdge = width - getPaddingRight();
final int widthAvailable = rightEdge - leftEdge;
+ boolean isIgnoreOffsetLimitSet = false;
+ int ignoreOffsetLimit = 0;
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
@@ -1036,9 +1067,24 @@ public class ResolverDrawerLayout extends ViewGroup {
continue;
}
+ if (mIgnoreOffsetTopLimitViewId != ID_NULL && !isIgnoreOffsetLimitSet) {
+ if (mIgnoreOffsetTopLimitViewId == child.getId()) {
+ ignoreOffsetLimit = child.getBottom() + lp.bottomMargin;
+ isIgnoreOffsetLimitSet = true;
+ }
+ }
+
int top = ypos + lp.topMargin;
if (lp.ignoreOffset) {
- top -= mCollapseOffset;
+ if (!isDragging()) {
+ lp.mFixedTop = (int) (top - mCollapseOffset);
+ }
+ if (isIgnoreOffsetLimitSet) {
+ top = Math.max(ignoreOffsetLimit + lp.topMargin, (int) (top - mCollapseOffset));
+ ignoreOffsetLimit = top + child.getMeasuredHeight() + lp.bottomMargin;
+ } else {
+ top -= mCollapseOffset;
+ }
}
final int bottom = top + child.getMeasuredHeight();
@@ -1102,11 +1148,23 @@ public class ResolverDrawerLayout extends ViewGroup {
mCollapsibleHeightReserved = ss.mCollapsibleHeightReserved;
}
+ private View findIgnoreOffsetLimitView() {
+ if (mIgnoreOffsetTopLimitViewId == ID_NULL) {
+ return null;
+ }
+ View v = findViewById(mIgnoreOffsetTopLimitViewId);
+ if (v != null && v != this && v.getParent() == this && v.getVisibility() != View.GONE) {
+ return v;
+ }
+ return null;
+ }
+
public static class LayoutParams extends MarginLayoutParams {
public boolean alwaysShow;
public boolean ignoreOffset;
public boolean hasNestedScrollIndicator;
public int maxHeight;
+ int mFixedTop;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index bb4ab39a59d1..1f64df49cb56 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -101,9 +101,18 @@ static void android_os_Parcel_markSensitive(jlong nativePtr)
static void android_os_Parcel_markForBinder(JNIEnv* env, jclass clazz, jlong nativePtr,
jobject binder)
{
+ LOG_ALWAYS_FATAL_IF(binder == nullptr, "Null binder specified for markForBinder");
+
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel) {
- parcel->markForBinder(ibinderForJavaObject(env, binder));
+ sp<IBinder> nBinder = ibinderForJavaObject(env, binder);
+
+ if (nBinder == nullptr) {
+ ALOGE("Native binder in markForBinder is null for non-null jobject");
+ return;
+ }
+
+ parcel->markForBinder(nBinder);
}
}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index f28e2f636dac..9f88f3369ae8 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -798,6 +798,12 @@ sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
JavaBBinderHolder* jbh = (JavaBBinderHolder*)
env->GetLongField(obj, gBinderOffsets.mObject);
+
+ if (jbh == nullptr) {
+ ALOGE("JavaBBinderHolder null on binder");
+ return nullptr;
+ }
+
return jbh->get(env, obj);
}
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 6a200d05c2d7..a06e8a4ea4a0 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -23,6 +23,7 @@
android:maxWidth="@dimen/resolver_max_width"
android:maxCollapsedHeight="@dimen/resolver_max_collapsed_height"
android:maxCollapsedHeightSmall="56dp"
+ android:ignoreOffsetTopLimit="@id/title_container"
android:id="@id/contentPanel">
<RelativeLayout
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 69c504364544..2d832bc71684 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9620,6 +9620,12 @@
<attr name="maxCollapsedHeightSmall" format="dimension" />
<!-- Whether the Drawer should be positioned at the top rather than at the bottom. -->
<attr name="showAtTop" format="boolean" />
+ <!-- By default `ResolverDrawerLayout`’s children views with `layout_ignoreOffset` property
+ set to true have a fixed position in the layout that won’t be affected by the drawer’s
+ movements. This property alternates that behavior. It specifies a child view’s id that
+ will push all ignoreOffset siblings below it when the drawer is moved i.e. setting the
+ top limit the ignoreOffset elements. -->
+ <attr name="ignoreOffsetTopLimit" format="reference" />
</declare-styleable>
<declare-styleable name="MessagingLinearLayout">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2fbf8034c27e..530891f18933 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1626,6 +1626,7 @@
<java-symbol type="xml" name="password_kbd_symbols_shift" />
<java-symbol type="xml" name="power_profile" />
<java-symbol type="xml" name="power_profile_test" />
+ <java-symbol type="xml" name="irq_device_map" />
<java-symbol type="xml" name="sms_short_codes" />
<java-symbol type="xml" name="audio_assets" />
<java-symbol type="xml" name="global_keys" />
diff --git a/core/res/res/xml/irq_device_map.xml b/core/res/res/xml/irq_device_map.xml
new file mode 100644
index 000000000000..86a44d6a9fe0
--- /dev/null
+++ b/core/res/res/xml/irq_device_map.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<irq-device-map>
+ <!-- This file maps devices (chips) that can send IRQs to the CPU (and bring it out of sleep) to
+ logical subsystems in userspace code. Since each Android device has its own uniquely
+ designed chipset, this mapping is expected to be empty by default and should be overridden
+ by device specific configs.
+ This mapping helps the system to meaningfully attribute CPU wakeups to logical work that
+ happened on the device. The devices are referred to by their names as defined in the kernel.
+ Currently defined subsystems are:
+ - Alarm: Use this to denote wakeup alarms requested by apps via the AlarmManager API.
+
+ The overlay should use tags <device> and <subsystem> to describe this mapping in the
+ following way:
+
+ <irq-device-map>
+ <device name="device_name_1">
+ <subsystem>Subsystem1</subsystem>
+ <subsystem>Subsystem2</subsystem>
+ :
+ :
+ </device>
+ <device name="device_name_2">
+ :
+ </device>
+ :
+ </irq-device-map>
+
+ The tag <device> should have a "name" attribute specifying the kernel name of the device.
+ Each <device> tag can then enclose multiple <subsystem> tags. Each <subsystem> tag should
+ enclose the name of the logical subsystems (one of the ones defined above) as text.
+ Undefined subsystem names will be ignored by the framework.
+ -->
+</irq-device-map> \ No newline at end of file
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 1174b685e92c..31a9592968c5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -94,7 +94,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
ActivityEmbeddingComponent {
static final String TAG = "SplitController";
static final boolean ENABLE_SHELL_TRANSITIONS =
- SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
+ SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
@VisibleForTesting
@GuardedBy("mLock")
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index aaaccd837298..1372f4d64678 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -81,7 +81,7 @@ public class Transitions implements RemoteCallable<Transitions> {
/** Set to {@code true} to enable shell transitions. */
public static final boolean ENABLE_SHELL_TRANSITIONS =
- SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
+ SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS
&& SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index 5ee8bf3006a3..1a1bebd28aef 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -61,7 +61,7 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public final class StageTaskListenerTests extends ShellTestCase {
private static final boolean ENABLE_SHELL_TRANSITIONS =
- SystemProperties.getBoolean("persist.wm.debug.shell_transit", false);
+ SystemProperties.getBoolean("persist.wm.debug.shell_transit", true);
@Mock
private ShellTaskOrganizer mTaskOrganizer;
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 21d4d8023e54..891379977a6c 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -139,8 +139,18 @@ void ASurfaceControl_release(ASurfaceControl* aSurfaceControl) {
}
ASurfaceControl* ASurfaceControl_fromSurfaceControl(JNIEnv* env, jobject surfaceControlObj) {
- return reinterpret_cast<ASurfaceControl*>(
- android_view_SurfaceControl_getNativeSurfaceControl(env, surfaceControlObj));
+ LOG_ALWAYS_FATAL_IF(!env,
+ "nullptr passed to ASurfaceControl_fromSurfaceControl as env argument");
+ LOG_ALWAYS_FATAL_IF(!surfaceControlObj,
+ "nullptr passed to ASurfaceControl_fromSurfaceControl as surfaceControlObj "
+ "argument");
+ SurfaceControl* surfaceControl =
+ android_view_SurfaceControl_getNativeSurfaceControl(env, surfaceControlObj);
+ LOG_ALWAYS_FATAL_IF(!surfaceControl,
+ "surfaceControlObj passed to ASurfaceControl_fromSurfaceControl is not "
+ "valid");
+ SurfaceControl_acquire(surfaceControl);
+ return reinterpret_cast<ASurfaceControl*>(surfaceControl);
}
struct ASurfaceControlStats {
@@ -200,8 +210,17 @@ void ASurfaceTransaction_delete(ASurfaceTransaction* aSurfaceTransaction) {
}
ASurfaceTransaction* ASurfaceTransaction_fromTransaction(JNIEnv* env, jobject transactionObj) {
- return reinterpret_cast<ASurfaceTransaction*>(
- android_view_SurfaceTransaction_getNativeSurfaceTransaction(env, transactionObj));
+ LOG_ALWAYS_FATAL_IF(!env,
+ "nullptr passed to ASurfaceTransaction_fromTransaction as env argument");
+ LOG_ALWAYS_FATAL_IF(!transactionObj,
+ "nullptr passed to ASurfaceTransaction_fromTransaction as transactionObj "
+ "argument");
+ Transaction* transaction =
+ android_view_SurfaceTransaction_getNativeSurfaceTransaction(env, transactionObj);
+ LOG_ALWAYS_FATAL_IF(!transaction,
+ "surfaceControlObj passed to ASurfaceTransaction_fromTransaction is not "
+ "valid");
+ return reinterpret_cast<ASurfaceTransaction*>(transaction);
}
void ASurfaceTransaction_apply(ASurfaceTransaction* aSurfaceTransaction) {
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index 9818ee78b2b1..56715b422069 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -46,6 +46,7 @@ import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.companion.AssociatedDevice;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
@@ -331,6 +332,7 @@ public class CompanionDeviceActivity extends FragmentActivity implements
private void onUserSelectedDevice(@NonNull DeviceFilterPair<?> selectedDevice) {
final MacAddress macAddress = selectedDevice.getMacAddress();
mRequest.setDisplayName(selectedDevice.getDisplayName());
+ mRequest.setAssociatedDevice(new AssociatedDevice(selectedDevice.getDevice()));
onAssociationApproved(macAddress);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java
index c2034f89e18a..0c0f5a8a808e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminController.java
@@ -18,12 +18,9 @@ package com.android.settingslib.enterprise;
import static java.util.Objects.requireNonNull;
-import android.app.admin.DevicePolicyManager;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.os.UserManager;
-import android.text.TextUtils;
import androidx.annotation.Nullable;
@@ -69,50 +66,11 @@ final class ManagedDeviceActionDisabledByAdminController
mResolveActivityChecker = requireNonNull(resolveActivityChecker);
}
- @Override
- public void setupLearnMoreButton(Context context) {
- assertInitialized();
-
- String url = mStringProvider.getLearnMoreHelpPageUrl();
-
- if (!TextUtils.isEmpty(url)
- && canLaunchHelpPageInPreferredOrCurrentUser(context, url, mPreferredUserHandle)) {
- setupLearnMoreButtonToLaunchHelpPage(context, url, mPreferredUserHandle);
- } else {
- mLauncher.setupLearnMoreButtonToShowAdminPolicies(context, mEnforcementAdminUserId,
- mEnforcedAdmin);
- }
- }
-
- private boolean canLaunchHelpPageInPreferredOrCurrentUser(
- Context context, String url, UserHandle preferredUserHandle) {
- PackageManager packageManager = context.getPackageManager();
- if (mLauncher.canLaunchHelpPage(
- packageManager, url, preferredUserHandle, mResolveActivityChecker)
- && mForegroundUserChecker.isUserForeground(context, preferredUserHandle)) {
- return true;
- }
- return mLauncher.canLaunchHelpPage(
- packageManager, url, context.getUser(), mResolveActivityChecker);
- }
-
/**
- * Sets up the "Learn more" button to launch the web help page in the {@code
- * preferredUserHandle} user. If not possible to launch it there, it sets up the button to
- * launch it in the current user instead.
+ * We don't show Learn More button in Admin-Support Dialog anymore.
*/
- private void setupLearnMoreButtonToLaunchHelpPage(
- Context context, String url, UserHandle preferredUserHandle) {
- PackageManager packageManager = context.getPackageManager();
- if (mLauncher.canLaunchHelpPage(
- packageManager, url, preferredUserHandle, mResolveActivityChecker)
- && mForegroundUserChecker.isUserForeground(context, preferredUserHandle)) {
- mLauncher.setupLearnMoreButtonToLaunchHelpPage(context, url, preferredUserHandle);
- }
- if (mLauncher.canLaunchHelpPage(
- packageManager, url, context.getUser(), mResolveActivityChecker)) {
- mLauncher.setupLearnMoreButtonToLaunchHelpPage(context, url, context.getUser());
- }
+ @Override
+ public void setupLearnMoreButton(Context context) {
}
private static boolean isUserForeground(Context context, UserHandle userHandle) {
@@ -122,25 +80,7 @@ final class ManagedDeviceActionDisabledByAdminController
@Override
public String getAdminSupportTitle(@Nullable String restriction) {
- if (restriction == null) {
- return mStringProvider.getDefaultDisabledByPolicyTitle();
- }
- switch (restriction) {
- case UserManager.DISALLOW_ADJUST_VOLUME:
- return mStringProvider.getDisallowAdjustVolumeTitle();
- case UserManager.DISALLOW_OUTGOING_CALLS:
- return mStringProvider.getDisallowOutgoingCallsTitle();
- case UserManager.DISALLOW_SMS:
- return mStringProvider.getDisallowSmsTitle();
- case DevicePolicyManager.POLICY_DISABLE_CAMERA:
- return mStringProvider.getDisableCameraTitle();
- case DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE:
- return mStringProvider.getDisableScreenCaptureTitle();
- case DevicePolicyManager.POLICY_SUSPEND_PACKAGES:
- return mStringProvider.getSuspendPackagesTitle();
- default:
- return mStringProvider.getDefaultDisabledByPolicyTitle();
- }
+ return mStringProvider.getDefaultDisabledByPolicyTitle();
}
@Override
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java
index bc9bdecb904d..7b885660ccea 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/enterprise/ManagedDeviceActionDisabledByAdminControllerTest.java
@@ -18,10 +18,7 @@ package com.android.settingslib.enterprise;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCED_ADMIN;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.ENFORCEMENT_ADMIN_USER_ID;
-import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.LEARN_MORE_ACTION_LAUNCH_HELP_PAGE;
-import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.LEARN_MORE_ACTION_SHOW_ADMIN_POLICIES;
import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.SUPPORT_MESSAGE;
-import static com.android.settingslib.enterprise.ActionDisabledByAdminControllerTestUtils.URL;
import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.DEFAULT_DISABLED_BY_POLICY_CONTENT;
import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.DEFAULT_DISABLED_BY_POLICY_TITLE;
import static com.android.settingslib.enterprise.FakeDeviceAdminStringProvider.DISALLOW_ADJUST_VOLUME_TITLE;
@@ -64,67 +61,6 @@ public class ManagedDeviceActionDisabledByAdminControllerTest {
}
@Test
- public void setupLearnMoreButton_noUrl_negativeButtonSet() {
- ManagedDeviceActionDisabledByAdminController controller = createController(EMPTY_URL);
-
- controller.setupLearnMoreButton(mContext);
-
- mTestUtils.assertLearnMoreAction(LEARN_MORE_ACTION_SHOW_ADMIN_POLICIES);
- }
-
- @Test
- public void setupLearnMoreButton_validUrl_foregroundUser_launchesHelpPage() {
- ManagedDeviceActionDisabledByAdminController controller = createController(
- URL,
- /* isUserForeground= */ true,
- /* preferredUserHandle= */ MANAGED_USER,
- /* userContainingBrowser= */ MANAGED_USER);
-
- controller.setupLearnMoreButton(mContext);
-
- mTestUtils.assertLearnMoreAction(LEARN_MORE_ACTION_LAUNCH_HELP_PAGE);
- }
-
- @Test
- public void setupLearnMoreButton_validUrl_browserInPreferredUser_notForeground_showsAdminPolicies() {
- ManagedDeviceActionDisabledByAdminController controller = createController(
- URL,
- /* isUserForeground= */ false,
- /* preferredUserHandle= */ MANAGED_USER,
- /* userContainingBrowser= */ MANAGED_USER);
-
- controller.setupLearnMoreButton(mContext);
-
- mTestUtils.assertLearnMoreAction(LEARN_MORE_ACTION_SHOW_ADMIN_POLICIES);
- }
-
- @Test
- public void setupLearnMoreButton_validUrl_browserInCurrentUser_launchesHelpPage() {
- ManagedDeviceActionDisabledByAdminController controller = createController(
- URL,
- /* isUserForeground= */ false,
- /* preferredUserHandle= */ MANAGED_USER,
- /* userContainingBrowser= */ mContext.getUser());
-
- controller.setupLearnMoreButton(mContext);
-
- mTestUtils.assertLearnMoreAction(LEARN_MORE_ACTION_LAUNCH_HELP_PAGE);
- }
-
- @Test
- public void setupLearnMoreButton_validUrl_browserNotOnAnyUser_showsAdminPolicies() {
- ManagedDeviceActionDisabledByAdminController controller = createController(
- URL,
- /* isUserForeground= */ false,
- /* preferredUserHandle= */ MANAGED_USER,
- /* userContainingBrowser= */ null);
-
- controller.setupLearnMoreButton(mContext);
-
- mTestUtils.assertLearnMoreAction(LEARN_MORE_ACTION_SHOW_ADMIN_POLICIES);
- }
-
- @Test
public void getAdminSupportTitleResource_noRestriction_works() {
ManagedDeviceActionDisabledByAdminController controller = createController();
@@ -137,7 +73,7 @@ public class ManagedDeviceActionDisabledByAdminControllerTest {
ManagedDeviceActionDisabledByAdminController controller = createController();
assertThat(controller.getAdminSupportTitle(RESTRICTION))
- .isEqualTo(SUPPORT_TITLE_FOR_RESTRICTION);
+ .isEqualTo(DEFAULT_DISABLED_BY_POLICY_TITLE);
}
@Test
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 3325eec0c451..6d61fd86e39d 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -5,20 +5,32 @@ set noparent
dsandler@android.com
aaliomer@google.com
+aaronjli@google.com
+acul@google.com
adamcohen@google.com
+aioana@google.com
alexflo@google.com
+andonian@google.com
+aroederer@google.com
arteiro@google.com
asc@google.com
awickham@google.com
+ayepin@google.com
+bbade@google.com
beverlyt@google.com
-brzezinski@google.com
+bhinegardner@google.com
+bhnm@google.com
brycelee@google.com
+brzezinski@google.com
caitlinshk@google.com
+chandruis@google.com
chrisgollner@google.com
+cinek@google.com
dupin@google.com
ethibodeau@google.com
evanlaird@google.com
florenceyang@google.com
+gallmann@google.com
gwasserman@google.com
hwwang@google.com
hyunyoungs@google.com
@@ -35,31 +47,41 @@ joshtrask@google.com
juliacr@google.com
juliatuttle@google.com
justinkoh@google.com
+justinweir@google.com
kozynski@google.com
kprevas@google.com
+lusilva@google.com
lynhan@google.com
madym@google.com
mankoff@google.com
+mateuszc@google.com
+michaelmikhil@google.com
+michschn@google.com
mkephart@google.com
mpietal@google.com
mrcasey@google.com
mrenouf@google.com
nickchameyev@google.com
nicomazz@google.com
+nijamkin@google.com
ogunwale@google.com
+omarmt@google.com
+patmanning@google.com
peanutbutter@google.com
peskal@google.com
pinyaoting@google.com
pixel@google.com
pomini@google.com
rahulbanerjee@google.com
+rasheedlewis@google.com
roosa@google.com
+saff@google.com
santie@google.com
shanh@google.com
snoeberger@google.com
steell@google.com
-sfufa@google.com
stwu@google.com
+syeonlee@google.com
sunnygoyal@google.com
susikp@google.com
thiruram@google.com
@@ -70,10 +92,13 @@ vadimt@google.com
victortulias@google.com
winsonc@google.com
wleshner@google.com
+xilei@google.com
xuqiu@google.com
+yeinj@google.com
yuandizhou@google.com
yurilin@google.com
zakcohen@google.com
+zoepage@google.com
#Android TV
rgl@google.com
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
index 5486adbd2b8e..6ec65cebf840 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_password_view.xml
@@ -24,7 +24,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
androidprv:layout_maxWidth="@dimen/keyguard_security_width"
- androidprv:layout_maxHeight="@dimen/keyguard_security_height"
android:layout_gravity="center_horizontal|bottom"
android:gravity="bottom"
>
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
index 00f1c0108d0b..207f3440d38b 100644
--- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java
@@ -15,6 +15,12 @@
*/
package com.android.keyguard;
+import static androidx.constraintlayout.widget.ConstraintSet.BOTTOM;
+import static androidx.constraintlayout.widget.ConstraintSet.END;
+import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
+import static androidx.constraintlayout.widget.ConstraintSet.START;
+import static androidx.constraintlayout.widget.ConstraintSet.TOP;
+
import android.annotation.Nullable;
import android.app.admin.IKeyguardCallback;
import android.app.admin.IKeyguardClient;
@@ -30,7 +36,10 @@ import android.util.Log;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
-import android.view.ViewGroup;
+import android.view.View;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.constraintlayout.widget.ConstraintSet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
@@ -49,7 +58,7 @@ public class AdminSecondaryLockScreenController {
private static final int REMOTE_CONTENT_READY_TIMEOUT_MILLIS = 500;
private final KeyguardUpdateMonitor mUpdateMonitor;
private final Context mContext;
- private final ViewGroup mParent;
+ private final ConstraintLayout mParent;
private AdminSecurityView mView;
private Handler mHandler;
private IKeyguardClient mClient;
@@ -156,6 +165,7 @@ public class AdminSecondaryLockScreenController {
mUpdateMonitor = updateMonitor;
mKeyguardCallback = callback;
mView = new AdminSecurityView(mContext, mSurfaceHolderCallback);
+ mView.setId(View.generateViewId());
}
/**
@@ -167,6 +177,15 @@ public class AdminSecondaryLockScreenController {
}
if (!mView.isAttachedToWindow()) {
mParent.addView(mView);
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.clone(mParent);
+ constraintSet.connect(mView.getId(), TOP, PARENT_ID, TOP);
+ constraintSet.connect(mView.getId(), START, PARENT_ID, START);
+ constraintSet.connect(mView.getId(), END, PARENT_ID, END);
+ constraintSet.connect(mView.getId(), BOTTOM, PARENT_ID, BOTTOM);
+ constraintSet.constrainHeight(mView.getId(), ConstraintSet.MATCH_CONSTRAINT);
+ constraintSet.constrainWidth(mView.getId(), ConstraintSet.MATCH_CONSTRAINT);
+ constraintSet.applyTo(mParent);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 353c3698dce4..c34db1532d6c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -21,15 +21,23 @@ import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
+import static androidx.constraintlayout.widget.ConstraintSet.BOTTOM;
+import static androidx.constraintlayout.widget.ConstraintSet.CHAIN_SPREAD;
+import static androidx.constraintlayout.widget.ConstraintSet.END;
+import static androidx.constraintlayout.widget.ConstraintSet.LEFT;
+import static androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT;
+import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
+import static androidx.constraintlayout.widget.ConstraintSet.RIGHT;
+import static androidx.constraintlayout.widget.ConstraintSet.START;
+import static androidx.constraintlayout.widget.ConstraintSet.TOP;
+import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT;
+
import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY;
import static java.lang.Integer.max;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.admin.DevicePolicyManager;
@@ -44,12 +52,12 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.UserManager;
import android.provider.Settings;
+import android.transition.TransitionManager;
import android.util.AttributeSet;
import android.util.Log;
import android.util.MathUtils;
import android.util.TypedValue;
import android.view.GestureDetector;
-import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
@@ -59,8 +67,6 @@ import android.view.ViewGroup;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
import android.view.WindowManager;
-import android.view.animation.AnimationUtils;
-import android.view.animation.Interpolator;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -68,8 +74,9 @@ import android.widget.TextView;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.constraintlayout.widget.ConstraintSet;
import androidx.dynamicanimation.animation.DynamicAnimation;
import androidx.dynamicanimation.animation.SpringAnimation;
@@ -92,9 +99,9 @@ import com.android.systemui.util.settings.GlobalSettings;
import java.util.ArrayList;
import java.util.List;
-import java.util.function.Consumer;
-public class KeyguardSecurityContainer extends FrameLayout {
+/** Determines how the bouncer is displayed to the user. */
+public class KeyguardSecurityContainer extends ConstraintLayout {
static final int USER_TYPE_PRIMARY = 1;
static final int USER_TYPE_WORK_PROFILE = 2;
static final int USER_TYPE_SECONDARY_USER = 3;
@@ -125,15 +132,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
// How much to scale the default slop by, to avoid accidental drags.
private static final float SLOP_SCALE = 4f;
- private static final long IME_DISAPPEAR_DURATION_MS = 125;
-
- // The duration of the animation to switch security sides.
- private static final long SECURITY_SHIFT_ANIMATION_DURATION_MS = 500;
-
- // How much of the switch sides animation should be dedicated to fading the security out. The
- // remainder will fade it back in again.
- private static final float SECURITY_SHIFT_ANIMATION_FADE_OUT_PROPORTION = 0.2f;
-
@VisibleForTesting
KeyguardSecurityViewFlipper mSecurityViewFlipper;
private GlobalSettings mGlobalSettings;
@@ -649,47 +647,8 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int maxHeight = 0;
- int maxWidth = 0;
- int childState = 0;
-
- for (int i = 0; i < getChildCount(); i++) {
- final View view = getChildAt(i);
- if (view.getVisibility() != GONE) {
- int updatedWidthMeasureSpec = mViewMode.getChildWidthMeasureSpec(widthMeasureSpec);
- final LayoutParams lp = (LayoutParams) view.getLayoutParams();
-
- // When using EXACTLY spec, measure will use the layout width if > 0. Set before
- // measuring the child
- lp.width = MeasureSpec.getSize(updatedWidthMeasureSpec);
- measureChildWithMargins(view, updatedWidthMeasureSpec, 0,
- heightMeasureSpec, 0);
-
- maxWidth = Math.max(maxWidth,
- view.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
- maxHeight = Math.max(maxHeight,
- view.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
- childState = combineMeasuredStates(childState, view.getMeasuredState());
- }
- }
-
- maxWidth += getPaddingLeft() + getPaddingRight();
- maxHeight += getPaddingTop() + getPaddingBottom();
-
- // Check against our minimum height and width
- maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
- maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
-
- setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
- resolveSizeAndState(maxHeight, heightMeasureSpec,
- childState << MEASURED_HEIGHT_STATE_SHIFT));
- }
-
- @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
-
int width = right - left;
if (changed && mWidth != width) {
mWidth = width;
@@ -761,7 +720,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
* Enscapsulates the differences between bouncer modes for the container.
*/
interface ViewMode {
- default void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+ default void init(@NonNull ConstraintLayout v, @NonNull GlobalSettings globalSettings,
@NonNull KeyguardSecurityViewFlipper viewFlipper,
@NonNull FalsingManager falsingManager,
@NonNull UserSwitcherController userSwitcherController) {};
@@ -787,11 +746,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
/** On notif tap, this animation will run */
default void startAppearAnimation(SecurityMode securityMode) {};
- /** Override to alter the width measure spec to perhaps limit the ViewFlipper size */
- default int getChildWidthMeasureSpec(int parentWidthMeasureSpec) {
- return parentWidthMeasureSpec;
- }
-
/** Called when we are setting a new ViewMode */
default void onDestroy() {};
}
@@ -801,13 +755,12 @@ public class KeyguardSecurityContainer extends FrameLayout {
* screen devices
*/
abstract static class SidedSecurityMode implements ViewMode {
- @Nullable private ValueAnimator mRunningSecurityShiftAnimator;
private KeyguardSecurityViewFlipper mViewFlipper;
- private ViewGroup mView;
+ private ConstraintLayout mView;
private GlobalSettings mGlobalSettings;
private int mDefaultSideSetting;
- public void init(ViewGroup v, KeyguardSecurityViewFlipper viewFlipper,
+ public void init(ConstraintLayout v, KeyguardSecurityViewFlipper viewFlipper,
GlobalSettings globalSettings, boolean leftAlignedByDefault) {
mView = v;
mViewFlipper = viewFlipper;
@@ -850,127 +803,6 @@ public class KeyguardSecurityContainer extends FrameLayout {
protected abstract void updateSecurityViewLocation(boolean leftAlign, boolean animate);
- protected void translateSecurityViewLocation(boolean leftAlign, boolean animate) {
- translateSecurityViewLocation(leftAlign, animate, i -> {});
- }
-
- /**
- * Moves the inner security view to the correct location with animation. This is triggered
- * when the user double taps on the side of the screen that is not currently occupied by
- * the security view.
- */
- protected void translateSecurityViewLocation(boolean leftAlign, boolean animate,
- Consumer<Float> securityAlphaListener) {
- if (mRunningSecurityShiftAnimator != null) {
- mRunningSecurityShiftAnimator.cancel();
- mRunningSecurityShiftAnimator = null;
- }
-
- int targetTranslation = leftAlign
- ? 0 : mView.getMeasuredWidth() - mViewFlipper.getWidth();
-
- if (animate) {
- // This animation is a bit fun to implement. The bouncer needs to move, and fade
- // in/out at the same time. The issue is, the bouncer should only move a short
- // amount (120dp or so), but obviously needs to go from one side of the screen to
- // the other. This needs a pretty custom animation.
- //
- // This works as follows. It uses a ValueAnimation to simply drive the animation
- // progress. This animator is responsible for both the translation of the bouncer,
- // and the current fade. It will fade the bouncer out while also moving it along the
- // 120dp path. Once the bouncer is fully faded out though, it will "snap" the
- // bouncer closer to its destination, then fade it back in again. The effect is that
- // the bouncer will move from 0 -> X while fading out, then
- // (destination - X) -> destination while fading back in again.
- // TODO(b/208250221): Make this animation properly abortable.
- Interpolator positionInterpolator = AnimationUtils.loadInterpolator(
- mView.getContext(), android.R.interpolator.fast_out_extra_slow_in);
- Interpolator fadeOutInterpolator = Interpolators.FAST_OUT_LINEAR_IN;
- Interpolator fadeInInterpolator = Interpolators.LINEAR_OUT_SLOW_IN;
-
- mRunningSecurityShiftAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
- mRunningSecurityShiftAnimator.setDuration(SECURITY_SHIFT_ANIMATION_DURATION_MS);
- mRunningSecurityShiftAnimator.setInterpolator(Interpolators.LINEAR);
-
- int initialTranslation = (int) mViewFlipper.getTranslationX();
- int totalTranslation = (int) mView.getResources().getDimension(
- R.dimen.security_shift_animation_translation);
-
- final boolean shouldRestoreLayerType = mViewFlipper.hasOverlappingRendering()
- && mViewFlipper.getLayerType() != View.LAYER_TYPE_HARDWARE;
- if (shouldRestoreLayerType) {
- mViewFlipper.setLayerType(View.LAYER_TYPE_HARDWARE, /* paint= */null);
- }
-
- float initialAlpha = mViewFlipper.getAlpha();
-
- mRunningSecurityShiftAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mRunningSecurityShiftAnimator = null;
- }
- });
- mRunningSecurityShiftAnimator.addUpdateListener(animation -> {
- float switchPoint = SECURITY_SHIFT_ANIMATION_FADE_OUT_PROPORTION;
- boolean isFadingOut = animation.getAnimatedFraction() < switchPoint;
-
- int currentTranslation = (int) (positionInterpolator.getInterpolation(
- animation.getAnimatedFraction()) * totalTranslation);
- int translationRemaining = totalTranslation - currentTranslation;
-
- // Flip the sign if we're going from right to left.
- if (leftAlign) {
- currentTranslation = -currentTranslation;
- translationRemaining = -translationRemaining;
- }
-
- float opacity;
- if (isFadingOut) {
- // The bouncer fades out over the first X%.
- float fadeOutFraction = MathUtils.constrainedMap(
- /* rangeMin= */1.0f,
- /* rangeMax= */0.0f,
- /* valueMin= */0.0f,
- /* valueMax= */switchPoint,
- animation.getAnimatedFraction());
- opacity = fadeOutInterpolator.getInterpolation(fadeOutFraction);
-
- // When fading out, the alpha needs to start from the initial opacity of the
- // view flipper, otherwise we get a weird bit of jank as it ramps back to
- // 100%.
- mViewFlipper.setAlpha(opacity * initialAlpha);
-
- // Animate away from the source.
- mViewFlipper.setTranslationX(initialTranslation + currentTranslation);
- } else {
- // And in again over the remaining (100-X)%.
- float fadeInFraction = MathUtils.constrainedMap(
- /* rangeMin= */0.0f,
- /* rangeMax= */1.0f,
- /* valueMin= */switchPoint,
- /* valueMax= */1.0f,
- animation.getAnimatedFraction());
-
- opacity = fadeInInterpolator.getInterpolation(fadeInFraction);
- mViewFlipper.setAlpha(opacity);
-
- // Fading back in, animate towards the destination.
- mViewFlipper.setTranslationX(targetTranslation - translationRemaining);
- }
- securityAlphaListener.accept(opacity);
-
- if (animation.getAnimatedFraction() == 1.0f && shouldRestoreLayerType) {
- mViewFlipper.setLayerType(View.LAYER_TYPE_NONE, /* paint= */null);
- }
- });
-
- mRunningSecurityShiftAnimator.start();
- } else {
- mViewFlipper.setTranslationX(targetTranslation);
- }
- }
-
-
boolean isLeftAligned() {
return mGlobalSettings.getInt(Settings.Global.ONE_HANDED_KEYGUARD_SIDE,
mDefaultSideSetting)
@@ -989,11 +821,11 @@ public class KeyguardSecurityContainer extends FrameLayout {
* Default bouncer is centered within the space
*/
static class DefaultViewMode implements ViewMode {
- private ViewGroup mView;
+ private ConstraintLayout mView;
private KeyguardSecurityViewFlipper mViewFlipper;
@Override
- public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+ public void init(@NonNull ConstraintLayout v, @NonNull GlobalSettings globalSettings,
@NonNull KeyguardSecurityViewFlipper viewFlipper,
@NonNull FalsingManager falsingManager,
@NonNull UserSwitcherController userSwitcherController) {
@@ -1005,11 +837,14 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
private void updateSecurityViewGroup() {
- FrameLayout.LayoutParams lp =
- (FrameLayout.LayoutParams) mViewFlipper.getLayoutParams();
- lp.gravity = Gravity.CENTER_HORIZONTAL;
- mViewFlipper.setLayoutParams(lp);
- mViewFlipper.setTranslationX(0);
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.connect(mViewFlipper.getId(), START, PARENT_ID, START);
+ constraintSet.connect(mViewFlipper.getId(), END, PARENT_ID, END);
+ constraintSet.connect(mViewFlipper.getId(), BOTTOM, PARENT_ID, BOTTOM);
+ constraintSet.connect(mViewFlipper.getId(), TOP, PARENT_ID, TOP);
+ constraintSet.constrainHeight(mViewFlipper.getId(), MATCH_CONSTRAINT);
+ constraintSet.constrainWidth(mViewFlipper.getId(), MATCH_CONSTRAINT);
+ constraintSet.applyTo(mView);
}
}
@@ -1018,7 +853,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
* a user switcher, in both portrait and landscape modes.
*/
static class UserSwitcherViewMode extends SidedSecurityMode {
- private ViewGroup mView;
+ private ConstraintLayout mView;
private ViewGroup mUserSwitcherViewGroup;
private KeyguardSecurityViewFlipper mViewFlipper;
private TextView mUserSwitcher;
@@ -1029,11 +864,8 @@ public class KeyguardSecurityContainer extends FrameLayout {
private UserSwitcherController.UserSwitchCallback mUserSwitchCallback =
this::setupUserSwitcher;
- private float mAnimationLastAlpha = 1f;
- private boolean mAnimationWaitsToShift = true;
-
@Override
- public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+ public void init(@NonNull ConstraintLayout v, @NonNull GlobalSettings globalSettings,
@NonNull KeyguardSecurityViewFlipper viewFlipper,
@NonNull FalsingManager falsingManager,
@NonNull UserSwitcherController userSwitcherController) {
@@ -1240,89 +1072,56 @@ public class KeyguardSecurityContainer extends FrameLayout {
});
}
- /**
- * Each view will get half the width. Yes, it would be easier to use something other than
- * FrameLayout but it was too disruptive to downstream projects to change.
- */
- @Override
- public int getChildWidthMeasureSpec(int parentWidthMeasureSpec) {
- return MeasureSpec.makeMeasureSpec(
- MeasureSpec.getSize(parentWidthMeasureSpec) / 2,
- MeasureSpec.EXACTLY);
- }
-
@Override
public void updateSecurityViewLocation() {
updateSecurityViewLocation(isLeftAligned(), /* animate= */false);
}
public void updateSecurityViewLocation(boolean leftAlign, boolean animate) {
- setYTranslation();
- setGravity();
- setXTranslation(leftAlign, animate);
- }
-
- private void setXTranslation(boolean leftAlign, boolean animate) {
- if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- mUserSwitcherViewGroup.setTranslationX(0);
- mViewFlipper.setTranslationX(0);
- } else {
- int switcherTargetTranslation = leftAlign
- ? mView.getMeasuredWidth() - mViewFlipper.getWidth() : 0;
- if (animate) {
- mAnimationWaitsToShift = true;
- mAnimationLastAlpha = 1f;
- translateSecurityViewLocation(leftAlign, animate, securityAlpha -> {
- // During the animation security view fades out - alpha goes from 1 to
- // (almost) 0 - and then fades in - alpha grows back to 1.
- // If new alpha is bigger than previous one it means we're at inflection
- // point and alpha is zero or almost zero. That's when we want to do
- // translation of user switcher, so that it's not visible to the user.
- boolean fullyFadeOut = securityAlpha == 0.0f
- || securityAlpha > mAnimationLastAlpha;
- if (fullyFadeOut && mAnimationWaitsToShift) {
- mUserSwitcherViewGroup.setTranslationX(switcherTargetTranslation);
- mAnimationWaitsToShift = false;
- }
- mUserSwitcherViewGroup.setAlpha(securityAlpha);
- mAnimationLastAlpha = securityAlpha;
- });
- } else {
- translateSecurityViewLocation(leftAlign, animate);
- mUserSwitcherViewGroup.setTranslationX(switcherTargetTranslation);
- }
- }
-
- }
-
- private void setGravity() {
- if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- updateViewGravity(mUserSwitcherViewGroup, Gravity.CENTER_HORIZONTAL);
- updateViewGravity(mViewFlipper, Gravity.CENTER_HORIZONTAL);
- } else {
- // horizontal gravity is the same because we translate these views anyway
- updateViewGravity(mViewFlipper, Gravity.LEFT | Gravity.BOTTOM);
- updateViewGravity(mUserSwitcherViewGroup, Gravity.LEFT | Gravity.CENTER_VERTICAL);
+ if (animate) {
+ TransitionManager.beginDelayedTransition(mView,
+ new KeyguardSecurityViewTransition());
}
- }
-
- private void setYTranslation() {
int yTrans = mResources.getDimensionPixelSize(R.dimen.bouncer_user_switcher_y_trans);
if (mResources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
- mUserSwitcherViewGroup.setTranslationY(yTrans);
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.connect(mUserSwitcherViewGroup.getId(), TOP, PARENT_ID, TOP, yTrans);
+ constraintSet.connect(mViewFlipper.getId(), TOP, PARENT_ID, TOP);
+ constraintSet.connect(mViewFlipper.getId(), BOTTOM, PARENT_ID, BOTTOM);
+ constraintSet.centerHorizontally(mViewFlipper.getId(), PARENT_ID);
+ constraintSet.centerHorizontally(mUserSwitcherViewGroup.getId(), PARENT_ID);
+ constraintSet.setVerticalChainStyle(mViewFlipper.getId(), CHAIN_SPREAD);
+ constraintSet.setVerticalChainStyle(mUserSwitcherViewGroup.getId(), CHAIN_SPREAD);
+ constraintSet.constrainHeight(mUserSwitcherViewGroup.getId(), WRAP_CONTENT);
+ constraintSet.constrainWidth(mUserSwitcherViewGroup.getId(), WRAP_CONTENT);
+ constraintSet.constrainHeight(mViewFlipper.getId(), MATCH_CONSTRAINT);
+ constraintSet.applyTo(mView);
} else {
- // Attempt to reposition a bit higher to make up for this frame being a bit lower
- // on the device
- mUserSwitcherViewGroup.setTranslationY(-yTrans);
- mViewFlipper.setTranslationY(0);
+ int leftElement = leftAlign ? mViewFlipper.getId() : mUserSwitcherViewGroup.getId();
+ int rightElement =
+ leftAlign ? mUserSwitcherViewGroup.getId() : mViewFlipper.getId();
+
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.connect(leftElement, LEFT, PARENT_ID, LEFT);
+ constraintSet.connect(leftElement, RIGHT, rightElement, LEFT);
+ constraintSet.connect(rightElement, LEFT, leftElement, RIGHT);
+ constraintSet.connect(rightElement, RIGHT, PARENT_ID, RIGHT);
+ constraintSet.connect(mUserSwitcherViewGroup.getId(), TOP, PARENT_ID, TOP);
+ constraintSet.connect(mUserSwitcherViewGroup.getId(), BOTTOM, PARENT_ID, BOTTOM,
+ yTrans);
+ constraintSet.connect(mViewFlipper.getId(), TOP, PARENT_ID, TOP);
+ constraintSet.connect(mViewFlipper.getId(), BOTTOM, PARENT_ID, BOTTOM);
+ constraintSet.setHorizontalChainStyle(mUserSwitcherViewGroup.getId(), CHAIN_SPREAD);
+ constraintSet.setHorizontalChainStyle(mViewFlipper.getId(), CHAIN_SPREAD);
+ constraintSet.constrainHeight(mUserSwitcherViewGroup.getId(),
+ MATCH_CONSTRAINT);
+ constraintSet.constrainWidth(mUserSwitcherViewGroup.getId(),
+ MATCH_CONSTRAINT);
+ constraintSet.constrainWidth(mViewFlipper.getId(), MATCH_CONSTRAINT);
+ constraintSet.constrainHeight(mViewFlipper.getId(), MATCH_CONSTRAINT);
+ constraintSet.applyTo(mView);
}
}
-
- private void updateViewGravity(View v, int gravity) {
- FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams();
- lp.gravity = gravity;
- v.setLayoutParams(lp);
- }
}
/**
@@ -1330,11 +1129,11 @@ public class KeyguardSecurityContainer extends FrameLayout {
* between alternate sides of the display.
*/
static class OneHandedViewMode extends SidedSecurityMode {
- private ViewGroup mView;
+ private ConstraintLayout mView;
private KeyguardSecurityViewFlipper mViewFlipper;
@Override
- public void init(@NonNull ViewGroup v, @NonNull GlobalSettings globalSettings,
+ public void init(@NonNull ConstraintLayout v, @NonNull GlobalSettings globalSettings,
@NonNull KeyguardSecurityViewFlipper viewFlipper,
@NonNull FalsingManager falsingManager,
@NonNull UserSwitcherController userSwitcherController) {
@@ -1342,28 +1141,10 @@ public class KeyguardSecurityContainer extends FrameLayout {
mView = v;
mViewFlipper = viewFlipper;
- updateSecurityViewGravity();
updateSecurityViewLocation(isLeftAligned(), /* animate= */false);
}
/**
- * One-handed mode contains the child to half of the available space.
- */
- @Override
- public int getChildWidthMeasureSpec(int parentWidthMeasureSpec) {
- return MeasureSpec.makeMeasureSpec(
- MeasureSpec.getSize(parentWidthMeasureSpec) / 2,
- MeasureSpec.EXACTLY);
- }
-
- private void updateSecurityViewGravity() {
- FrameLayout.LayoutParams lp =
- (FrameLayout.LayoutParams) mViewFlipper.getLayoutParams();
- lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
- mViewFlipper.setLayoutParams(lp);
- }
-
- /**
* Moves the bouncer to align with a tap (most likely in the shade), so the bouncer
* appears on the same side as a touch.
*/
@@ -1380,7 +1161,20 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
protected void updateSecurityViewLocation(boolean leftAlign, boolean animate) {
- translateSecurityViewLocation(leftAlign, animate);
+ if (animate) {
+ TransitionManager.beginDelayedTransition(mView,
+ new KeyguardSecurityViewTransition());
+ }
+ ConstraintSet constraintSet = new ConstraintSet();
+ if (leftAlign) {
+ constraintSet.connect(mViewFlipper.getId(), LEFT, PARENT_ID, LEFT);
+ } else {
+ constraintSet.connect(mViewFlipper.getId(), RIGHT, PARENT_ID, RIGHT);
+ }
+ constraintSet.connect(mViewFlipper.getId(), TOP, PARENT_ID, TOP);
+ constraintSet.connect(mViewFlipper.getId(), BOTTOM, PARENT_ID, BOTTOM);
+ constraintSet.constrainPercentWidth(mViewFlipper.getId(), 0.5f);
+ constraintSet.applyTo(mView);
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt
new file mode 100644
index 000000000000..9eb2c118abac
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewTransition.kt
@@ -0,0 +1,210 @@
+/*
+ * 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.keyguard
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.graphics.Rect
+import android.transition.Transition
+import android.transition.TransitionValues
+import android.util.MathUtils
+import android.view.View
+import android.view.ViewGroup
+import android.view.animation.AnimationUtils
+import com.android.internal.R.interpolator.fast_out_extra_slow_in
+import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
+
+/** Animates constraint layout changes for the security view. */
+class KeyguardSecurityViewTransition : Transition() {
+
+ companion object {
+ const val PROP_BOUNDS = "securityViewLocation:bounds"
+
+ // The duration of the animation to switch security sides.
+ const val SECURITY_SHIFT_ANIMATION_DURATION_MS = 500L
+
+ // How much of the switch sides animation should be dedicated to fading the security out.
+ // The remainder will fade it back in again.
+ const val SECURITY_SHIFT_ANIMATION_FADE_OUT_PROPORTION = 0.2f
+ }
+
+ private fun captureValues(values: TransitionValues) {
+ val boundsRect = Rect()
+ boundsRect.left = values.view.left
+ boundsRect.top = values.view.top
+ boundsRect.right = values.view.right
+ boundsRect.bottom = values.view.bottom
+ values.values[PROP_BOUNDS] = boundsRect
+ }
+
+ override fun getTransitionProperties(): Array<String>? {
+ return arrayOf(PROP_BOUNDS)
+ }
+
+ override fun captureEndValues(transitionValues: TransitionValues?) {
+ transitionValues?.let { captureValues(it) }
+ }
+
+ override fun captureStartValues(transitionValues: TransitionValues?) {
+ transitionValues?.let { captureValues(it) }
+ }
+
+ override fun createAnimator(
+ sceneRoot: ViewGroup?,
+ startValues: TransitionValues?,
+ endValues: TransitionValues?
+ ): Animator? {
+ if (sceneRoot == null || startValues == null || endValues == null) {
+ return null
+ }
+
+ // This animation is a bit fun to implement. The bouncer needs to move, and fade
+ // in/out at the same time. The issue is, the bouncer should only move a short
+ // amount (120dp or so), but obviously needs to go from one side of the screen to
+ // the other. This needs a pretty custom animation.
+ //
+ // This works as follows. It uses a ValueAnimation to simply drive the animation
+ // progress. This animator is responsible for both the translation of the bouncer,
+ // and the current fade. It will fade the bouncer out while also moving it along the
+ // 120dp path. Once the bouncer is fully faded out though, it will "snap" the
+ // bouncer closer to its destination, then fade it back in again. The effect is that
+ // the bouncer will move from 0 -> X while fading out, then
+ // (destination - X) -> destination while fading back in again.
+ // TODO(b/208250221): Make this animation properly abortable.
+ val positionInterpolator =
+ AnimationUtils.loadInterpolator(sceneRoot.context, fast_out_extra_slow_in)
+ val fadeOutInterpolator = Interpolators.FAST_OUT_LINEAR_IN
+ val fadeInInterpolator = Interpolators.LINEAR_OUT_SLOW_IN
+ var runningSecurityShiftAnimator = ValueAnimator.ofFloat(0.0f, 1.0f)
+ runningSecurityShiftAnimator.duration = SECURITY_SHIFT_ANIMATION_DURATION_MS
+ runningSecurityShiftAnimator.interpolator = Interpolators.LINEAR
+ val startRect = startValues.values[PROP_BOUNDS] as Rect
+ val endRect = endValues.values[PROP_BOUNDS] as Rect
+ val v = startValues.view
+ val totalTranslation: Int =
+ sceneRoot.resources.getDimension(R.dimen.security_shift_animation_translation).toInt()
+ val shouldRestoreLayerType =
+ (v.hasOverlappingRendering() && v.layerType != View.LAYER_TYPE_HARDWARE)
+ if (shouldRestoreLayerType) {
+ v.setLayerType(View.LAYER_TYPE_HARDWARE, /* paint= */ null)
+ }
+ val initialAlpha: Float = v.alpha
+ runningSecurityShiftAnimator.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ runningSecurityShiftAnimator = null
+ }
+ }
+ )
+
+ var finishedFadingOutNonSecurityView = false
+
+ runningSecurityShiftAnimator.addUpdateListener { animation: ValueAnimator ->
+ val switchPoint = SECURITY_SHIFT_ANIMATION_FADE_OUT_PROPORTION
+ val isFadingOut = animation.animatedFraction < switchPoint
+ val opacity: Float
+ var currentTranslation =
+ (positionInterpolator.getInterpolation(animation.animatedFraction) *
+ totalTranslation)
+ .toInt()
+ var translationRemaining = totalTranslation - currentTranslation
+ val leftAlign = endRect.left < startRect.left
+ if (leftAlign) {
+ currentTranslation = -currentTranslation
+ translationRemaining = -translationRemaining
+ }
+
+ if (isFadingOut) {
+ // The bouncer fades out over the first X%.
+ val fadeOutFraction =
+ MathUtils.constrainedMap(
+ /* rangeMin= */ 1.0f,
+ /* rangeMax= */ 0.0f,
+ /* valueMin= */ 0.0f,
+ /* valueMax= */ switchPoint,
+ animation.animatedFraction
+ )
+ opacity = fadeOutInterpolator.getInterpolation(fadeOutFraction)
+
+ // When fading out, the alpha needs to start from the initial opacity of the
+ // view flipper, otherwise we get a weird bit of jank as it ramps back to
+ // 100%.
+ v.alpha = opacity * initialAlpha
+ if (v is KeyguardSecurityViewFlipper) {
+ v.setLeftTopRightBottom(
+ startRect.left + currentTranslation,
+ startRect.top,
+ startRect.right + currentTranslation,
+ startRect.bottom
+ )
+ }
+ } else {
+ // And in again over the remaining (100-X)%.
+ val fadeInFraction =
+ MathUtils.constrainedMap(
+ /* rangeMin= */ 0.0f,
+ /* rangeMax= */ 1.0f,
+ /* valueMin= */ switchPoint,
+ /* valueMax= */ 1.0f,
+ animation.animatedFraction
+ )
+ opacity = fadeInInterpolator.getInterpolation(fadeInFraction)
+ v.alpha = opacity
+
+ // Fading back in, animate towards the destination.
+ if (v is KeyguardSecurityViewFlipper) {
+ v.setLeftTopRightBottom(
+ endRect.left - translationRemaining,
+ endRect.top,
+ endRect.right - translationRemaining,
+ endRect.bottom
+ )
+ }
+ }
+ if (animation.animatedFraction == 1.0f && shouldRestoreLayerType) {
+ v.setLayerType(View.LAYER_TYPE_NONE, /* paint= */ null)
+ }
+
+ // For views that are not the security view flipper, we do not want to apply
+ // an x translation animation. Instead, we want to fade out, move to final position and
+ // then fade in.
+ if (v !is KeyguardSecurityViewFlipper) {
+ // Opacity goes close to 0 but does not fully get to 0.
+ if (opacity - 0.001f < 0f) {
+ v.setLeftTopRightBottom(
+ endRect.left,
+ endRect.top,
+ endRect.right,
+ endRect.bottom
+ )
+ finishedFadingOutNonSecurityView = true
+ } else if (!finishedFadingOutNonSecurityView) {
+ v.setLeftTopRightBottom(
+ startRect.left,
+ startRect.top,
+ startRect.right,
+ startRect.bottom
+ )
+ }
+ }
+ }
+ runningSecurityShiftAnimator.start()
+ return runningSecurityShiftAnimator
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt b/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
index 109be40ce10f..37829f25d179 100644
--- a/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
+++ b/packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
@@ -3,6 +3,7 @@ package com.android.systemui
import android.content.ComponentName
import android.content.Context
import android.content.pm.PackageManager
+import android.util.Log
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -55,7 +56,11 @@ class ChooserSelector @Inject constructor(
} else {
PackageManager.COMPONENT_ENABLED_STATE_DISABLED
}
- packageManager.setComponentEnabledSetting(chooserComponent, newState, /* flags = */ 0)
+ try {
+ packageManager.setComponentEnabledSetting(chooserComponent, newState, /* flags = */ 0)
+ } catch (e: IllegalArgumentException) {
+ Log.w("ChooserSelector", "Unable to set IntentResolver enabled=" + enabled, e)
+ }
}
suspend inline fun awaitCancellation(): Nothing = suspendCancellableCoroutine { }
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 8aaadeffae12..8e2ac064b4f1 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -116,7 +116,7 @@ public class Flags {
* the framework APIs.
*/
public static final UnreleasedFlag USER_INTERACTOR_AND_REPO_USE_CONTROLLER =
- new UnreleasedFlag(210, true);
+ new UnreleasedFlag(210);
/**
* Whether `UserSwitcherController` should use the user interactor.
@@ -127,8 +127,7 @@ public class Flags {
* <p>Note: do not set this to true if {@link #USER_INTERACTOR_AND_REPO_USE_CONTROLLER} is
* {@code true} as it would created a cycle between controller -> interactor -> controller.
*/
- public static final UnreleasedFlag USER_CONTROLLER_USES_INTERACTOR =
- new UnreleasedFlag(211, false);
+ public static final ReleasedFlag USER_CONTROLLER_USES_INTERACTOR = new ReleasedFlag(211);
/***************************************/
// 300 - power menu
@@ -236,7 +235,7 @@ public class Flags {
// 1100 - windowing
@Keep
public static final SysPropBooleanFlag WM_ENABLE_SHELL_TRANSITIONS =
- new SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", false);
+ new SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", true);
/**
* b/170163464: animate bubbles expanded view collapse with home gesture
diff --git a/packages/SystemUI/src/com/android/systemui/shade/OWNERS b/packages/SystemUI/src/com/android/systemui/shade/OWNERS
index 7dc9dc7efeb7..d71fbf656e5f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/shade/OWNERS
@@ -1,3 +1,6 @@
+justinweir@google.com
+syeonlee@google.com
+
per-file *Notification* = set noparent
per-file *Notification* = file:../statusbar/notification/OWNERS
@@ -11,4 +14,4 @@ per-file NotificationShadeWindowView.java = pixel@google.com, cinek@google.com,
per-file NotificationPanelUnfoldAnimationController.kt = alexflo@google.com, jeffdq@google.com, juliacr@google.com
per-file NotificationPanelView.java = pixel@google.com, cinek@google.com, juliacr@google.com, justinweir@google.com
-per-file NotificationPanelViewController.java = pixel@google.com, cinek@google.com, juliacr@google.com, justinweir@google.com \ No newline at end of file
+per-file NotificationPanelViewController.java = pixel@google.com, cinek@google.com, juliacr@google.com, justinweir@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
index a7719d3d82a4..e71d80c130da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.notification.collection.inflation;
import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+
import android.os.SystemClock;
import android.service.notification.NotificationStats;
@@ -70,6 +72,8 @@ public class OnUserInteractionCallbackImpl implements OnUserInteractionCallback
dismissalSurface = NotificationStats.DISMISSAL_PEEK;
} else if (mStatusBarStateController.isDozing()) {
dismissalSurface = NotificationStats.DISMISSAL_AOD;
+ } else if (mStatusBarStateController.getState() == KEYGUARD) {
+ dismissalSurface = NotificationStats.DISMISSAL_LOCKSCREEN;
}
return new DismissedByUserStats(
dismissalSurface,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
index 3292a8fcdb50..6cf4bf318c99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java
@@ -37,6 +37,19 @@ public interface NotificationInterruptStateProvider {
boolean shouldHeadsUp(NotificationEntry entry);
/**
+ * Returns the value of whether this entry should peek (from shouldHeadsUp(entry)), but only
+ * optionally logs the status.
+ *
+ * This method should be used in cases where the caller needs to check whether a notification
+ * qualifies for a heads up, but is not necessarily guaranteed to make the heads-up happen.
+ *
+ * @param entry the entry to check
+ * @param log whether or not to log the results of this check
+ * @return true if the entry should heads up, false otherwise
+ */
+ boolean checkHeadsUp(NotificationEntry entry, boolean log);
+
+ /**
* Whether the notification should appear as a bubble with a fly-out on top of the screen.
*
* @param entry the entry to check
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 558fd62c78bf..c5a69217a1ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -137,11 +137,11 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
public boolean shouldBubbleUp(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
- if (!canAlertCommon(entry)) {
+ if (!canAlertCommon(entry, true)) {
return false;
}
- if (!canAlertAwakeCommon(entry)) {
+ if (!canAlertAwakeCommon(entry, true)) {
return false;
}
@@ -163,10 +163,15 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
@Override
public boolean shouldHeadsUp(NotificationEntry entry) {
+ return checkHeadsUp(entry, true);
+ }
+
+ @Override
+ public boolean checkHeadsUp(NotificationEntry entry, boolean log) {
if (mStatusBarStateController.isDozing()) {
- return shouldHeadsUpWhenDozing(entry);
+ return shouldHeadsUpWhenDozing(entry, log);
} else {
- return shouldHeadsUpWhenAwake(entry);
+ return shouldHeadsUpWhenAwake(entry, log);
}
}
@@ -263,61 +268,61 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
}
}
- private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) {
+ private boolean shouldHeadsUpWhenAwake(NotificationEntry entry, boolean log) {
StatusBarNotification sbn = entry.getSbn();
if (!mUseHeadsUp) {
- mLogger.logNoHeadsUpFeatureDisabled();
+ if (log) mLogger.logNoHeadsUpFeatureDisabled();
return false;
}
- if (!canAlertCommon(entry)) {
+ if (!canAlertCommon(entry, log)) {
return false;
}
- if (!canAlertHeadsUpCommon(entry)) {
+ if (!canAlertHeadsUpCommon(entry, log)) {
return false;
}
- if (!canAlertAwakeCommon(entry)) {
+ if (!canAlertAwakeCommon(entry, log)) {
return false;
}
if (isSnoozedPackage(sbn)) {
- mLogger.logNoHeadsUpPackageSnoozed(entry);
+ if (log) mLogger.logNoHeadsUpPackageSnoozed(entry);
return false;
}
boolean inShade = mStatusBarStateController.getState() == SHADE;
if (entry.isBubble() && inShade) {
- mLogger.logNoHeadsUpAlreadyBubbled(entry);
+ if (log) mLogger.logNoHeadsUpAlreadyBubbled(entry);
return false;
}
if (entry.shouldSuppressPeek()) {
- mLogger.logNoHeadsUpSuppressedByDnd(entry);
+ if (log) mLogger.logNoHeadsUpSuppressedByDnd(entry);
return false;
}
if (entry.getImportance() < NotificationManager.IMPORTANCE_HIGH) {
- mLogger.logNoHeadsUpNotImportant(entry);
+ if (log) mLogger.logNoHeadsUpNotImportant(entry);
return false;
}
boolean inUse = mPowerManager.isScreenOn() && !isDreaming();
if (!inUse) {
- mLogger.logNoHeadsUpNotInUse(entry);
+ if (log) mLogger.logNoHeadsUpNotInUse(entry);
return false;
}
for (int i = 0; i < mSuppressors.size(); i++) {
if (mSuppressors.get(i).suppressAwakeHeadsUp(entry)) {
- mLogger.logNoHeadsUpSuppressedBy(entry, mSuppressors.get(i));
+ if (log) mLogger.logNoHeadsUpSuppressedBy(entry, mSuppressors.get(i));
return false;
}
}
- mLogger.logHeadsUp(entry);
+ if (log) mLogger.logHeadsUp(entry);
return true;
}
@@ -328,37 +333,37 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
* @param entry the entry to check
* @return true if the entry should ambient pulse, false otherwise
*/
- private boolean shouldHeadsUpWhenDozing(NotificationEntry entry) {
+ private boolean shouldHeadsUpWhenDozing(NotificationEntry entry, boolean log) {
if (!mAmbientDisplayConfiguration.pulseOnNotificationEnabled(UserHandle.USER_CURRENT)) {
- mLogger.logNoPulsingSettingDisabled(entry);
+ if (log) mLogger.logNoPulsingSettingDisabled(entry);
return false;
}
if (mBatteryController.isAodPowerSave()) {
- mLogger.logNoPulsingBatteryDisabled(entry);
+ if (log) mLogger.logNoPulsingBatteryDisabled(entry);
return false;
}
- if (!canAlertCommon(entry)) {
- mLogger.logNoPulsingNoAlert(entry);
+ if (!canAlertCommon(entry, log)) {
+ if (log) mLogger.logNoPulsingNoAlert(entry);
return false;
}
- if (!canAlertHeadsUpCommon(entry)) {
- mLogger.logNoPulsingNoAlert(entry);
+ if (!canAlertHeadsUpCommon(entry, log)) {
+ if (log) mLogger.logNoPulsingNoAlert(entry);
return false;
}
if (entry.shouldSuppressAmbient()) {
- mLogger.logNoPulsingNoAmbientEffect(entry);
+ if (log) mLogger.logNoPulsingNoAmbientEffect(entry);
return false;
}
if (entry.getImportance() < NotificationManager.IMPORTANCE_DEFAULT) {
- mLogger.logNoPulsingNotImportant(entry);
+ if (log) mLogger.logNoPulsingNotImportant(entry);
return false;
}
- mLogger.logPulsing(entry);
+ if (log) mLogger.logPulsing(entry);
return true;
}
@@ -366,18 +371,22 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
* Common checks between regular & AOD heads up and bubbles.
*
* @param entry the entry to check
+ * @param log whether or not to log the results of these checks
* @return true if these checks pass, false if the notification should not alert
*/
- private boolean canAlertCommon(NotificationEntry entry) {
+ private boolean canAlertCommon(NotificationEntry entry, boolean log) {
for (int i = 0; i < mSuppressors.size(); i++) {
if (mSuppressors.get(i).suppressInterruptions(entry)) {
- mLogger.logNoAlertingSuppressedBy(entry, mSuppressors.get(i), /* awake */ false);
+ if (log) {
+ mLogger.logNoAlertingSuppressedBy(entry, mSuppressors.get(i),
+ /* awake */ false);
+ }
return false;
}
}
if (mKeyguardNotificationVisibilityProvider.shouldHideNotification(entry)) {
- mLogger.keyguardHideNotification(entry);
+ if (log) mLogger.keyguardHideNotification(entry);
return false;
}
@@ -388,19 +397,20 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
* Common checks for heads up notifications on regular and AOD displays.
*
* @param entry the entry to check
+ * @param log whether or not to log the results of these checks
* @return true if these checks pass, false if the notification should not alert
*/
- private boolean canAlertHeadsUpCommon(NotificationEntry entry) {
+ private boolean canAlertHeadsUpCommon(NotificationEntry entry, boolean log) {
StatusBarNotification sbn = entry.getSbn();
// Don't alert notifications that are suppressed due to group alert behavior
if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) {
- mLogger.logNoAlertingGroupAlertBehavior(entry);
+ if (log) mLogger.logNoAlertingGroupAlertBehavior(entry);
return false;
}
if (entry.hasJustLaunchedFullScreenIntent()) {
- mLogger.logNoAlertingRecentFullscreen(entry);
+ if (log) mLogger.logNoAlertingRecentFullscreen(entry);
return false;
}
@@ -413,12 +423,14 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter
* @param entry the entry to check
* @return true if these checks pass, false if the notification should not alert
*/
- private boolean canAlertAwakeCommon(NotificationEntry entry) {
+ private boolean canAlertAwakeCommon(NotificationEntry entry, boolean log) {
StatusBarNotification sbn = entry.getSbn();
for (int i = 0; i < mSuppressors.size(); i++) {
if (mSuppressors.get(i).suppressAwakeInterruptions(entry)) {
- mLogger.logNoAlertingSuppressedBy(entry, mSuppressors.get(i), /* awake */ true);
+ if (log) {
+ mLogger.logNoAlertingSuppressedBy(entry, mSuppressors.get(i), /* awake */ true);
+ }
return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index ce465bcfb42d..2719dd88b7be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -44,7 +44,8 @@ import java.io.PrintWriter;
import javax.inject.Inject;
/**
- * A global state to track all input states for the algorithm.
+ * Global state to track all input states for
+ * {@link com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm}.
*/
@SysUISingleton
public class AmbientState implements Dumpable {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
index 80385e69cfa4..f5cd0ca7ab3b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java
@@ -41,6 +41,7 @@ import android.testing.TestableLooper.RunWithLooper;
import android.testing.ViewUtils;
import android.view.SurfaceControlViewHost;
import android.view.SurfaceView;
+import android.view.View;
import androidx.test.filters.SmallTest;
@@ -84,6 +85,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mKeyguardSecurityContainer = spy(new KeyguardSecurityContainer(mContext));
+ mKeyguardSecurityContainer.setId(View.generateViewId());
ViewUtils.attachView(mKeyguardSecurityContainer);
mTestableLooper = TestableLooper.get(this);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index c1036e356cfa..52f8825c724b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -21,10 +21,14 @@ import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE;
import static android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE_LEFT;
import static android.provider.Settings.Global.ONE_HANDED_KEYGUARD_SIDE_RIGHT;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.systemBars;
+import static androidx.constraintlayout.widget.ConstraintSet.CHAIN_SPREAD;
+import static androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT;
+import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
+import static androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT;
+
import static com.android.keyguard.KeyguardSecurityContainer.MODE_DEFAULT;
import static com.android.keyguard.KeyguardSecurityContainer.MODE_ONE_HANDED;
@@ -32,9 +36,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,19 +44,17 @@ import android.content.res.Configuration;
import android.graphics.Insets;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
-import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowInsets;
-import android.widget.FrameLayout;
+import androidx.constraintlayout.widget.ConstraintSet;
import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.user.data.source.UserRecord;
import com.android.systemui.util.settings.GlobalSettings;
@@ -64,8 +63,6 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@@ -78,14 +75,11 @@ import java.util.ArrayList;
public class KeyguardSecurityContainerTest extends SysuiTestCase {
private static final int VIEW_WIDTH = 1600;
-
- private int mScreenWidth;
- private int mFakeMeasureSpec;
+ private static final int VIEW_HEIGHT = 900;
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
- @Mock
private KeyguardSecurityViewFlipper mSecurityViewFlipper;
@Mock
private GlobalSettings mGlobalSettings;
@@ -93,59 +87,32 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
private FalsingManager mFalsingManager;
@Mock
private UserSwitcherController mUserSwitcherController;
- @Mock
- private KeyguardStateController mKeyguardStateController;
- @Captor
- private ArgumentCaptor<FrameLayout.LayoutParams> mLayoutCaptor;
private KeyguardSecurityContainer mKeyguardSecurityContainer;
- private FrameLayout.LayoutParams mSecurityViewFlipperLayoutParams;
@Before
public void setup() {
// Needed here, otherwise when mKeyguardSecurityContainer is created below, it'll cache
// the real references (rather than the TestableResources that this call creates).
mContext.ensureTestableResources();
- mSecurityViewFlipperLayoutParams = new FrameLayout.LayoutParams(
- MATCH_PARENT, MATCH_PARENT);
- when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
+ mSecurityViewFlipper = new KeyguardSecurityViewFlipper(getContext());
+ mSecurityViewFlipper.setId(View.generateViewId());
mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext());
+ mKeyguardSecurityContainer.setRight(VIEW_WIDTH);
+ mKeyguardSecurityContainer.setLeft(0);
+ mKeyguardSecurityContainer.setTop(0);
+ mKeyguardSecurityContainer.setBottom(VIEW_HEIGHT);
+ mKeyguardSecurityContainer.setId(View.generateViewId());
mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper;
mKeyguardSecurityContainer.addView(mSecurityViewFlipper, new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
when(mUserSwitcherController.getCurrentUserName()).thenReturn("Test User");
when(mUserSwitcherController.isKeyguardShowing()).thenReturn(true);
-
- mScreenWidth = getUiDevice().getDisplayWidth();
- mFakeMeasureSpec = View
- .MeasureSpec.makeMeasureSpec(mScreenWidth, View.MeasureSpec.EXACTLY);
- }
-
- @Test
- public void onMeasure_usesHalfWidthWithOneHandedModeEnabled() {
- mKeyguardSecurityContainer.initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingManager,
- mUserSwitcherController);
-
- int halfWidthMeasureSpec =
- View.MeasureSpec.makeMeasureSpec(mScreenWidth / 2, View.MeasureSpec.EXACTLY);
- mKeyguardSecurityContainer.onMeasure(mFakeMeasureSpec, mFakeMeasureSpec);
-
- verify(mSecurityViewFlipper).measure(halfWidthMeasureSpec, mFakeMeasureSpec);
}
-
- @Test
- public void onMeasure_usesFullWidthWithOneHandedModeDisabled() {
- mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
- mUserSwitcherController);
-
- mKeyguardSecurityContainer.measure(mFakeMeasureSpec, mFakeMeasureSpec);
- verify(mSecurityViewFlipper).measure(mFakeMeasureSpec, mFakeMeasureSpec);
- }
-
@Test
- public void onMeasure_respectsViewInsets() {
+ public void testOnApplyWindowInsets() {
int paddingBottom = getContext().getResources()
.getDimensionPixelSize(R.dimen.keyguard_security_view_bottom_margin);
int imeInsetAmount = paddingBottom + 1;
@@ -162,17 +129,12 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
.setInsetsIgnoringVisibility(systemBars(), systemBarInset)
.build();
- // It's reduced by the max of the systembar and IME, so just subtract IME inset.
- int expectedHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
- mScreenWidth - imeInsetAmount, View.MeasureSpec.EXACTLY);
-
mKeyguardSecurityContainer.onApplyWindowInsets(insets);
- mKeyguardSecurityContainer.measure(mFakeMeasureSpec, mFakeMeasureSpec);
- verify(mSecurityViewFlipper).measure(mFakeMeasureSpec, expectedHeightMeasureSpec);
+ assertThat(mKeyguardSecurityContainer.getPaddingBottom()).isEqualTo(imeInsetAmount);
}
@Test
- public void onMeasure_respectsViewInsets_largerSystembar() {
+ public void testOnApplyWindowInsets_largerSystembar() {
int imeInsetAmount = 0;
int paddingBottom = getContext().getResources()
.getDimensionPixelSize(R.dimen.keyguard_security_view_bottom_margin);
@@ -189,25 +151,22 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
.setInsetsIgnoringVisibility(systemBars(), systemBarInset)
.build();
- int expectedHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
- mScreenWidth - systemBarInsetAmount, View.MeasureSpec.EXACTLY);
-
mKeyguardSecurityContainer.onApplyWindowInsets(insets);
- mKeyguardSecurityContainer.measure(mFakeMeasureSpec, mFakeMeasureSpec);
- verify(mSecurityViewFlipper).measure(mFakeMeasureSpec, expectedHeightMeasureSpec);
+ assertThat(mKeyguardSecurityContainer.getPaddingBottom()).isEqualTo(systemBarInsetAmount);
}
- private void setupForUpdateKeyguardPosition(boolean oneHandedMode) {
- int mode = oneHandedMode ? MODE_ONE_HANDED : MODE_DEFAULT;
- mKeyguardSecurityContainer.initMode(mode, mGlobalSettings, mFalsingManager,
+ @Test
+ public void testDefaultViewMode() {
+ mKeyguardSecurityContainer.initMode(MODE_ONE_HANDED, mGlobalSettings, mFalsingManager,
mUserSwitcherController);
-
- mKeyguardSecurityContainer.measure(mFakeMeasureSpec, mFakeMeasureSpec);
- mKeyguardSecurityContainer.layout(0, 0, mScreenWidth, mScreenWidth);
-
- // Clear any interactions with the mock so we know the interactions definitely come from the
- // below testing.
- reset(mSecurityViewFlipper);
+ mKeyguardSecurityContainer.initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager,
+ mUserSwitcherController);
+ ConstraintSet.Constraint viewFlipperConstraint =
+ getViewConstraint(mSecurityViewFlipper.getId());
+ assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.startToStart).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.endToEnd).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.bottomToBottom).isEqualTo(PARENT_ID);
}
@Test
@@ -217,13 +176,22 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
mKeyguardSecurityContainer.getWidth() - 1f);
verify(mGlobalSettings).putInt(ONE_HANDED_KEYGUARD_SIDE, ONE_HANDED_KEYGUARD_SIDE_RIGHT);
- assertSecurityTranslationX(
- mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
+ ConstraintSet.Constraint viewFlipperConstraint =
+ getViewConstraint(mSecurityViewFlipper.getId());
+ assertThat(viewFlipperConstraint.layout.widthPercent).isEqualTo(0.5f);
+ assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(-1);
mKeyguardSecurityContainer.updatePositionByTouchX(1f);
verify(mGlobalSettings).putInt(ONE_HANDED_KEYGUARD_SIDE, ONE_HANDED_KEYGUARD_SIDE_LEFT);
- verify(mSecurityViewFlipper).setTranslationX(0.0f);
+ viewFlipperConstraint =
+ getViewConstraint(mSecurityViewFlipper.getId());
+ assertThat(viewFlipperConstraint.layout.widthPercent).isEqualTo(0.5f);
+ assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(-1);
}
@Test
@@ -232,10 +200,16 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
mKeyguardSecurityContainer.updatePositionByTouchX(
mKeyguardSecurityContainer.getWidth() - 1f);
- verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
+ ConstraintSet.Constraint viewFlipperConstraint =
+ getViewConstraint(mSecurityViewFlipper.getId());
+ assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(-1);
+ assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(-1);
mKeyguardSecurityContainer.updatePositionByTouchX(1f);
- verify(mSecurityViewFlipper, never()).setTranslationX(anyInt());
+ viewFlipperConstraint =
+ getViewConstraint(mSecurityViewFlipper.getId());
+ assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(-1);
+ assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(-1);
}
@Test
@@ -249,17 +223,31 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
setupUserSwitcher();
mKeyguardSecurityContainer.onConfigurationChanged(landscapeConfig);
- // THEN views are oriented side by side
- assertSecurityGravity(Gravity.LEFT | Gravity.BOTTOM);
- assertUserSwitcherGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
- assertSecurityTranslationX(
- mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
- assertUserSwitcherTranslationX(0f);
-
+ ConstraintSet.Constraint viewFlipperConstraint =
+ getViewConstraint(mSecurityViewFlipper.getId());
+ ConstraintSet.Constraint userSwitcherConstraint =
+ getViewConstraint(R.id.keyguard_bouncer_user_switcher);
+ assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.leftToRight).isEqualTo(
+ R.id.keyguard_bouncer_user_switcher);
+ assertThat(userSwitcherConstraint.layout.leftToLeft).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.rightToLeft).isEqualTo(
+ mSecurityViewFlipper.getId());
+ assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.bottomToBottom).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.bottomToBottom).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.bottomMargin).isEqualTo(
+ getContext().getResources().getDimensionPixelSize(
+ R.dimen.bouncer_user_switcher_y_trans));
+ assertThat(viewFlipperConstraint.layout.horizontalChainStyle).isEqualTo(CHAIN_SPREAD);
+ assertThat(userSwitcherConstraint.layout.horizontalChainStyle).isEqualTo(CHAIN_SPREAD);
+ assertThat(viewFlipperConstraint.layout.mHeight).isEqualTo(MATCH_CONSTRAINT);
+ assertThat(userSwitcherConstraint.layout.mHeight).isEqualTo(MATCH_CONSTRAINT);
}
@Test
- public void testUserSwitcherModeViewGravityPortrait() {
+ public void testUserSwitcherModeViewPositionPortrait() {
// GIVEN one user has been setup and in landscape
when(mUserSwitcherController.getUsers()).thenReturn(buildUserRecords(1));
Configuration portraitConfig = configuration(ORIENTATION_PORTRAIT);
@@ -267,15 +255,28 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
// WHEN UserSwitcherViewMode is initialized and config has changed
setupUserSwitcher();
- reset(mSecurityViewFlipper);
- when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
mKeyguardSecurityContainer.onConfigurationChanged(portraitConfig);
- // THEN views are both centered horizontally
- assertSecurityGravity(Gravity.CENTER_HORIZONTAL);
- assertUserSwitcherGravity(Gravity.CENTER_HORIZONTAL);
- assertSecurityTranslationX(0);
- assertUserSwitcherTranslationX(0);
+ ConstraintSet.Constraint viewFlipperConstraint =
+ getViewConstraint(mSecurityViewFlipper.getId());
+ ConstraintSet.Constraint userSwitcherConstraint =
+ getViewConstraint(R.id.keyguard_bouncer_user_switcher);
+
+ assertThat(viewFlipperConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.bottomToBottom).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.topToTop).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.topMargin).isEqualTo(
+ getContext().getResources().getDimensionPixelSize(
+ R.dimen.bouncer_user_switcher_y_trans));
+ assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.rightToRight).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.leftToLeft).isEqualTo(PARENT_ID);
+ assertThat(userSwitcherConstraint.layout.rightToRight).isEqualTo(PARENT_ID);
+ assertThat(viewFlipperConstraint.layout.verticalChainStyle).isEqualTo(CHAIN_SPREAD);
+ assertThat(userSwitcherConstraint.layout.verticalChainStyle).isEqualTo(CHAIN_SPREAD);
+ assertThat(viewFlipperConstraint.layout.mHeight).isEqualTo(MATCH_CONSTRAINT);
+ assertThat(userSwitcherConstraint.layout.mHeight).isEqualTo(WRAP_CONTENT);
+ assertThat(userSwitcherConstraint.layout.mWidth).isEqualTo(WRAP_CONTENT);
}
@Test
@@ -337,9 +338,9 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
when(mGlobalSettings.getInt(any(), anyInt())).thenReturn(ONE_HANDED_KEYGUARD_SIDE_LEFT);
mKeyguardSecurityContainer.onConfigurationChanged(new Configuration());
- assertSecurityTranslationX(0);
- assertUserSwitcherTranslationX(
- mKeyguardSecurityContainer.getWidth() - mSecurityViewFlipper.getWidth());
+ ConstraintSet.Constraint viewFlipperConstraint = getViewConstraint(
+ mSecurityViewFlipper.getId());
+ assertThat(viewFlipperConstraint.layout.leftToLeft).isEqualTo(PARENT_ID);
}
private Configuration configuration(@Configuration.Orientation int orientation) {
@@ -348,28 +349,6 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
return config;
}
- private void assertSecurityTranslationX(float translation) {
- verify(mSecurityViewFlipper).setTranslationX(translation);
- }
-
- private void assertUserSwitcherTranslationX(float translation) {
- ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
- R.id.keyguard_bouncer_user_switcher);
- assertThat(userSwitcher.getTranslationX()).isEqualTo(translation);
- }
-
- private void assertUserSwitcherGravity(@Gravity.GravityFlags int gravity) {
- ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
- R.id.keyguard_bouncer_user_switcher);
- assertThat(((FrameLayout.LayoutParams) userSwitcher.getLayoutParams()).gravity)
- .isEqualTo(gravity);
- }
-
- private void assertSecurityGravity(@Gravity.GravityFlags int gravity) {
- verify(mSecurityViewFlipper, atLeastOnce()).setLayoutParams(mLayoutCaptor.capture());
- assertThat(mLayoutCaptor.getValue().gravity).isEqualTo(gravity);
- }
-
private void setViewWidth(int width) {
mKeyguardSecurityContainer.setRight(width);
mKeyguardSecurityContainer.setLeft(0);
@@ -399,9 +378,6 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
when(mGlobalSettings.getInt(any(), anyInt())).thenReturn(ONE_HANDED_KEYGUARD_SIDE_RIGHT);
mKeyguardSecurityContainer.initMode(KeyguardSecurityContainer.MODE_USER_SWITCHER,
mGlobalSettings, mFalsingManager, mUserSwitcherController);
- // reset mSecurityViewFlipper so setup doesn't influence test verifications
- reset(mSecurityViewFlipper);
- when(mSecurityViewFlipper.getLayoutParams()).thenReturn(mSecurityViewFlipperLayoutParams);
}
private ArrayList<UserRecord> buildUserRecords(int count) {
@@ -415,4 +391,17 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
}
return users;
}
+
+ private void setupForUpdateKeyguardPosition(boolean oneHandedMode) {
+ int mode = oneHandedMode ? MODE_ONE_HANDED : MODE_DEFAULT;
+ mKeyguardSecurityContainer.initMode(mode, mGlobalSettings, mFalsingManager,
+ mUserSwitcherController);
+ }
+
+ /** Get the ConstraintLayout constraint of the view. */
+ private ConstraintSet.Constraint getViewConstraint(int viewId) {
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.clone(mKeyguardSecurityContainer);
+ return constraintSet.getConstraint(viewId);
+ }
}
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index 0ab5a8afe907..80d9d972ed0a 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -270,7 +270,8 @@ class AssociationRequestsProcessor {
final long callingIdentity = Binder.clearCallingIdentity();
try {
association = mService.createAssociation(userId, packageName, macAddress,
- request.getDisplayName(), request.getDeviceProfile(), request.isSelfManaged());
+ request.getDisplayName(), request.getDeviceProfile(),
+ request.getAssociatedDevice(), request.isSelfManaged());
} finally {
Binder.restoreCallingIdentity(callingIdentity);
}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 0426c79358ba..f2cb6028e854 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -55,6 +55,7 @@ import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.companion.AssociatedDevice;
import android.companion.AssociationInfo;
import android.companion.AssociationRequest;
import android.companion.DeviceNotAssociatedException;
@@ -872,23 +873,25 @@ public class CompanionDeviceManagerService extends SystemService {
/**
* @deprecated use
- * {@link #createAssociation(int, String, MacAddress, CharSequence, String, boolean)}
+ * {@link #createAssociation(int, String, MacAddress, CharSequence, String, AssociatedDevice,
+ * boolean)}
*/
@Deprecated
void legacyCreateAssociation(@UserIdInt int userId, @NonNull String deviceMacAddress,
@NonNull String packageName, @Nullable String deviceProfile) {
final MacAddress macAddress = MacAddress.fromString(deviceMacAddress);
- createAssociation(userId, packageName, macAddress, null, deviceProfile, false);
+ createAssociation(userId, packageName, macAddress, null, deviceProfile, null, false);
}
AssociationInfo createAssociation(@UserIdInt int userId, @NonNull String packageName,
@Nullable MacAddress macAddress, @Nullable CharSequence displayName,
- @Nullable String deviceProfile, boolean selfManaged) {
+ @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice,
+ boolean selfManaged) {
final int id = getNewAssociationIdForPackage(userId, packageName);
final long timestamp = System.currentTimeMillis();
final AssociationInfo association = new AssociationInfo(id, userId, packageName,
- macAddress, displayName, deviceProfile, selfManaged,
+ macAddress, displayName, deviceProfile, associatedDevice, selfManaged,
/* notifyOnDeviceNearby */ false, /* revoked */ false, timestamp, Long.MAX_VALUE);
Slog.i(TAG, "New CDM association created=" + association);
mAssociationStore.addAssociation(association);
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index 4b56c1b28036..c4f576647058 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -229,6 +229,11 @@ final class PersistentDataStore {
/**
* Reads previously persisted data for the given user "into" the provided containers.
*
+ * Note that {@link AssociationInfo#getAssociatedDevice()} will always be {@code null} after
+ * retrieval from this datastore because it is not persisted (by design). This means that
+ * persisted data is not guaranteed to be identical to the initial data that was stored at the
+ * time of association.
+ *
* @param userId Android UserID
* @param associationsOut a container to read the {@link AssociationInfo}s "into".
* @param previouslyUsedIdsPerPackageOut a container to read the used IDs "into".
@@ -293,6 +298,9 @@ final class PersistentDataStore {
/**
* Persisted data to the disk.
*
+ * Note that associatedDevice field in {@link AssociationInfo} is not persisted by this
+ * datastore implementation.
+ *
* @param userId Android UserID
* @param associations a set of user's associations.
* @param previouslyUsedIdsPerPackage a set previously used Association IDs for the user.
@@ -419,7 +427,7 @@ final class PersistentDataStore {
final long timeApproved = readLongAttribute(parser, XML_ATTR_TIME_APPROVED, 0L);
out.add(new AssociationInfo(associationId, userId, appPackage,
- MacAddress.fromString(deviceAddress), null, profile,
+ MacAddress.fromString(deviceAddress), null, profile, null,
/* managedByCompanionApp */ false, notify, /* revoked */ false, timeApproved,
Long.MAX_VALUE));
}
@@ -556,9 +564,11 @@ final class PersistentDataStore {
boolean notify, boolean revoked, long timeApproved, long lastTimeConnected) {
AssociationInfo associationInfo = null;
try {
+ // We do not persist AssociatedDevice, which means that AssociationInfo retrieved from
+ // datastore is not guaranteed to be identical to the one from initial association.
associationInfo = new AssociationInfo(associationId, userId, appPackage, macAddress,
- displayName, profile, selfManaged, notify, revoked, timeApproved,
- lastTimeConnected);
+ displayName, profile, null, selfManaged, notify, revoked,
+ timeApproved, lastTimeConnected);
} catch (Exception e) {
if (DEBUG) Log.w(TAG, "Could not create AssociationInfo", e);
}
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index 285c4068d84b..41044bfc70ca 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -16,12 +16,18 @@
package android.os;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
import com.android.internal.os.BinderCallsStats;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Collection;
import java.util.List;
+
/**
* Battery stats local system service interface. This is used to pass internal data out of
* BatteryStatsImpl, as well as make unchecked calls into BatteryStatsImpl.
@@ -29,6 +35,19 @@ import java.util.List;
* @hide Only for use within Android OS.
*/
public abstract class BatteryStatsInternal {
+
+ public static final int CPU_WAKEUP_SUBSYSTEM_UNKNOWN = -1;
+ public static final int CPU_WAKEUP_SUBSYSTEM_ALARM = 1;
+
+ /** @hide */
+ @IntDef(prefix = {"CPU_WAKEUP_SUBSYSTEM_"}, value = {
+ CPU_WAKEUP_SUBSYSTEM_UNKNOWN,
+ CPU_WAKEUP_SUBSYSTEM_ALARM,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface CpuWakeupSubsystem {
+ }
+
/**
* Returns the wifi interfaces.
*/
@@ -56,9 +75,10 @@ public abstract class BatteryStatsInternal {
/**
* Inform battery stats how many deferred jobs existed when the app got launched and how
* long ago was the last job execution for the app.
- * @param uid the uid of the app.
+ *
+ * @param uid the uid of the app.
* @param numDeferred number of deferred jobs.
- * @param sinceLast how long in millis has it been since a job was run
+ * @param sinceLast how long in millis has it been since a job was run
*/
public abstract void noteJobsDeferred(int uid, int numDeferred, long sinceLast);
@@ -72,4 +92,11 @@ public abstract class BatteryStatsInternal {
* Informs battery stats of native thread IDs of threads taking incoming binder calls.
*/
public abstract void noteBinderThreadNativeIds(int[] binderThreadNativeTids);
+
+ /**
+ * Reports any activity that could potentially have caused the CPU to wake up.
+ * Accepts a timestamp to allow the reporter to report it before or after the event.
+ */
+ public abstract void noteCpuWakingActivity(@CpuWakeupSubsystem int subsystem,
+ long elapsedMillis, @NonNull int... uids);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b34fe69f5a3e..ee13118f3e5c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5285,7 +5285,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// Tell anyone interested that we are done booting!
SystemProperties.set("sys.boot_completed", "1");
SystemProperties.set("dev.bootcomplete", "1");
- mUserController.sendBootCompleted(
+ mUserController.onBootComplete(
new IIntentReceiver.Stub() {
@Override
public void performReceive(Intent intent, int resultCode,
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index d9d29d650f03..75d1f6897111 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -81,9 +81,11 @@ import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
import android.telephony.SignalStrength;
import android.telephony.TelephonyManager;
+import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.StatsEvent;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
@@ -104,6 +106,7 @@ import com.android.server.power.stats.BatteryExternalStatsWorker;
import com.android.server.power.stats.BatteryStatsImpl;
import com.android.server.power.stats.BatteryUsageStatsProvider;
import com.android.server.power.stats.BatteryUsageStatsStore;
+import com.android.server.power.stats.CpuWakeupStats;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
import java.io.File;
@@ -120,6 +123,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@@ -142,6 +146,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
private final PowerProfile mPowerProfile;
final BatteryStatsImpl mStats;
+ @GuardedBy("mWakeupStats")
+ final CpuWakeupStats mCpuWakeupStats;
private final BatteryUsageStatsStore mBatteryUsageStatsStore;
private final BatteryStatsImpl.UserInfoProvider mUserManagerUserInfoProvider;
private final Context mContext;
@@ -373,6 +379,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats,
mBatteryUsageStatsStore);
+ mCpuWakeupStats = new CpuWakeupStats(context, R.xml.irq_device_map);
}
public void publish() {
@@ -464,6 +471,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mStats.noteBinderThreadNativeIds(binderThreadNativeTids);
}
}
+
+ @Override
+ public void noteCpuWakingActivity(int subsystem, long elapsedMillis, int... uids) {
+ Objects.requireNonNull(uids);
+ mCpuWakeupStats.noteWakingActivity(subsystem, elapsedMillis, uids);
+ }
}
@Override
@@ -2251,12 +2264,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub
try {
String reason;
while ((reason = waitWakeup()) != null) {
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final long nowUptime = SystemClock.uptimeMillis();
// Wait for the completion of pending works if there is any
awaitCompletion();
-
+ mCpuWakeupStats.noteWakeupTimeAndReason(nowElapsed, nowUptime, reason);
synchronized (mStats) {
- mStats.noteWakeupReasonLocked(reason,
- SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
+ mStats.noteWakeupReasonLocked(reason, nowElapsed, nowUptime);
}
}
} catch (RuntimeException e) {
@@ -2312,6 +2326,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
pw.println(" --read-daily: read-load last written daily stats.");
pw.println(" --settings: dump the settings key/values related to batterystats");
pw.println(" --cpu: dump cpu stats for debugging purpose");
+ pw.println(" --wakeups: dump CPU wakeup history and attribution.");
pw.println(" --power-profile: dump the power profile constants");
pw.println(" --usage: write battery usage stats. Optional arguments:");
pw.println(" --proto: output as a binary protobuffer");
@@ -2567,6 +2582,10 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
dumpUsageStatsToProto(fd, pw, model, proto);
return;
+ } else if ("--wakeups".equals(arg)) {
+ mCpuWakeupStats.dump(new IndentingPrintWriter(pw, " "),
+ SystemClock.elapsedRealtime());
+ return;
} else if ("-a".equals(arg)) {
flags |= BatteryStats.DUMP_VERBOSE;
} else if (arg.length() > 0 && arg.charAt(0) == '-'){
@@ -2697,12 +2716,16 @@ public final class BatteryStatsService extends IBatteryStats.Stub
} else {
if (DBG) Slog.d(TAG, "begin dumpLocked from UID " + Binder.getCallingUid());
awaitCompletion();
+
synchronized (mStats) {
mStats.dumpLocked(mContext, pw, flags, reqUid, historyStart);
if (writeData) {
mStats.writeAsyncLocked();
}
}
+ pw.println();
+ mCpuWakeupStats.dump(new IndentingPrintWriter(pw, " "), SystemClock.elapsedRealtime());
+
if (DBG) Slog.d(TAG, "end dumpLocked");
}
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 226c63862226..216a48ec699c 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -439,11 +439,6 @@ class UserController implements Handler.Callback {
mUserLru.add(UserHandle.USER_SYSTEM);
mLockPatternUtils = mInjector.getLockPatternUtils();
updateStartedUserArrayLU();
-
- // TODO(b/232452368): currently mAllowUserUnlocking is only used on devices with HSUM
- // (Headless System User Mode), but on master it will be used by all devices (and hence this
- // initial assignment should be removed).
- mAllowUserUnlocking = !UserManager.isHeadlessSystemUserMode();
}
void setInitialConfig(boolean userSwitchUiEnabled, int maxRunningUsers,
@@ -602,8 +597,11 @@ class UserController implements Handler.Callback {
if (!mInjector.getUserManager().isPreCreated(userId)) {
mHandler.sendMessage(mHandler.obtainMessage(REPORT_LOCKED_BOOT_COMPLETE_MSG,
userId, 0));
- // In case of headless system user mode, do not send boot complete broadcast for
- // system user as it is sent by sendBootCompleted call.
+ // The "locked boot complete" broadcast for the system user is supposed be sent when
+ // the device has finished booting. Normally, that is the same time that the system
+ // user transitions to RUNNING_LOCKED. However, in "headless system user mode", the
+ // system user is explicitly started before the device has finished booting. In
+ // that case, we need to wait until onBootComplete() to send the broadcast.
if (!(UserManager.isHeadlessSystemUserMode() && uss.mHandle.isSystem())) {
// ACTION_LOCKED_BOOT_COMPLETED
sendLockedBootCompletedBroadcast(resultTo, userId);
@@ -1808,15 +1806,13 @@ class UserController implements Handler.Callback {
*/
private boolean maybeUnlockUser(@UserIdInt int userId, @Nullable IProgressListener listener) {
- // Delay user unlocking for headless system user mode until the system boot
- // completes. When the system boot completes, the {@link #onBootCompleted()}
- // method unlocks all started users for headless system user mode. This is done
- // to prevent unlocking the users too early during the system boot up.
- // Otherwise, emulated volumes are mounted too early during the system
- // boot up. When vold is reset on boot complete, vold kills all apps/services
- // (that use these emulated volumes) before unmounting the volumes(b/241929666).
- // In the past, these killings have caused the system to become too unstable on
- // some occasions.
+ // We cannot allow users to be unlocked before PHASE_BOOT_COMPLETED, for two reasons.
+ // First, emulated volumes aren't supposed to be used until then; StorageManagerService
+ // assumes it can reset everything upon reaching PHASE_BOOT_COMPLETED. Second, on some
+ // devices the Weaver HAL needed to unlock the user's storage isn't available until sometime
+ // shortly before PHASE_BOOT_COMPLETED. The below logic enforces a consistent flow across
+ // all devices, regardless of their Weaver implementation.
+ //
// Any unlocks that get delayed by this will be done by onBootComplete() instead.
if (!mAllowUserUnlocking) {
Slogf.i(TAG, "Not unlocking user %d yet because boot hasn't completed", userId);
@@ -2424,11 +2420,8 @@ class UserController implements Handler.Callback {
}
}
- /**
- * @deprecated TODO(b/232452368): this logic will be merged into sendBootCompleted
- */
- @Deprecated
- private void onBootCompletedOnHeadlessSystemUserModeDevices() {
+ void onBootComplete(IIntentReceiver resultTo) {
+ // Now that PHASE_BOOT_COMPLETED has been reached, user unlocking is allowed.
setAllowUserUnlocking(true);
// Get a copy of mStartedUsers to use outside of lock.
@@ -2436,37 +2429,30 @@ class UserController implements Handler.Callback {
synchronized (mLock) {
startedUsers = mStartedUsers.clone();
}
+ // In non-headless system user mode, call finishUserBoot() to transition the system user
+ // from the BOOTING state to RUNNING_LOCKED, then to RUNNING_UNLOCKED if possible.
+ //
+ // In headless system user mode, additional users may have been started, and all users
+ // (including the system user) that ever get started are started explicitly. In this case,
+ // we should *not* transition users out of the BOOTING state using finishUserBoot(), as that
+ // doesn't handle issuing the needed onUserStarting() call, and it would just race with an
+ // explicit start anyway. We do, however, need to send the "locked boot complete" broadcast
+ // for the system user, as that got skipped earlier due to the *device* boot not being
+ // complete yet. We also need to try to unlock all started users, since until now explicit
+ // user starts didn't proceed to unlocking, due to it being too early in the device boot.
+ //
// USER_SYSTEM must be processed first. It will be first in the array, as its ID is lowest.
Preconditions.checkArgument(startedUsers.keyAt(0) == UserHandle.USER_SYSTEM);
for (int i = 0; i < startedUsers.size(); i++) {
- UserState uss = startedUsers.valueAt(i);
- int userId = uss.mHandle.getIdentifier();
- Slogf.i(TAG, "Attempting to unlock user %d on boot complete", userId);
- maybeUnlockUser(userId);
- }
- }
-
- void sendBootCompleted(IIntentReceiver resultTo) {
- if (UserManager.isHeadlessSystemUserMode()) {
- // Unlocking users is delayed until boot complete for headless system user mode.
- onBootCompletedOnHeadlessSystemUserModeDevices();
- }
-
- // Get a copy of mStartedUsers to use outside of lock
- SparseArray<UserState> startedUsers;
- synchronized (mLock) {
- startedUsers = mStartedUsers.clone();
- }
- for (int i = 0; i < startedUsers.size(); i++) {
+ int userId = startedUsers.keyAt(i);
UserState uss = startedUsers.valueAt(i);
if (!UserManager.isHeadlessSystemUserMode()) {
finishUserBoot(uss, resultTo);
- } else if (uss.mHandle.isSystem()) {
- // In case of headless system user mode, send only locked boot complete broadcast
- // for system user since finishUserBoot call will be made using other code path;
- // for non-system user, do nothing since finishUserBoot will be called elsewhere.
- sendLockedBootCompletedBroadcast(resultTo, uss.mHandle.getIdentifier());
- return;
+ } else {
+ if (userId == UserHandle.USER_SYSTEM) {
+ sendLockedBootCompletedBroadcast(resultTo, userId);
+ }
+ maybeUnlockUser(userId);
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4b18add39999..77fea09b5ecc 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4872,6 +4872,13 @@ public class NotificationManagerService extends SystemService {
}
@Override
+ public int getHintsFromListenerNoToken() {
+ synchronized (mNotificationLock) {
+ return mListenerHints;
+ }
+ }
+
+ @Override
public void requestInterruptionFilterFromListener(INotificationListener token,
int interruptionFilter) throws RemoteException {
final long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
new file mode 100644
index 000000000000..5f76fbc0ec36
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
@@ -0,0 +1,455 @@
+/*
+ * 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.power.stats;
+
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.util.IndentingPrintWriter;
+import android.util.LongSparseArray;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
+import android.util.TimeSparseArray;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.IntPair;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Stores stats about CPU wakeups and tries to attribute them to subsystems and uids.
+ */
+public class CpuWakeupStats {
+ private static final String SUBSYSTEM_ALARM_STRING = "Alarm";
+ @VisibleForTesting
+ static final long WAKEUP_RETENTION_MS = 3 * 24 * 60 * 60_000; // 3 days.
+ @VisibleForTesting
+ static final long WAKEUP_REASON_HALF_WINDOW_MS = 500;
+
+ private final IrqDeviceMap mIrqDeviceMap;
+ private final WakingActivityHistory mRecentWakingActivity = new WakingActivityHistory();
+
+ @VisibleForTesting
+ final TimeSparseArray<Wakeup> mWakeupEvents = new TimeSparseArray<>();
+ @VisibleForTesting
+ final TimeSparseArray<SparseArray<SparseBooleanArray>> mWakeupAttribution =
+ new TimeSparseArray<>();
+
+ public CpuWakeupStats(Context context, int mapRes) {
+ mIrqDeviceMap = IrqDeviceMap.getInstance(context, mapRes);
+ }
+
+ /** Notes a wakeup reason as reported by SuspendControlService to battery stats. */
+ public synchronized void noteWakeupTimeAndReason(long elapsedRealtime, long uptime,
+ String rawReason) {
+ final Wakeup parsedWakeup = new Wakeup(rawReason, elapsedRealtime, uptime);
+ mWakeupEvents.put(elapsedRealtime, parsedWakeup);
+ attemptAttributionFor(parsedWakeup);
+ // Assuming that wakeups always arrive in monotonically increasing elapsedRealtime order,
+ // we can delete all history that will not be useful in attributing future wakeups.
+ mRecentWakingActivity.clearAllBefore(elapsedRealtime - WAKEUP_REASON_HALF_WINDOW_MS);
+
+ // Limit history of wakeups and their attribution to the last WAKEUP_RETENTION_MS. Note that
+ // the last wakeup and its attribution (if computed) is always stored, even if that wakeup
+ // had occurred before WAKEUP_RETENTION_MS.
+ int lastIdx = mWakeupEvents.closestIndexOnOrBefore(elapsedRealtime - WAKEUP_RETENTION_MS);
+ for (int i = lastIdx; i >= 0; i--) {
+ mWakeupEvents.removeAt(i);
+ }
+ lastIdx = mWakeupAttribution.closestIndexOnOrBefore(elapsedRealtime - WAKEUP_RETENTION_MS);
+ for (int i = lastIdx; i >= 0; i--) {
+ mWakeupAttribution.removeAt(i);
+ }
+ }
+
+ /** Notes a waking activity that could have potentially woken up the CPU. */
+ public synchronized void noteWakingActivity(int subsystem, long elapsedRealtime, int... uids) {
+ if (!attemptAttributionWith(subsystem, elapsedRealtime, uids)) {
+ mRecentWakingActivity.recordActivity(subsystem, elapsedRealtime, uids);
+ }
+ }
+
+ private synchronized void attemptAttributionFor(Wakeup wakeup) {
+ final SparseBooleanArray subsystems = getResponsibleSubsystemsForWakeup(wakeup);
+ if (subsystems == null) {
+ // We don't support attribution for this kind of wakeup yet.
+ return;
+ }
+
+ SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(wakeup.mElapsedMillis);
+ if (attribution == null) {
+ attribution = new SparseArray<>();
+ mWakeupAttribution.put(wakeup.mElapsedMillis, attribution);
+ }
+
+ for (int subsystemIdx = 0; subsystemIdx < subsystems.size(); subsystemIdx++) {
+ final int subsystem = subsystems.keyAt(subsystemIdx);
+
+ // Blame all activity that happened WAKEUP_REASON_HALF_WINDOW_MS before or after
+ // the wakeup from each responsible subsystem.
+ final long startTime = wakeup.mElapsedMillis - WAKEUP_REASON_HALF_WINDOW_MS;
+ final long endTime = wakeup.mElapsedMillis + WAKEUP_REASON_HALF_WINDOW_MS;
+
+ final SparseBooleanArray uidsToBlame = mRecentWakingActivity.removeBetween(subsystem,
+ startTime, endTime);
+ attribution.put(subsystem, uidsToBlame);
+ }
+ }
+
+ private synchronized boolean attemptAttributionWith(int subsystem, long activityElapsed,
+ int... uids) {
+ final int startIdx = mWakeupEvents.closestIndexOnOrAfter(
+ activityElapsed - WAKEUP_REASON_HALF_WINDOW_MS);
+ final int endIdx = mWakeupEvents.closestIndexOnOrBefore(
+ activityElapsed + WAKEUP_REASON_HALF_WINDOW_MS);
+
+ for (int wakeupIdx = startIdx; wakeupIdx <= endIdx; wakeupIdx++) {
+ final Wakeup wakeup = mWakeupEvents.valueAt(wakeupIdx);
+ final SparseBooleanArray subsystems = getResponsibleSubsystemsForWakeup(wakeup);
+ if (subsystems == null) {
+ // Unsupported for attribution
+ continue;
+ }
+ if (subsystems.get(subsystem)) {
+ // We don't expect more than one wakeup to be found within such a short window, so
+ // just attribute this one and exit
+ SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(
+ wakeup.mElapsedMillis);
+ if (attribution == null) {
+ attribution = new SparseArray<>();
+ mWakeupAttribution.put(wakeup.mElapsedMillis, attribution);
+ }
+ SparseBooleanArray uidsToBlame = attribution.get(subsystem);
+ if (uidsToBlame == null) {
+ uidsToBlame = new SparseBooleanArray(uids.length);
+ attribution.put(subsystem, uidsToBlame);
+ }
+ for (final int uid : uids) {
+ uidsToBlame.put(uid, true);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Dumps the relevant stats for cpu wakeups and their attribution to subsystem and uids */
+ public synchronized void dump(IndentingPrintWriter pw, long nowElapsed) {
+ pw.println("CPU wakeup stats:");
+ pw.increaseIndent();
+
+ mIrqDeviceMap.dump(pw);
+ pw.println();
+
+ mRecentWakingActivity.dump(pw, nowElapsed);
+ pw.println();
+
+ final SparseLongArray attributionStats = new SparseLongArray();
+ pw.println("Wakeup events:");
+ pw.increaseIndent();
+ for (int i = mWakeupEvents.size() - 1; i >= 0; i--) {
+ TimeUtils.formatDuration(mWakeupEvents.keyAt(i), nowElapsed, pw);
+ pw.println(":");
+
+ pw.increaseIndent();
+ final Wakeup wakeup = mWakeupEvents.valueAt(i);
+ pw.println(wakeup);
+ pw.print("Attribution: ");
+ final SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(
+ wakeup.mElapsedMillis);
+ if (attribution == null) {
+ pw.println("N/A");
+ } else {
+ for (int subsystemIdx = 0; subsystemIdx < attribution.size(); subsystemIdx++) {
+ if (subsystemIdx > 0) {
+ pw.print(", ");
+ }
+ final long counters = attributionStats.get(attribution.keyAt(subsystemIdx),
+ IntPair.of(0, 0));
+ int attributed = IntPair.first(counters);
+ final int total = IntPair.second(counters) + 1;
+
+ pw.print("subsystem: " + subsystemToString(attribution.keyAt(subsystemIdx)));
+ pw.print(", uids: [");
+ final SparseBooleanArray uids = attribution.valueAt(subsystemIdx);
+ if (uids != null) {
+ for (int uidIdx = 0; uidIdx < uids.size(); uidIdx++) {
+ if (uidIdx > 0) {
+ pw.print(", ");
+ }
+ UserHandle.formatUid(pw, uids.keyAt(uidIdx));
+ }
+ attributed++;
+ }
+ pw.print("]");
+
+ attributionStats.put(attribution.keyAt(subsystemIdx),
+ IntPair.of(attributed, total));
+ }
+ pw.println();
+ }
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+
+ pw.println("Attribution stats:");
+ pw.increaseIndent();
+ for (int i = 0; i < attributionStats.size(); i++) {
+ pw.print("Subsystem " + subsystemToString(attributionStats.keyAt(i)));
+ pw.print(": ");
+ final long ratio = attributionStats.valueAt(i);
+ pw.println(IntPair.first(ratio) + "/" + IntPair.second(ratio));
+ }
+ pw.println("Total: " + mWakeupEvents.size());
+ pw.decreaseIndent();
+
+ pw.decreaseIndent();
+ pw.println();
+ }
+
+ private static final class WakingActivityHistory {
+ private static final long WAKING_ACTIVITY_RETENTION_MS = 3 * 60 * 60_000; // 3 hours.
+ private SparseArray<TimeSparseArray<SparseBooleanArray>> mWakingActivity =
+ new SparseArray<>();
+
+ void recordActivity(int subsystem, long elapsedRealtime, int... uids) {
+ if (uids == null) {
+ return;
+ }
+ TimeSparseArray<SparseBooleanArray> wakingActivity = mWakingActivity.get(subsystem);
+ if (wakingActivity == null) {
+ wakingActivity = new TimeSparseArray<>();
+ mWakingActivity.put(subsystem, wakingActivity);
+ }
+ SparseBooleanArray uidsToBlame = wakingActivity.get(elapsedRealtime);
+ if (uidsToBlame == null) {
+ uidsToBlame = new SparseBooleanArray(uids.length);
+ wakingActivity.put(elapsedRealtime, uidsToBlame);
+ }
+ for (int i = 0; i < uids.length; i++) {
+ uidsToBlame.put(uids[i], true);
+ }
+ // Limit activity history per subsystem to the last WAKING_ACTIVITY_RETENTION_MS.
+ // Note that the last activity is always present, even if it occurred before
+ // WAKING_ACTIVITY_RETENTION_MS.
+ final int endIdx = wakingActivity.closestIndexOnOrBefore(
+ elapsedRealtime - WAKING_ACTIVITY_RETENTION_MS);
+ for (int i = endIdx; i >= 0; i--) {
+ wakingActivity.removeAt(endIdx);
+ }
+ }
+
+ void clearAllBefore(long elapsedRealtime) {
+ for (int subsystemIdx = mWakingActivity.size() - 1; subsystemIdx >= 0; subsystemIdx--) {
+ final TimeSparseArray<SparseBooleanArray> activityPerSubsystem =
+ mWakingActivity.valueAt(subsystemIdx);
+ final int endIdx = activityPerSubsystem.closestIndexOnOrBefore(elapsedRealtime);
+ for (int removeIdx = endIdx; removeIdx >= 0; removeIdx--) {
+ activityPerSubsystem.removeAt(removeIdx);
+ }
+ // Generally waking activity is a high frequency occurrence for any subsystem, so we
+ // don't delete the TimeSparseArray even if it is now empty, to avoid object churn.
+ // This will leave one TimeSparseArray per subsystem, which are few right now.
+ }
+ }
+
+ SparseBooleanArray removeBetween(int subsystem, long startElapsed, long endElapsed) {
+ final SparseBooleanArray uidsToReturn = new SparseBooleanArray();
+
+ final TimeSparseArray<SparseBooleanArray> activityForSubsystem =
+ mWakingActivity.get(subsystem);
+ if (activityForSubsystem != null) {
+ final int startIdx = activityForSubsystem.closestIndexOnOrAfter(startElapsed);
+ final int endIdx = activityForSubsystem.closestIndexOnOrBefore(endElapsed);
+ for (int i = endIdx; i >= startIdx; i--) {
+ final SparseBooleanArray uidsForTime = activityForSubsystem.valueAt(i);
+ for (int j = 0; j < uidsForTime.size(); j++) {
+ if (uidsForTime.valueAt(j)) {
+ uidsToReturn.put(uidsForTime.keyAt(j), true);
+ }
+ }
+ }
+ // More efficient to remove in a separate loop as it avoids repeatedly calling gc().
+ for (int i = endIdx; i >= startIdx; i--) {
+ activityForSubsystem.removeAt(i);
+ }
+ // Generally waking activity is a high frequency occurrence for any subsystem, so we
+ // don't delete the TimeSparseArray even if it is now empty, to avoid object churn.
+ // This will leave one TimeSparseArray per subsystem, which are few right now.
+ }
+ return uidsToReturn.size() > 0 ? uidsToReturn : null;
+ }
+
+ void dump(IndentingPrintWriter pw, long nowElapsed) {
+ pw.println("Recent waking activity:");
+ pw.increaseIndent();
+ for (int i = 0; i < mWakingActivity.size(); i++) {
+ pw.println("Subsystem " + subsystemToString(mWakingActivity.keyAt(i)) + ":");
+ final LongSparseArray<SparseBooleanArray> wakingActivity =
+ mWakingActivity.valueAt(i);
+ if (wakingActivity == null) {
+ continue;
+ }
+ pw.increaseIndent();
+ for (int j = wakingActivity.size() - 1; j >= 0; j--) {
+ TimeUtils.formatDuration(wakingActivity.keyAt(j), nowElapsed, pw);
+ final SparseBooleanArray uidsToBlame = wakingActivity.valueAt(j);
+ if (uidsToBlame == null) {
+ pw.println();
+ continue;
+ }
+ pw.print(": ");
+ for (int k = 0; k < uidsToBlame.size(); k++) {
+ if (uidsToBlame.valueAt(k)) {
+ UserHandle.formatUid(pw, uidsToBlame.keyAt(k));
+ pw.print(", ");
+ }
+ }
+ pw.println();
+ }
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ }
+ }
+
+ private SparseBooleanArray getResponsibleSubsystemsForWakeup(Wakeup wakeup) {
+ if (ArrayUtils.isEmpty(wakeup.mDevices)) {
+ return null;
+ }
+ final SparseBooleanArray result = new SparseBooleanArray();
+ for (final Wakeup.IrqDevice device : wakeup.mDevices) {
+ final List<String> rawSubsystems = mIrqDeviceMap.getSubsystemsForDevice(device.mDevice);
+
+ boolean anyKnownSubsystem = false;
+ if (rawSubsystems != null) {
+ for (int i = 0; i < rawSubsystems.size(); i++) {
+ final int subsystem = stringToKnownSubsystem(rawSubsystems.get(i));
+ if (subsystem != CPU_WAKEUP_SUBSYSTEM_UNKNOWN) {
+ // Just in case the xml had arbitrary subsystem names, we want to make sure
+ // that we only put the known ones into our attribution map.
+ result.put(subsystem, true);
+ anyKnownSubsystem = true;
+ }
+ }
+ }
+ if (!anyKnownSubsystem) {
+ result.put(CPU_WAKEUP_SUBSYSTEM_UNKNOWN, true);
+ }
+ }
+ return result;
+ }
+
+ static int stringToKnownSubsystem(String rawSubsystem) {
+ switch (rawSubsystem) {
+ case SUBSYSTEM_ALARM_STRING:
+ return CPU_WAKEUP_SUBSYSTEM_ALARM;
+ }
+ return CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
+ }
+
+ static String subsystemToString(int subsystem) {
+ switch (subsystem) {
+ case CPU_WAKEUP_SUBSYSTEM_ALARM:
+ return SUBSYSTEM_ALARM_STRING;
+ case CPU_WAKEUP_SUBSYSTEM_UNKNOWN:
+ return "Unknown";
+ }
+ return "N/A";
+ }
+
+ private static final class Wakeup {
+ private static final String PARSER_TAG = "CpuWakeupStats.Wakeup";
+ private static final String ABORT_REASON_PREFIX = "Abort";
+ private static final Pattern sIrqPattern = Pattern.compile("(\\d+)\\s+(\\S+)");
+
+ String mRawReason;
+ long mElapsedMillis;
+ long mUptimeMillis;
+ IrqDevice[] mDevices;
+
+ Wakeup(String rawReason, long elapsedMillis, long uptimeMillis) {
+ mRawReason = rawReason;
+ mElapsedMillis = elapsedMillis;
+ mUptimeMillis = uptimeMillis;
+ mDevices = parseIrqDevices(rawReason);
+ }
+
+ private static IrqDevice[] parseIrqDevices(String rawReason) {
+ final String[] components = rawReason.split(":");
+ if (ArrayUtils.isEmpty(components) || components[0].startsWith(ABORT_REASON_PREFIX)) {
+ // We don't support parsing aborts yet.
+ return null;
+ }
+
+ int parsedDeviceCount = 0;
+ IrqDevice[] parsedDevices = new IrqDevice[components.length];
+
+ for (String component : components) {
+ final Matcher matcher = sIrqPattern.matcher(component);
+ if (matcher.find()) {
+ final int line;
+ final String device;
+ try {
+ line = Integer.parseInt(matcher.group(1));
+ device = matcher.group(2);
+ } catch (NumberFormatException e) {
+ Slog.e(PARSER_TAG,
+ "Exception while parsing device names from part: " + component, e);
+ continue;
+ }
+ parsedDevices[parsedDeviceCount++] = new IrqDevice(line, device);
+ }
+ }
+ return (parsedDeviceCount > 0) ? Arrays.copyOf(parsedDevices, parsedDeviceCount) : null;
+ }
+
+ @Override
+ public String toString() {
+ return "Wakeup{"
+ + "mRawReason='" + mRawReason + '\''
+ + ", mElapsedMillis=" + mElapsedMillis
+ + ", mUptimeMillis=" + TimeUtils.formatDuration(mUptimeMillis)
+ + ", mDevices=" + Arrays.toString(mDevices)
+ + '}';
+ }
+
+ static final class IrqDevice {
+ int mLine;
+ String mDevice;
+
+ IrqDevice(int line, String device) {
+ mLine = line;
+ mDevice = device;
+ }
+
+ @Override
+ public String toString() {
+ return "IrqDevice{" + "mLine=" + mLine + ", mDevice='" + mDevice + '\'' + '}';
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/IrqDeviceMap.java b/services/core/java/com/android/server/power/stats/IrqDeviceMap.java
new file mode 100644
index 000000000000..091d18e30ccc
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/IrqDeviceMap.java
@@ -0,0 +1,137 @@
+/*
+ * 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.power.stats;
+
+import android.annotation.XmlRes;
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.LongSparseArray;
+
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Parses irq_device_map.xml to store a mapping of devices that can send IRQs to the CPU to
+ * subsystems that represent some logical work happening on the device that could need an IRQ.
+ */
+public class IrqDeviceMap {
+ private static final String TAG_IRQ_DEVICE_MAP = "irq-device-map";
+ private static final String TAG_DEVICE = "device";
+ private static final String TAG_SUBSYSTEM = "subsystem";
+ private static final String ATTR_NAME = "name";
+
+ private static LongSparseArray<IrqDeviceMap> sInstanceMap = new LongSparseArray<>(1);
+
+ private final ArrayMap<String, List<String>> mSubsystemsForDevice = new ArrayMap();
+
+ private IrqDeviceMap(XmlResourceParser parser) {
+ try {
+ XmlUtils.beginDocument(parser, TAG_IRQ_DEVICE_MAP);
+
+ int type;
+ String currentDevice = null;
+ final ArraySet<String> subsystems = new ArraySet<>();
+
+ while ((type = parser.getEventType()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_DEVICE)) {
+ currentDevice = parser.getAttributeValue(null, ATTR_NAME);
+ }
+ if (currentDevice != null && type == XmlPullParser.END_TAG
+ && parser.getName().equals(TAG_DEVICE)) {
+ final int n = subsystems.size();
+ if (n > 0) {
+ mSubsystemsForDevice.put(currentDevice,
+ Collections.unmodifiableList(new ArrayList<>(subsystems)));
+ }
+ subsystems.clear();
+ currentDevice = null;
+ }
+ if (currentDevice != null && type == XmlPullParser.START_TAG
+ && parser.getName().equals(TAG_SUBSYSTEM)) {
+ parser.next();
+ if (parser.getEventType() == XmlPullParser.TEXT) {
+ subsystems.add(parser.getText());
+ }
+ }
+ parser.next();
+ }
+ } catch (XmlPullParserException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ parser.close();
+ }
+ }
+
+ /**
+ * Returns an instance of IrqDeviceMap initialzed with the given context and xml resource.
+ * The xml resource should describe the mapping in a way similar to
+ * core/res/res/xml/irq_device_map.xml.
+ */
+ public static IrqDeviceMap getInstance(Context context, @XmlRes int resId) {
+ synchronized (IrqDeviceMap.class) {
+ final int idx = sInstanceMap.indexOfKey(resId);
+ if (idx >= 0) {
+ return sInstanceMap.valueAt(idx);
+ }
+ }
+ final XmlResourceParser parser = context.getResources().getXml(resId);
+ final IrqDeviceMap irqDeviceMap = new IrqDeviceMap(parser);
+ synchronized (IrqDeviceMap.class) {
+ sInstanceMap.put(resId, irqDeviceMap);
+ }
+ return irqDeviceMap;
+ }
+
+ List<String> getSubsystemsForDevice(String device) {
+ return mSubsystemsForDevice.get(device);
+ }
+
+ void dump(IndentingPrintWriter pw) {
+ pw.println("Irq device map:");
+ pw.increaseIndent();
+
+ final LongSparseArray<IrqDeviceMap> instanceMap;
+ synchronized (IrqDeviceMap.class) {
+ instanceMap = sInstanceMap;
+ }
+ final int idx = instanceMap.indexOfValue(this);
+ final String res = (idx >= 0) ? ("0x" + Long.toHexString(instanceMap.keyAt(idx))) : null;
+ pw.println("Loaded from xml resource: " + res);
+
+ pw.println("Map:");
+ pw.increaseIndent();
+ for (int i = 0; i < mSubsystemsForDevice.size(); i++) {
+ pw.print(mSubsystemsForDevice.keyAt(i) + ": ");
+ pw.println(mSubsystemsForDevice.valueAt(i));
+ }
+ pw.decreaseIndent();
+
+ pw.decreaseIndent();
+ }
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 961c320dabe6..ad3f045987ea 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -423,7 +423,7 @@ public class WindowManagerService extends IWindowManager.Stub
* @see #ENABLE_SHELL_TRANSITIONS
*/
public static final boolean sEnableShellTransitions =
- SystemProperties.getBoolean(ENABLE_SHELL_TRANSITIONS, false);
+ SystemProperties.getBoolean(ENABLE_SHELL_TRANSITIONS, true);
/**
* Allows a fullscreen windowing mode activity to launch in its desired orientation directly
diff --git a/services/tests/servicestests/res/xml/irq_device_map_1.xml b/services/tests/servicestests/res/xml/irq_device_map_1.xml
new file mode 100644
index 000000000000..1f1a77b437ab
--- /dev/null
+++ b/services/tests/servicestests/res/xml/irq_device_map_1.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<irq-device-map>
+ <device name="test.device.1">
+ <subsystem>test.subsystem.1</subsystem>
+ <subsystem>test.subsystem.2</subsystem>
+ </device>
+ <device name="test.device.2">
+ <subsystem>test.subsystem.3</subsystem>
+ <subsystem>test.subsystem.2</subsystem>
+ </device>
+</irq-device-map> \ No newline at end of file
diff --git a/services/tests/servicestests/res/xml/irq_device_map_2.xml b/services/tests/servicestests/res/xml/irq_device_map_2.xml
new file mode 100644
index 000000000000..508c98d871da
--- /dev/null
+++ b/services/tests/servicestests/res/xml/irq_device_map_2.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<!-- Long comment describing anything that may be needed
+for this file and its maintenance -->
+<irq-device-map>
+ <!-- Small comment specific to this device -->
+ <invalid name="test.device.1">
+ <!-- These valid subsystem definitions should be ignored because of invalid parent tag -->
+ <subsystem>test.subsystem.1</subsystem>
+ <subsystem>test.subsystem.2</subsystem>
+ </invalid>
+ <device name="test.device.2">
+ <!-- Multiline comment to describe nuances
+ about why these subsystems
+ rely on this hardware device
+ to wake the CPU up from sleep
+ -->
+ <subsystem>test.subsystem.3</subsystem>
+ <!-- Small comment specific to test.subsystem.4 -->
+ <subsystem>test.subsystem.4</subsystem>
+ <subsystem>test.subsystem.5</subsystem>
+ <!-- Duplicates should be ignored -->
+ <subsystem>test.subsystem.4</subsystem>
+ <subsystem>test.subsystem.3</subsystem>
+ <subsystem>test.subsystem.5</subsystem>
+ </device>
+
+ <device name="test.device.3">
+ <!-- All child tags are invalid, mapping should be empty / non-existent for this device -->
+ <subsys>ignored</subsys>
+ <system>redundant</system>
+ </device>
+
+ <device name="test.device.4">
+ <!-- Invalid child tags should be skipped but others should be mapped -->
+ <invalid>unused</invalid>
+ <random>skipped</random>
+ <subsystem>test.subsystem.1</subsystem>
+ <subsystem>test.subsystem.4</subsystem>
+ </device>
+
+</irq-device-map> \ No newline at end of file
diff --git a/services/tests/servicestests/res/xml/irq_device_map_3.xml b/services/tests/servicestests/res/xml/irq_device_map_3.xml
new file mode 100644
index 000000000000..498b676dd1dc
--- /dev/null
+++ b/services/tests/servicestests/res/xml/irq_device_map_3.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+<irq-device-map>
+ <device name="test.alarm.device">
+ <subsystem>Alarm</subsystem>
+ </device>
+ <device name="test.wifi.device">
+ <subsystem>undefined</subsystem>
+ </device>
+</irq-device-map> \ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 2d2c76c40b10..0b776a3e6642 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -181,11 +181,8 @@ public class UserControllerTest {
doNothing().when(mInjector).taskSupervisorRemoveUser(anyInt());
mockIsUsersOnSecondaryDisplaysEnabled(false);
// All UserController params are set to default.
- mUserController = new UserController(mInjector);
- // TODO(b/232452368): need to explicitly call setAllowUserUnlocking(), otherwise most
- // tests would fail. But we might need to disable it for the onBootComplete() test (i.e,
- // to make sure the users are unlocked at the right time)
+ mUserController = new UserController(mInjector);
mUserController.setAllowUserUnlocking(true);
setUpUser(TEST_USER_ID, NO_USERINFO_FLAGS);
setUpUser(TEST_PRE_CREATED_USER_ID, NO_USERINFO_FLAGS, /* preCreated= */ true, null);
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 6388c7d52f10..9c5d1a5b0610 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -226,7 +226,7 @@ public class VirtualDeviceManagerServiceTest {
mContext.getSystemService(WindowManager.class), threadVerifier);
mAssociationInfo = new AssociationInfo(1, 0, null,
- MacAddress.BROADCAST_ADDRESS, "", null, true, false, false, 0, 0);
+ MacAddress.BROADCAST_ADDRESS, "", null, null, true, false, false, 0, 0);
mVdms = new VirtualDeviceManagerService(mContext);
mLocalService = mVdms.getLocalServiceInstance();
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
new file mode 100644
index 000000000000..7731a326cf61
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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.power.stats;
+
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
+
+import static com.android.server.power.stats.CpuWakeupStats.WAKEUP_REASON_HALF_WINDOW_MS;
+import static com.android.server.power.stats.CpuWakeupStats.WAKEUP_RETENTION_MS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.frameworks.servicestests.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ThreadLocalRandom;
+
+@RunWith(AndroidJUnit4.class)
+public class CpuWakeupStatsTest {
+ private static final String KERNEL_REASON_ALARM_IRQ = "120 test.alarm.device";
+ private static final String KERNEL_REASON_UNKNOWN_IRQ = "140 test.unknown.device";
+ private static final String KERNEL_REASON_UNKNOWN = "unsupported-free-form-reason";
+
+ private static final int TEST_UID_1 = 13239823;
+ private static final int TEST_UID_2 = 25268423;
+ private static final int TEST_UID_3 = 92261423;
+ private static final int TEST_UID_4 = 56926423;
+ private static final int TEST_UID_5 = 76421423;
+
+ private static final Context sContext = InstrumentationRegistry.getTargetContext();
+ private final ThreadLocalRandom mRandom = ThreadLocalRandom.current();
+
+ @Test
+ public void removesOldWakeups() {
+ final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_1);
+
+ final Set<Long> timestamps = new HashSet<>();
+ final long firstWakeup = 453192;
+
+ obj.noteWakeupTimeAndReason(firstWakeup, 32, "unused");
+ timestamps.add(firstWakeup);
+ for (int i = 1; i < 1000; i++) {
+ final long delta = mRandom.nextLong(WAKEUP_RETENTION_MS);
+ if (timestamps.add(firstWakeup + delta)) {
+ obj.noteWakeupTimeAndReason(firstWakeup + delta, i, "unused");
+ }
+ }
+ assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size());
+
+ obj.noteWakeupTimeAndReason(firstWakeup + WAKEUP_RETENTION_MS + 1242, 231, "unused");
+ assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size());
+
+ for (int i = 0; i < 100; i++) {
+ final long now = mRandom.nextLong(WAKEUP_RETENTION_MS + 1, 100 * WAKEUP_RETENTION_MS);
+ obj.noteWakeupTimeAndReason(now, i, "unused");
+ assertThat(obj.mWakeupEvents.closestIndexOnOrBefore(now - WAKEUP_RETENTION_MS))
+ .isLessThan(0);
+ }
+ }
+
+ @Test
+ public void alarmIrqAttributionSolo() {
+ final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3);
+ final long wakeupTime = 12423121;
+
+ obj.noteWakeupTimeAndReason(wakeupTime, 1, KERNEL_REASON_ALARM_IRQ);
+
+ // Outside the window, so should be ignored.
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
+ wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
+ wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
+ // Should be attributed
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3, TEST_UID_5);
+
+ final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+ assertThat(attribution).isNotNull();
+ assertThat(attribution.size()).isEqualTo(1);
+ assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_1)).isEqualTo(false);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_2)).isEqualTo(false);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(true);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(true);
+ }
+
+ @Test
+ public void alarmIrqAttributionCombined() {
+ final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3);
+ final long wakeupTime = 92123210;
+
+ obj.noteWakeupTimeAndReason(wakeupTime, 4,
+ KERNEL_REASON_UNKNOWN_IRQ + ":" + KERNEL_REASON_ALARM_IRQ);
+
+ // Outside the window, so should be ignored.
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
+ wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
+ wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
+ // Should be attributed
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4,
+ TEST_UID_5);
+
+ final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+ assertThat(attribution).isNotNull();
+ assertThat(attribution.size()).isEqualTo(2);
+ assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_1)).isEqualTo(false);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_2)).isEqualTo(false);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(true);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_4)).isEqualTo(true);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(true);
+ assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isTrue();
+ }
+
+ @Test
+ public void unknownIrqAttribution() {
+ final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3);
+ final long wakeupTime = 92123410;
+
+ obj.noteWakeupTimeAndReason(wakeupTime, 24, KERNEL_REASON_UNKNOWN_IRQ);
+
+ // Unrelated subsystems, should not be attributed
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4,
+ TEST_UID_5);
+
+ final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+ assertThat(attribution).isNotNull();
+ assertThat(attribution.size()).isEqualTo(1);
+ assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isTrue();
+ final SparseBooleanArray uids = attribution.get(CPU_WAKEUP_SUBSYSTEM_UNKNOWN);
+ assertThat(uids == null || uids.size() == 0).isTrue();
+ }
+
+ @Test
+ public void unknownAttribution() {
+ final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3);
+ final long wakeupTime = 72123210;
+
+ obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_UNKNOWN);
+
+ // Unrelated subsystems, should be ignored.
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4);
+
+ // There should be nothing in the attribution map.
+ assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/IrqDeviceMapTest.java b/services/tests/servicestests/src/com/android/server/power/stats/IrqDeviceMapTest.java
new file mode 100644
index 000000000000..43d9e60c4a2b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/stats/IrqDeviceMapTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.power.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.frameworks.servicestests.R;
+import com.android.internal.util.CollectionUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class IrqDeviceMapTest {
+ private static final String TEST_DEVICE_1 = "test.device.1";
+ private static final String TEST_DEVICE_2 = "test.device.2";
+ private static final String TEST_DEVICE_3 = "test.device.3";
+ private static final String TEST_DEVICE_4 = "test.device.4";
+
+ private static final String TEST_SUBSYSTEM_1 = "test.subsystem.1";
+ private static final String TEST_SUBSYSTEM_2 = "test.subsystem.2";
+ private static final String TEST_SUBSYSTEM_3 = "test.subsystem.3";
+ private static final String TEST_SUBSYSTEM_4 = "test.subsystem.4";
+ private static final String TEST_SUBSYSTEM_5 = "test.subsystem.5";
+
+ private static final Context sContext = InstrumentationRegistry.getTargetContext();
+
+ @Test
+ public void cachesInstancesPerXml() {
+ IrqDeviceMap irqDeviceMap1 = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_1);
+ IrqDeviceMap irqDeviceMap2 = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_1);
+ assertThat(irqDeviceMap1).isSameInstanceAs(irqDeviceMap2);
+
+ irqDeviceMap2 = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_2);
+ assertThat(irqDeviceMap1).isNotSameInstanceAs(irqDeviceMap2);
+
+ irqDeviceMap1 = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_2);
+ assertThat(irqDeviceMap1).isSameInstanceAs(irqDeviceMap2);
+ }
+
+ @Test
+ public void simpleXml() {
+ IrqDeviceMap deviceMap = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_1);
+
+ List<String> subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_1);
+ assertThat(subsystems).hasSize(2);
+ // No specific order is required.
+ assertThat(subsystems).containsExactly(TEST_SUBSYSTEM_2, TEST_SUBSYSTEM_1);
+
+ subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_2);
+ assertThat(subsystems).hasSize(2);
+ // No specific order is required.
+ assertThat(subsystems).containsExactly(TEST_SUBSYSTEM_2, TEST_SUBSYSTEM_3);
+ }
+
+ @Test
+ public void complexXml() {
+ IrqDeviceMap deviceMap = IrqDeviceMap.getInstance(sContext, R.xml.irq_device_map_2);
+
+ List<String> subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_1);
+ assertThat(CollectionUtils.isEmpty(subsystems)).isTrue();
+
+ subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_2);
+ assertThat(subsystems).hasSize(3);
+ // No specific order is required.
+ assertThat(subsystems).containsExactly(TEST_SUBSYSTEM_3, TEST_SUBSYSTEM_4,
+ TEST_SUBSYSTEM_5);
+
+ subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_3);
+ assertThat(CollectionUtils.isEmpty(subsystems)).isTrue();
+
+ subsystems = deviceMap.getSubsystemsForDevice(TEST_DEVICE_4);
+ assertThat(subsystems).hasSize(2);
+ // No specific order is required.
+ assertThat(subsystems).containsExactly(TEST_SUBSYSTEM_4, TEST_SUBSYSTEM_1);
+ }
+}