Merge "Don't back off for first few system stops." into udc-dev
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index 8e9f474..17076bc 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -98,6 +98,15 @@
public static final int TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1;
/**
+ * Delay freezing the app when the broadcast is delivered. This flag is not required if
+ * TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED or
+ * TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED are specified, as those will
+ * already defer freezing during the allowlist duration.
+ * @hide temporarily until the next release
+ */
+ public static final int TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED = 1 << 2;
+
+ /**
* The list of temp allow list types.
* @hide
*/
@@ -105,6 +114,7 @@
TEMPORARY_ALLOW_LIST_TYPE_NONE,
TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED,
+ TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED
})
@Retention(RetentionPolicy.SOURCE)
public @interface TempAllowListType {}
@@ -216,6 +226,11 @@
* Set temp-allow-list for transferring accounts between users.
*/
public static final int REASON_ACCOUNT_TRANSFER = 104;
+ /**
+ * Set temp-allow-list for server push messaging that can be deferred.
+ * @hide temporarily until the next release
+ */
+ public static final int REASON_PUSH_MESSAGING_DEFERRABLE = 105;
/* Reason code range 200-299 are reserved for broadcast actions */
/**
@@ -449,6 +464,7 @@
REASON_PUSH_MESSAGING_OVER_QUOTA,
REASON_ACTIVITY_RECOGNITION,
REASON_ACCOUNT_TRANSFER,
+ REASON_PUSH_MESSAGING_DEFERRABLE,
REASON_BOOT_COMPLETED,
REASON_PRE_BOOT_COMPLETED,
REASON_LOCKED_BOOT_COMPLETED,
@@ -781,6 +797,8 @@
return "ACTIVITY_RECOGNITION";
case REASON_ACCOUNT_TRANSFER:
return "REASON_ACCOUNT_TRANSFER";
+ case REASON_PUSH_MESSAGING_DEFERRABLE:
+ return "PUSH_MESSAGING_DEFERRABLE";
case REASON_BOOT_COMPLETED:
return "BOOT_COMPLETED";
case REASON_PRE_BOOT_COMPLETED:
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e42e526..9eb9d66 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -4156,6 +4156,7 @@
public static class WindowInfosListenerForTest.WindowInfo {
field @NonNull public final android.graphics.Rect bounds;
field public final boolean isTrustedOverlay;
+ field public final boolean isVisible;
field @NonNull public final String name;
field @NonNull public final android.os.IBinder windowToken;
}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index afe375c..96118f6 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -616,6 +616,7 @@
/** {@hide} */
public PackageInstaller(IPackageInstaller installer,
String installerPackageName, String installerAttributionTag, int userId) {
+ Objects.requireNonNull(installer, "installer cannot be null");
mInstaller = installer;
mInstallerPackageName = installerPackageName;
mAttributionTag = installerAttributionTag;
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 230f511..7f0a666 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -2231,7 +2231,7 @@
mDestroyed = true;
- if (mIWallpaperEngine.mDisplayManager != null) {
+ if (mIWallpaperEngine != null && mIWallpaperEngine.mDisplayManager != null) {
mIWallpaperEngine.mDisplayManager.unregisterDisplayListener(mDisplayListener);
}
diff --git a/core/java/android/view/ContentRecordingSession.java b/core/java/android/view/ContentRecordingSession.java
index fdecb8b..a89f795 100644
--- a/core/java/android/view/ContentRecordingSession.java
+++ b/core/java/android/view/ContentRecordingSession.java
@@ -87,7 +87,7 @@
*
* <p>Only set on the server side to sanitize any input from the client process.
*/
- private boolean mWaitingToRecord = false;
+ private boolean mWaitingForConsent = false;
/**
* Default instance, with recording the display.
@@ -181,7 +181,7 @@
@RecordContent int contentToRecord,
int displayToRecord,
@Nullable IBinder tokenToRecord,
- boolean waitingToRecord) {
+ boolean waitingForConsent) {
this.mVirtualDisplayId = virtualDisplayId;
this.mContentToRecord = contentToRecord;
@@ -195,7 +195,7 @@
this.mDisplayToRecord = displayToRecord;
this.mTokenToRecord = tokenToRecord;
- this.mWaitingToRecord = waitingToRecord;
+ this.mWaitingForConsent = waitingForConsent;
// onConstructed(); // You can define this method to get a callback
}
@@ -246,8 +246,8 @@
* <p>Only set on the server side to sanitize any input from the client process.
*/
@DataClass.Generated.Member
- public boolean isWaitingToRecord() {
- return mWaitingToRecord;
+ public boolean isWaitingForConsent() {
+ return mWaitingForConsent;
}
/**
@@ -309,8 +309,8 @@
* <p>Only set on the server side to sanitize any input from the client process.
*/
@DataClass.Generated.Member
- public @NonNull ContentRecordingSession setWaitingToRecord( boolean value) {
- mWaitingToRecord = value;
+ public @NonNull ContentRecordingSession setWaitingForConsent( boolean value) {
+ mWaitingForConsent = value;
return this;
}
@@ -325,7 +325,7 @@
"contentToRecord = " + recordContentToString(mContentToRecord) + ", " +
"displayToRecord = " + mDisplayToRecord + ", " +
"tokenToRecord = " + mTokenToRecord + ", " +
- "waitingToRecord = " + mWaitingToRecord +
+ "waitingForConsent = " + mWaitingForConsent +
" }";
}
@@ -346,7 +346,7 @@
&& mContentToRecord == that.mContentToRecord
&& mDisplayToRecord == that.mDisplayToRecord
&& java.util.Objects.equals(mTokenToRecord, that.mTokenToRecord)
- && mWaitingToRecord == that.mWaitingToRecord;
+ && mWaitingForConsent == that.mWaitingForConsent;
}
@Override
@@ -360,7 +360,7 @@
_hash = 31 * _hash + mContentToRecord;
_hash = 31 * _hash + mDisplayToRecord;
_hash = 31 * _hash + java.util.Objects.hashCode(mTokenToRecord);
- _hash = 31 * _hash + Boolean.hashCode(mWaitingToRecord);
+ _hash = 31 * _hash + Boolean.hashCode(mWaitingForConsent);
return _hash;
}
@@ -371,7 +371,7 @@
// void parcelFieldName(Parcel dest, int flags) { ... }
byte flg = 0;
- if (mWaitingToRecord) flg |= 0x10;
+ if (mWaitingForConsent) flg |= 0x10;
if (mTokenToRecord != null) flg |= 0x8;
dest.writeByte(flg);
dest.writeInt(mVirtualDisplayId);
@@ -392,7 +392,7 @@
// static FieldType unparcelFieldName(Parcel in) { ... }
byte flg = in.readByte();
- boolean waitingToRecord = (flg & 0x10) != 0;
+ boolean waitingForConsent = (flg & 0x10) != 0;
int virtualDisplayId = in.readInt();
int contentToRecord = in.readInt();
int displayToRecord = in.readInt();
@@ -411,7 +411,7 @@
this.mDisplayToRecord = displayToRecord;
this.mTokenToRecord = tokenToRecord;
- this.mWaitingToRecord = waitingToRecord;
+ this.mWaitingForConsent = waitingForConsent;
// onConstructed(); // You can define this method to get a callback
}
@@ -441,7 +441,7 @@
private @RecordContent int mContentToRecord;
private int mDisplayToRecord;
private @Nullable IBinder mTokenToRecord;
- private boolean mWaitingToRecord;
+ private boolean mWaitingForConsent;
private long mBuilderFieldsSet = 0L;
@@ -506,10 +506,10 @@
* <p>Only set on the server side to sanitize any input from the client process.
*/
@DataClass.Generated.Member
- public @NonNull Builder setWaitingToRecord(boolean value) {
+ public @NonNull Builder setWaitingForConsent(boolean value) {
checkNotUsed();
mBuilderFieldsSet |= 0x10;
- mWaitingToRecord = value;
+ mWaitingForConsent = value;
return this;
}
@@ -531,14 +531,14 @@
mTokenToRecord = null;
}
if ((mBuilderFieldsSet & 0x10) == 0) {
- mWaitingToRecord = false;
+ mWaitingForConsent = false;
}
ContentRecordingSession o = new ContentRecordingSession(
mVirtualDisplayId,
mContentToRecord,
mDisplayToRecord,
mTokenToRecord,
- mWaitingToRecord);
+ mWaitingForConsent);
return o;
}
@@ -551,10 +551,10 @@
}
@DataClass.Generated(
- time = 1679855157534L,
+ time = 1683628463074L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/ContentRecordingSession.java",
- inputSignatures = "public static final int RECORD_CONTENT_DISPLAY\npublic static final int RECORD_CONTENT_TASK\nprivate int mVirtualDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate int mDisplayToRecord\nprivate @android.annotation.Nullable android.os.IBinder mTokenToRecord\nprivate boolean mWaitingToRecord\npublic static android.view.ContentRecordingSession createDisplaySession(int)\npublic static android.view.ContentRecordingSession createTaskSession(android.os.IBinder)\npublic static boolean isValid(android.view.ContentRecordingSession)\npublic static boolean isProjectionOnSameDisplay(android.view.ContentRecordingSession,android.view.ContentRecordingSession)\nclass ContentRecordingSession extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genSetters=true, genEqualsHashCode=true)")
+ inputSignatures = "public static final int RECORD_CONTENT_DISPLAY\npublic static final int RECORD_CONTENT_TASK\nprivate int mVirtualDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate int mDisplayToRecord\nprivate @android.annotation.Nullable android.os.IBinder mTokenToRecord\nprivate boolean mWaitingForConsent\npublic static android.view.ContentRecordingSession createDisplaySession(int)\npublic static android.view.ContentRecordingSession createTaskSession(android.os.IBinder)\npublic static boolean isValid(android.view.ContentRecordingSession)\npublic static boolean isProjectionOnSameDisplay(android.view.ContentRecordingSession,android.view.ContentRecordingSession)\nclass ContentRecordingSession extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genSetters=true, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 8cff066..efd50e7 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -46,8 +46,6 @@
import android.os.RemoteException;
import android.text.Selection;
import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.Spanned;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.Log;
@@ -740,7 +738,10 @@
// Since the same CharSequence instance may be reused in the TextView, we need to make
// a copy of its content so that its value will not be changed by subsequent updates
// in the TextView.
- final CharSequence eventText = stringOrSpannedStringWithoutNoCopySpans(text);
+ CharSequence trimmed = TextUtils.trimToParcelableSize(text);
+ final CharSequence eventText = trimmed != null && trimmed == text
+ ? trimmed.toString()
+ : trimmed;
final int composingStart;
final int composingEnd;
@@ -761,16 +762,6 @@
.setSelectionIndex(startIndex, endIndex)));
}
- private CharSequence stringOrSpannedStringWithoutNoCopySpans(CharSequence source) {
- if (source == null) {
- return null;
- } else if (source instanceof Spanned) {
- return new SpannableString(source, /* ignoreNoCopySpan= */ true);
- } else {
- return source.toString();
- }
- }
-
/** Public because is also used by ViewRootImpl */
public void notifyViewInsetsChanged(int sessionId, @NonNull Insets viewInsets) {
mHandler.post(() -> sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_INSETS_CHANGED)
diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java
index 1762a58..044a31f 100644
--- a/core/java/android/view/contentcapture/ViewNode.java
+++ b/core/java/android/view/contentcapture/ViewNode.java
@@ -1052,14 +1052,21 @@
}
void writeToParcel(Parcel out, boolean simple) {
- TextUtils.writeToParcel(mText, out, 0);
+ CharSequence text = TextUtils.trimToParcelableSize(mText);
+ TextUtils.writeToParcel(text, out, 0);
out.writeFloat(mTextSize);
out.writeInt(mTextStyle);
out.writeInt(mTextColor);
if (!simple) {
+ int selectionStart = text != null
+ ? Math.min(mTextSelectionStart, text.length())
+ : mTextSelectionStart;
+ int selectionEnd = text != null
+ ? Math.min(mTextSelectionEnd, text.length())
+ : mTextSelectionEnd;
out.writeInt(mTextBackgroundColor);
- out.writeInt(mTextSelectionStart);
- out.writeInt(mTextSelectionEnd);
+ out.writeInt(selectionStart);
+ out.writeInt(selectionEnd);
out.writeIntArray(mLineCharOffsets);
out.writeIntArray(mLineBaselines);
out.writeString(mHint);
diff --git a/core/java/android/window/WindowInfosListenerForTest.java b/core/java/android/window/WindowInfosListenerForTest.java
index 01e577f..cfbeeff 100644
--- a/core/java/android/window/WindowInfosListenerForTest.java
+++ b/core/java/android/window/WindowInfosListenerForTest.java
@@ -68,12 +68,18 @@
*/
public final boolean isTrustedOverlay;
+ /**
+ * True if the window is visible.
+ */
+ public final boolean isVisible;
+
WindowInfo(@NonNull IBinder windowToken, @NonNull String name, @NonNull Rect bounds,
int inputConfig) {
this.windowToken = windowToken;
this.name = name;
this.bounds = bounds;
this.isTrustedOverlay = (inputConfig & InputConfig.TRUSTED_OVERLAY) != 0;
+ this.isVisible = (inputConfig & InputConfig.NOT_VISIBLE) == 0;
}
}
@@ -131,9 +137,6 @@
private static List<WindowInfo> buildWindowInfos(InputWindowHandle[] windowHandles) {
var windowInfos = new ArrayList<WindowInfo>(windowHandles.length);
for (var handle : windowHandles) {
- if ((handle.inputConfig & InputConfig.NOT_VISIBLE) != 0) {
- continue;
- }
var bounds = new Rect(handle.frameLeft, handle.frameTop, handle.frameRight,
handle.frameBottom);
windowInfos.add(new WindowInfo(handle.getWindowToken(), handle.name, bounds,
diff --git a/core/java/com/android/internal/app/AppLocaleCollector.java b/core/java/com/android/internal/app/AppLocaleCollector.java
index 7cf428a..56f633f 100644
--- a/core/java/com/android/internal/app/AppLocaleCollector.java
+++ b/core/java/com/android/internal/app/AppLocaleCollector.java
@@ -157,13 +157,13 @@
* Get a list of system locale that removes all extensions except for the numbering system.
*/
@VisibleForTesting
- public List<LocaleStore.LocaleInfo> getSystemCurrentLocales() {
- List<LocaleStore.LocaleInfo> sysLocales = LocaleStore.getSystemCurrentLocales();
+ public Set<LocaleStore.LocaleInfo> getSystemCurrentLocales() {
+ Set<LocaleStore.LocaleInfo> sysLocales = LocaleStore.getSystemCurrentLocales();
return sysLocales.stream().filter(
// For the locale to be added into the suggestion area, its country could not be
// empty.
info -> info.getLocale().getCountry().length() > 0).collect(
- Collectors.toList());
+ Collectors.toSet());
}
@Override
@@ -225,7 +225,10 @@
// Add current system language into suggestion list
if (!isForCountryMode) {
boolean isCurrentLocale, existsInApp, existsInIme;
- for (LocaleStore.LocaleInfo localeInfo : getSystemCurrentLocales()) {
+ // filter out the system locases that are supported by the application.
+ Set<LocaleStore.LocaleInfo> localeInfoSet =
+ filterSupportedLocales(getSystemCurrentLocales(), result.mAppSupportedLocales);
+ for (LocaleStore.LocaleInfo localeInfo : localeInfoSet) {
isCurrentLocale = mAppCurrentLocale != null
&& localeInfo.getLocale().equals(mAppCurrentLocale.getLocale());
// Add the system suggestion flag if the localeInfo exists in mAllAppActiveLocales
diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java
index f4b858f..43d263b 100644
--- a/core/java/com/android/internal/app/LocaleStore.java
+++ b/core/java/com/android/internal/app/LocaleStore.java
@@ -32,7 +32,6 @@
import java.io.Serializable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -404,8 +403,8 @@
/**
* Returns a list of system locale that removes all extensions except for the numbering system.
*/
- public static List<LocaleInfo> getSystemCurrentLocales() {
- List<LocaleInfo> localeList = new ArrayList<>();
+ public static Set<LocaleInfo> getSystemCurrentLocales() {
+ Set<LocaleInfo> localeList = new HashSet<>();
LocaleList systemLangList = LocaleList.getDefault();
for(int i = 0; i < systemLangList.size(); i++) {
Locale sysLocale = getLocaleWithOnlyNumberingSystem(systemLangList.get(i));
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 21bdf09..e5d5676 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -652,9 +652,7 @@
char saveResolvedClassesDelayMsOptsBuf[
sizeof("-Xps-save-resolved-classes-delay-ms:")-1 + PROPERTY_VALUE_MAX];
char profileMinSavePeriodOptsBuf[sizeof("-Xps-min-save-period-ms:")-1 + PROPERTY_VALUE_MAX];
- char profileMinFirstSaveOptsBuf[
- sizeof("-Xps-min-first-save-ms:")-1 + PROPERTY_VALUE_MAX];
- char madviseRandomOptsBuf[sizeof("-XX:MadviseRandomAccess:")-1 + PROPERTY_VALUE_MAX];
+ char profileMinFirstSaveOptsBuf[sizeof("-Xps-min-first-save-ms:") - 1 + PROPERTY_VALUE_MAX];
char madviseWillNeedFileSizeVdex[
sizeof("-XMadviseWillNeedVdexFileSize:")-1 + PROPERTY_VALUE_MAX];
char madviseWillNeedFileSizeOdex[
@@ -866,13 +864,8 @@
jitprithreadweightOptBuf,
"-Xjitprithreadweight:");
- parseRuntimeOption("dalvik.vm.jittransitionweight",
- jittransitionweightOptBuf,
+ parseRuntimeOption("dalvik.vm.jittransitionweight", jittransitionweightOptBuf,
"-Xjittransitionweight:");
- /*
- * Madvise related options.
- */
- parseRuntimeOption("dalvik.vm.madvise-random", madviseRandomOptsBuf, "-XX:MadviseRandomAccess:");
/*
* Use default platform configuration as limits for madvising,
diff --git a/core/res/res/xml/irq_device_map.xml b/core/res/res/xml/irq_device_map.xml
index 8b3667e..2f894b9 100644
--- a/core/res/res/xml/irq_device_map.xml
+++ b/core/res/res/xml/irq_device_map.xml
@@ -28,6 +28,8 @@
- Wifi: Use this to denote network traffic that uses the wifi transport.
- Sound_trigger: Use this to denote sound phrase detection, like the ones supported by
SoundTriggerManager.
+ - Sensor: Use this to denote wakeups due to sensor events.
+ - Cellular_data: Use this to denote network traffic on the cellular transport.
The overlay should use tags <device> and <subsystem> to describe this mapping in the
following way:
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
index c6bb07b..6d99e94 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
@@ -45,7 +45,6 @@
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.junit.After;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
@@ -67,22 +66,9 @@
@Rule
public TestRule compatChangeRule = new PlatformCompatChangeRule();
- private float mOriginalFontScale = Float.MIN_VALUE;
-
- @Before
- public void setup() {
- mOriginalFontScale = Settings.System.getFloat(
- InstrumentationRegistry.getInstrumentation().getContext().getContentResolver(),
- Settings.System.FONT_SCALE,
- Float.MIN_VALUE
- );
- }
-
@After
public void teardown() {
- if (mOriginalFontScale != Float.MIN_VALUE) {
- setSystemFontScale(mOriginalFontScale);
- }
+ restoreSystemFontScaleToDefault();
}
@IwTest(focusArea = "accessibility")
@@ -160,6 +146,28 @@
);
}
+ private static void restoreSystemFontScaleToDefault() {
+ ShellIdentityUtils.invokeWithShellPermissions(() -> {
+ // TODO(b/279083734): would use Settings.System.resetToDefaults() if it existed
+ Settings.System.putString(
+ InstrumentationRegistry.getInstrumentation()
+ .getContext()
+ .getContentResolver(),
+ Settings.System.FONT_SCALE,
+ null,
+ /* overrideableByRestore= */ true);
+ });
+
+ PollingCheck.waitFor(
+ /* timeout= */ 5000,
+ () -> InstrumentationRegistry.getInstrumentation()
+ .getContext()
+ .getResources()
+ .getConfiguration()
+ .fontScale == 1
+ );
+ }
+
private Matcher<View> withTextSizeInRange(float sizeStartPx, float sizeEndPx) {
return new BoundedMatcher<>(TextView.class) {
private static final float TOLERANCE = 0.05f;
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 2a0e476..4b4e722 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1675,6 +1675,12 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/am\/ActivityManagerService.java"
},
+ "-584061725": {
+ "message": "Content Recording: Accept session updating same display %d with granted consent, with an existing session %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/ContentRecordingController.java"
+ },
"-583031528": {
"message": "%s",
"level": "INFO",
@@ -2167,6 +2173,12 @@
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/TaskFragment.java"
},
+ "-125383273": {
+ "message": "Content Recording: waiting to record, so do nothing",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_CONTENT_RECORDING",
+ "at": "com\/android\/server\/wm\/ContentRecorder.java"
+ },
"-124316973": {
"message": "Translucent=%s Floating=%s ShowWallpaper=%s Disable=%s",
"level": "VERBOSE",
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml
deleted file mode 100644
index d8f3561..0000000
--- a/libs/WindowManager/Shell/res/drawable/pip_ic_move_down.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:fillColor="@color/tv_pip_menu_focus_border"
- android:pathData="M7,10l5,5 5,-5H7z"/>
-</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml
deleted file mode 100644
index 3e0011c..0000000
--- a/libs/WindowManager/Shell/res/drawable/pip_ic_move_left.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:fillColor="@color/tv_pip_menu_focus_border"
- android:pathData="M14,7l-5,5 5,5V7z"/>
-</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml
deleted file mode 100644
index f6b3c72..0000000
--- a/libs/WindowManager/Shell/res/drawable/pip_ic_move_right.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:fillColor="@color/tv_pip_menu_focus_border"
- android:pathData="M10,17l5,-5 -5,-5v10z"/>
-</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml b/libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml
deleted file mode 100644
index 1a34462..0000000
--- a/libs/WindowManager/Shell/res/drawable/pip_ic_move_up.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:fillColor="@color/tv_pip_menu_focus_border"
- android:pathData="M7,14l5,-5 5,5H7z"/>
-</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
index ab64f9e..82a358c 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
@@ -104,9 +104,7 @@
android:layout_centerHorizontal="true"
android:layout_alignParentTop="true"
android:alpha="0"
- android:contentDescription="@string/a11y_action_pip_move_up"
- android:elevation="@dimen/pip_menu_arrow_elevation"
- android:src="@drawable/pip_ic_move_up" />
+ android:contentDescription="@string/a11y_action_pip_move_up"/>
<ImageView
android:id="@+id/tv_pip_menu_arrow_right"
@@ -115,9 +113,7 @@
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:alpha="0"
- android:contentDescription="@string/a11y_action_pip_move_right"
- android:elevation="@dimen/pip_menu_arrow_elevation"
- android:src="@drawable/pip_ic_move_right" />
+ android:contentDescription="@string/a11y_action_pip_move_right"/>
<ImageView
android:id="@+id/tv_pip_menu_arrow_down"
@@ -126,9 +122,7 @@
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:alpha="0"
- android:contentDescription="@string/a11y_action_pip_move_down"
- android:elevation="@dimen/pip_menu_arrow_elevation"
- android:src="@drawable/pip_ic_move_down" />
+ android:contentDescription="@string/a11y_action_pip_move_down"/>
<ImageView
android:id="@+id/tv_pip_menu_arrow_left"
@@ -137,7 +131,5 @@
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:alpha="0"
- android:contentDescription="@string/a11y_action_pip_move_left"
- android:elevation="@dimen/pip_menu_arrow_elevation"
- android:src="@drawable/pip_ic_move_left" />
+ android:contentDescription="@string/a11y_action_pip_move_left"/>
</RelativeLayout>
diff --git a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
index adbf656..fd82563 100644
--- a/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
+++ b/libs/WindowManager/Shell/res/values-tvdpi/dimen.xml
@@ -33,8 +33,8 @@
<!-- outer space minus border width -->
<dimen name="pip_menu_outer_space_frame">20dp</dimen>
- <dimen name="pip_menu_arrow_size">24dp</dimen>
- <dimen name="pip_menu_arrow_elevation">5dp</dimen>
+ <dimen name="pip_menu_arrow_size">12dp</dimen>
+ <dimen name="pip_menu_arrow_elevation">1dp</dimen>
<dimen name="pip_menu_elevation_no_menu">1dp</dimen>
<dimen name="pip_menu_elevation_move_menu">7dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/colors_tv.xml b/libs/WindowManager/Shell/res/values/colors_tv.xml
index e6933ca..5f7fb12 100644
--- a/libs/WindowManager/Shell/res/values/colors_tv.xml
+++ b/libs/WindowManager/Shell/res/values/colors_tv.xml
@@ -27,6 +27,10 @@
<color name="tv_pip_menu_focus_border">#E8EAED</color>
<color name="tv_pip_menu_dim_layer">#990E0E0F</color>
<color name="tv_pip_menu_background">#1E232C</color>
+ <!-- Normally, the arrow color would be the same as the focus border color. But due to
+ optical illusion that looks too dark on the screen. That's why we define a separate
+ (lighter) arrow color. -->
+ <color name="tv_pip_menu_arrow_color">#F1F3F4</color>
<color name="tv_pip_edu_text">#99D2E3FC</color>
<color name="tv_pip_edu_text_home_icon">#D2E3FC</color>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
index 53a438e..c980906 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
@@ -54,10 +54,42 @@
void setTriggerBack(boolean triggerBack);
/**
- * Sets the threshold values that defining edge swipe behavior.
- * @param progressThreshold the max threshold to keep linear progressing back animation.
+ * Sets the threshold values that define edge swipe behavior.<br>
+ * <br>
+ * <h1>How does {@code nonLinearFactor} work?</h1>
+ * <pre>
+ * screen screen screen
+ * width width width
+ * |——————| |————————————| |————————————————————|
+ * A B A B C A
+ * 1 +——————+—————+ 1 +————————————+ 1 +————————————+———————+
+ * | / | | —/| | | —————/|
+ * | / | | —/ | | ——/ |
+ * | / | | —/ | | ——/ | |
+ * | / | | —/ | | ——/ | |
+ * | / | | —/ | | ——/ | |
+ * |/ | |—/ | |—/ | |
+ * 0 +————————————+ 0 +————————————+ 0 +————————————+———————+
+ * B B B
+ * </pre>
+ * Three devices with different widths (smaller, equal, and wider) relative to the progress
+ * threshold are shown in the graphs.<br>
+ * - A is the width of the screen<br>
+ * - B is the progress threshold (horizontal swipe distance where progress is linear)<br>
+ * - C equals B + (A - B) * nonLinearFactor<br>
+ * <br>
+ * If A is less than or equal to B, {@code progress} for the swipe distance between:<br>
+ * - [0, A] will scale linearly between [0, 1].<br>
+ * If A is greater than B, {@code progress} for swipe distance between:<br>
+ * - [0, B] will scale linearly between [0, B / C]<br>
+ * - (B, A] will scale non-linearly and reach 1.
+ *
+ * @param linearDistance up to this distance progress continues linearly. B in the graph above.
+ * @param maxDistance distance at which the progress will be 1f. A in the graph above.
+ * @param nonLinearFactor This value is used to calculate the target if the screen is wider
+ * than the progress threshold.
*/
- void setSwipeThresholds(float progressThreshold);
+ void setSwipeThresholds(float linearDistance, float maxDistance, float nonLinearFactor);
/**
* Sets the system bar listener to control the system bar color.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 6d879b8..bb543f2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -301,9 +301,12 @@
}
@Override
- public void setSwipeThresholds(float progressThreshold) {
+ public void setSwipeThresholds(
+ float linearDistance,
+ float maxDistance,
+ float nonLinearFactor) {
mShellExecutor.execute(() -> BackAnimationController.this.setSwipeThresholds(
- progressThreshold));
+ linearDistance, maxDistance, nonLinearFactor));
}
@Override
@@ -509,7 +512,7 @@
// Constraints - absolute values
float minVelocity = mFlingAnimationUtils.getMinVelocityPxPerSecond();
float maxVelocity = mFlingAnimationUtils.getHighVelocityPxPerSecond();
- float maxX = mTouchTracker.getMaxX(); // px
+ float maxX = mTouchTracker.getMaxDistance(); // px
float maxFlingDistance = maxX * MAX_FLING_PROGRESS; // px
// Current state
@@ -605,8 +608,11 @@
mTouchTracker.setTriggerBack(triggerBack);
}
- private void setSwipeThresholds(float progressThreshold) {
- mTouchTracker.setProgressThreshold(progressThreshold);
+ private void setSwipeThresholds(
+ float linearDistance,
+ float maxDistance,
+ float nonLinearFactor) {
+ mTouchTracker.setProgressThresholds(linearDistance, maxDistance, nonLinearFactor);
}
private void invokeOrCancelBack() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
index 7a00f5b..a0ada39 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/TouchTracker.java
@@ -28,11 +28,13 @@
* Helper class to record the touch location for gesture and generate back events.
*/
class TouchTracker {
- private static final String PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP =
- "persist.wm.debug.predictive_back_progress_threshold";
- private static final int PROGRESS_THRESHOLD = SystemProperties
- .getInt(PREDICTIVE_BACK_PROGRESS_THRESHOLD_PROP, -1);
- private float mProgressThreshold;
+ private static final String PREDICTIVE_BACK_LINEAR_DISTANCE_PROP =
+ "persist.wm.debug.predictive_back_linear_distance";
+ private static final int LINEAR_DISTANCE = SystemProperties
+ .getInt(PREDICTIVE_BACK_LINEAR_DISTANCE_PROP, -1);
+ private float mLinearDistance = LINEAR_DISTANCE;
+ private float mMaxDistance;
+ private float mNonLinearFactor;
/**
* Location of the latest touch event
*/
@@ -125,17 +127,42 @@
// the location everytime back is restarted after being cancelled.
float startX = mTriggerBack ? mInitTouchX : mStartThresholdX;
float deltaX = Math.abs(startX - touchX);
- float maxX = getMaxX();
- maxX = maxX == 0 ? 1 : maxX;
- return MathUtils.constrain(deltaX / maxX, 0, 1);
+ float linearDistance = mLinearDistance;
+ float maxDistance = getMaxDistance();
+ maxDistance = maxDistance == 0 ? 1 : maxDistance;
+ float progress;
+ if (linearDistance < maxDistance) {
+ // Up to linearDistance it behaves linearly, then slowly reaches 1f.
+
+ // maxDistance is composed of linearDistance + nonLinearDistance
+ float nonLinearDistance = maxDistance - linearDistance;
+ float initialTarget = linearDistance + nonLinearDistance * mNonLinearFactor;
+
+ boolean isLinear = deltaX <= linearDistance;
+ if (isLinear) {
+ progress = deltaX / initialTarget;
+ } else {
+ float nonLinearDeltaX = deltaX - linearDistance;
+ float nonLinearProgress = nonLinearDeltaX / nonLinearDistance;
+ float currentTarget = MathUtils.lerp(
+ /* start = */ initialTarget,
+ /* stop = */ maxDistance,
+ /* amount = */ nonLinearProgress);
+ progress = deltaX / currentTarget;
+ }
+ } else {
+ // Always linear behavior.
+ progress = deltaX / maxDistance;
+ }
+ return MathUtils.constrain(progress, 0, 1);
}
/**
- * Maximum X value (in pixels).
+ * Maximum distance in pixels.
* Progress is considered to be completed (1f) when this limit is exceeded.
*/
- float getMaxX() {
- return PROGRESS_THRESHOLD >= 0 ? PROGRESS_THRESHOLD : mProgressThreshold;
+ float getMaxDistance() {
+ return mMaxDistance;
}
BackMotionEvent createProgressEvent(float progress) {
@@ -149,7 +176,14 @@
/* departingAnimationTarget = */ null);
}
- public void setProgressThreshold(float progressThreshold) {
- mProgressThreshold = progressThreshold;
+ public void setProgressThresholds(float linearDistance, float maxDistance,
+ float nonLinearFactor) {
+ if (LINEAR_DISTANCE >= 0) {
+ mLinearDistance = LINEAR_DISTANCE;
+ } else {
+ mLinearDistance = linearDistance;
+ }
+ mMaxDistance = maxDistance;
+ mNonLinearFactor = nonLinearFactor;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 9677728..fa21db5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -1015,6 +1015,16 @@
mPipOrganizer.onExitPipFinished(prevPipTaskChange.getTaskInfo());
}
+ @Override
+ public boolean syncPipSurfaceState(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction) {
+ final TransitionInfo.Change pipChange = findCurrentPipTaskChange(info);
+ if (pipChange == null) return false;
+ updatePipForUnhandledTransition(pipChange, startTransaction, finishTransaction);
+ return true;
+ }
+
private void updatePipForUnhandledTransition(@NonNull TransitionInfo.Change pipChange,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction) {
@@ -1025,10 +1035,12 @@
final boolean isInPip = mPipTransitionState.isInPip();
mSurfaceTransactionHelper
.crop(startTransaction, leash, destBounds)
- .round(startTransaction, leash, isInPip);
+ .round(startTransaction, leash, isInPip)
+ .shadow(startTransaction, leash, isInPip);
mSurfaceTransactionHelper
.crop(finishTransaction, leash, destBounds)
- .round(finishTransaction, leash, isInPip);
+ .round(finishTransaction, leash, isInPip)
+ .shadow(finishTransaction, leash, isInPip);
}
/** Hides and shows the existing PIP during fixed rotation transition of other activities. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 949d6f5..2fff0e4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -240,6 +240,18 @@
@NonNull final Transitions.TransitionFinishCallback finishCallback) {
}
+ /**
+ * Applies the proper surface states (rounded corners/shadows) to pip surfaces in `info`.
+ * This is intended to be used when PiP is part of another animation but isn't, itself,
+ * animating (eg. unlocking).
+ * @return `true` if there was a pip in `info`.
+ */
+ public boolean syncPipSurfaceState(@NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction) {
+ return false;
+ }
+
/** End the currently-playing PiP animation. */
public void end() {
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
index d076418..613791c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -31,12 +31,19 @@
import static com.android.wm.shell.pip.tv.TvPipMenuController.MODE_NO_MENU;
import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Outline;
+import android.graphics.Path;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ShapeDrawable;
+import android.graphics.drawable.shapes.PathShape;
import android.os.Handler;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -91,6 +98,8 @@
private final ImageView mArrowLeft;
private final TvWindowMenuActionButton mA11yDoneButton;
+ private final int mArrowElevation;
+
private @TvPipMenuController.TvPipMenuMode int mCurrentMenuMode = MODE_NO_MENU;
private final Rect mCurrentPipBounds = new Rect();
private int mCurrentPipGravity;
@@ -131,21 +140,70 @@
mArrowLeft = findViewById(R.id.tv_pip_menu_arrow_left);
mA11yDoneButton = findViewById(R.id.tv_pip_menu_done_button);
- mResizeAnimationDuration = context.getResources().getInteger(
- R.integer.config_pipResizeAnimationDuration);
- mPipMenuFadeAnimationDuration = context.getResources()
- .getInteger(R.integer.tv_window_menu_fade_animation_duration);
+ final Resources res = context.getResources();
+ mResizeAnimationDuration = res.getInteger(R.integer.config_pipResizeAnimationDuration);
+ mPipMenuFadeAnimationDuration =
+ res.getInteger(R.integer.tv_window_menu_fade_animation_duration);
+ mPipMenuOuterSpace = res.getDimensionPixelSize(R.dimen.pip_menu_outer_space);
+ mPipMenuBorderWidth = res.getDimensionPixelSize(R.dimen.pip_menu_border_width);
+ mArrowElevation = res.getDimensionPixelSize(R.dimen.pip_menu_arrow_elevation);
- mPipMenuOuterSpace = context.getResources()
- .getDimensionPixelSize(R.dimen.pip_menu_outer_space);
- mPipMenuBorderWidth = context.getResources()
- .getDimensionPixelSize(R.dimen.pip_menu_border_width);
+ initMoveArrows();
mEduTextDrawer = new TvPipMenuEduTextDrawer(mContext, mainHandler, this);
mEduTextContainer = (ViewGroup) findViewById(R.id.tv_pip_menu_edu_text_container);
mEduTextContainer.addView(mEduTextDrawer);
}
+ private void initMoveArrows() {
+ final int arrowSize =
+ mContext.getResources().getDimensionPixelSize(R.dimen.pip_menu_arrow_size);
+ final Path arrowPath = createArrowPath(arrowSize);
+
+ final ShapeDrawable arrowDrawable = new ShapeDrawable();
+ arrowDrawable.setShape(new PathShape(arrowPath, arrowSize, arrowSize));
+ arrowDrawable.setTint(mContext.getResources().getColor(R.color.tv_pip_menu_arrow_color));
+
+ final ViewOutlineProvider arrowOutlineProvider = new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setPath(createArrowPath(view.getMeasuredHeight()));
+ }
+ };
+
+ initArrow(mArrowRight, arrowOutlineProvider, arrowDrawable, 0);
+ initArrow(mArrowDown, arrowOutlineProvider, arrowDrawable, 90);
+ initArrow(mArrowLeft, arrowOutlineProvider, arrowDrawable, 180);
+ initArrow(mArrowUp, arrowOutlineProvider, arrowDrawable, 270);
+ }
+
+ /**
+ * Creates a Path for a movement arrow in the MODE_MOVE_MENU. The resulting Path is a simple
+ * right-pointing triangle with its tip in the center of a size x size square:
+ * _ _ _ _ _
+ * |* |
+ * |* * |
+ * |* * |
+ * |* * |
+ * |* _ _ _ _|
+ *
+ */
+ private Path createArrowPath(int size) {
+ final Path triangle = new Path();
+ triangle.lineTo(0, size);
+ triangle.lineTo(size / 2, size / 2);
+ triangle.close();
+ return triangle;
+ }
+
+ private void initArrow(View v, ViewOutlineProvider arrowOutlineProvider, Drawable arrowDrawable,
+ int rotation) {
+ v.setOutlineProvider(arrowOutlineProvider);
+ v.setBackground(arrowDrawable);
+ v.setRotation(rotation);
+ v.setElevation(mArrowElevation);
+ }
+
void onPipTransitionToTargetBoundsStarted(Rect targetBounds) {
if (targetBounds == null) {
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 087e3a2..a95d038 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -582,7 +582,6 @@
return;
}
- prepareEvictChildTasksIfSplitActive(wct);
setSideStagePosition(splitPosition, wct);
options1 = options1 != null ? options1 : new Bundle();
addActivityOptions(options1, mSideStage);
@@ -604,7 +603,6 @@
return;
}
- prepareEvictChildTasksIfSplitActive(wct);
setSideStagePosition(splitPosition, wct);
options1 = options1 != null ? options1 : new Bundle();
addActivityOptions(options1, mSideStage);
@@ -625,7 +623,6 @@
return;
}
- prepareEvictChildTasksIfSplitActive(wct);
setSideStagePosition(splitPosition, wct);
options1 = options1 != null ? options1 : new Bundle();
addActivityOptions(options1, mSideStage);
@@ -691,7 +688,6 @@
mMainStage.activate(wct, false /* reparent */);
}
- prepareEvictChildTasksIfSplitActive(wct);
mSplitLayout.setDivideRatio(splitRatio);
updateWindowBounds(mSplitLayout, wct);
wct.reorder(mRootTaskInfo.token, true);
@@ -1075,24 +1071,11 @@
}
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
- prepareEvictNonOpeningChildTasks(SPLIT_POSITION_TOP_OR_LEFT, apps, evictWct);
- prepareEvictNonOpeningChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, apps, evictWct);
+ mMainStage.evictNonOpeningChildren(apps, evictWct);
+ mSideStage.evictNonOpeningChildren(apps, evictWct);
mSyncQueue.queue(evictWct);
}
-
- /**
- * Collects all the current child tasks of a specific split and prepares transaction to evict
- * them to display.
- */
- void prepareEvictChildTasks(@SplitPosition int position, WindowContainerTransaction wct) {
- if (position == mSideStagePosition) {
- mSideStage.evictAllChildren(wct);
- } else {
- mMainStage.evictAllChildren(wct);
- }
- }
-
void prepareEvictNonOpeningChildTasks(@SplitPosition int position, RemoteAnimationTarget[] apps,
WindowContainerTransaction wct) {
if (position == mSideStagePosition) {
@@ -1107,13 +1090,6 @@
mSideStage.evictInvisibleChildren(wct);
}
- void prepareEvictChildTasksIfSplitActive(WindowContainerTransaction wct) {
- if (mMainStage.isActive()) {
- mMainStage.evictAllChildren(wct);
- mSideStage.evictAllChildren(wct);
- }
- }
-
Bundle resolveStartStage(@StageType int stage, @SplitPosition int position,
@Nullable Bundle options, @Nullable WindowContainerTransaction wct) {
switch (stage) {
@@ -1500,10 +1476,8 @@
wct.startTask(taskInfo.taskId,
resolveStartStage(STAGE_TYPE_UNDEFINED, startPosition, null, wct));
}
- // If running background, we need to reparent current top visible task to another stage
- // and evict all tasks current under its.
+ // If running background, we need to reparent current top visible task to main stage.
if (!isSplitScreenVisible()) {
- // Recreate so we need to reset position rather than keep position of background split.
mMainStage.reparentTopTask(wct);
prepareSplitLayout(wct);
}
@@ -2337,7 +2311,7 @@
prepareEnterSplitScreen(out);
mSplitTransitions.setEnterTransition(transition, request.getRemoteTransition(),
null /* consumedCallback */, null /* finishedCallback */,
- 0 /* extraTransitType */, !mIsDropEntering);
+ TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, !mIsDropEntering);
}
}
return out;
@@ -2604,7 +2578,8 @@
== TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE) {
if (mainChild == null && sideChild == null) {
Log.w(TAG, "Launched a task in split, but didn't receive any task in transition.");
- mSplitTransitions.mPendingEnter.cancel(null /* finishedCb */);
+ mSplitTransitions.mPendingEnter.cancel((cancelWct, cancelT)
+ -> prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, cancelWct));
return true;
}
} else {
@@ -2614,10 +2589,7 @@
final int dismissTop = mainChild != null ? STAGE_TYPE_MAIN :
(sideChild != null ? STAGE_TYPE_SIDE : STAGE_TYPE_UNDEFINED);
mSplitTransitions.mPendingEnter.cancel(
- (cancelWct, cancelT) -> {
- mSideStage.removeAllTasks(cancelWct, dismissTop == STAGE_TYPE_SIDE);
- mMainStage.deactivate(cancelWct, dismissTop == STAGE_TYPE_MAIN);
- });
+ (cancelWct, cancelT) -> prepareExitSplitScreen(dismissTop, cancelWct));
return true;
}
}
@@ -2626,14 +2598,19 @@
// transitions locally, but remotes (like Launcher) may get confused if they were
// depending on listener callbacks. This can happen because task-organizer callbacks
// aren't serialized with transition callbacks.
+ // This usually occurred on app use trampoline launch new task and finish itself.
// TODO(b/184679596): Find a way to either include task-org information in
// the transition, or synchronize task-org callbacks.
- if (mainChild != null && !mMainStage.containsTask(mainChild.getTaskInfo().taskId)) {
+ final boolean mainNotContainOpenTask =
+ mainChild != null && !mMainStage.containsTask(mainChild.getTaskInfo().taskId);
+ final boolean sideNotContainOpenTask =
+ sideChild != null && !mSideStage.containsTask(sideChild.getTaskInfo().taskId);
+ if (mainNotContainOpenTask) {
Log.w(TAG, "Expected onTaskAppeared on " + mMainStage
+ " to have been called with " + mainChild.getTaskInfo().taskId
+ " before startAnimation().");
}
- if (sideChild != null && !mSideStage.containsTask(sideChild.getTaskInfo().taskId)) {
+ if (sideNotContainOpenTask) {
Log.w(TAG, "Expected onTaskAppeared on " + mSideStage
+ " to have been called with " + sideChild.getTaskInfo().taskId
+ " before startAnimation().");
@@ -2642,10 +2619,18 @@
final TransitionInfo.Change finalSideChild = sideChild;
enterTransition.setFinishedCallback((callbackWct, callbackT) -> {
if (finalMainChild != null) {
- mMainStage.evictOtherChildren(callbackWct, finalMainChild.getTaskInfo().taskId);
+ if (!mainNotContainOpenTask) {
+ mMainStage.evictOtherChildren(callbackWct, finalMainChild.getTaskInfo().taskId);
+ } else {
+ mMainStage.evictInvisibleChildren(callbackWct);
+ }
}
if (finalSideChild != null) {
- mSideStage.evictOtherChildren(callbackWct, finalSideChild.getTaskInfo().taskId);
+ if (!sideNotContainOpenTask) {
+ mSideStage.evictOtherChildren(callbackWct, finalSideChild.getTaskInfo().taskId);
+ } else {
+ mSideStage.evictInvisibleChildren(callbackWct);
+ }
}
if (enterTransition.mResizeAnim) {
mShowDecorImmediately = true;
@@ -2904,9 +2889,10 @@
if (!isSplitScreenVisible()) {
mIsDropEntering = true;
}
- if (!isSplitScreenVisible()) {
+ if (!isSplitScreenVisible() && !ENABLE_SHELL_TRANSITIONS) {
// If split running background, exit split first.
- // TODO(b/280392203) : skip doing this on shell transition once this bug is fixed.
+ // Skip this on shell transition due to we could evict existing tasks on transition
+ // finished.
exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RECREATE_SPLIT);
}
mLogger.enterRequestedByDrag(position, dragSessionId);
@@ -2916,9 +2902,10 @@
* Sets info to be logged when splitscreen is next entered.
*/
public void onRequestToSplit(InstanceId sessionId, int enterReason) {
- if (!isSplitScreenVisible()) {
+ if (!isSplitScreenVisible() && !ENABLE_SHELL_TRANSITIONS) {
// If split running background, exit split first.
- // TODO(b/280392203) : skip doing this on shell transition once this bug is fixed.
+ // Skip this on shell transition due to we could evict existing tasks on transition
+ // finished.
exitSplitScreen(null /* childrenToTop */, EXIT_REASON_RECREATE_SPLIT);
}
mLogger.enterRequested(sessionId, enterReason);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index 42633b7..863b5ab 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -304,8 +304,8 @@
return animateRecentsDuringSplit(mixed, info, startTransaction, finishTransaction,
finishCallback);
} else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) {
- return mKeyguardHandler.startAnimation(
- transition, info, startTransaction, finishTransaction, finishCallback);
+ return animateKeyguard(mixed, info, startTransaction, finishTransaction,
+ finishCallback);
} else {
mActiveTransitions.remove(mixed);
throw new IllegalStateException("Starting mixed animation without a known mixed type? "
@@ -557,6 +557,27 @@
return handled;
}
+ private boolean animateKeyguard(@NonNull final MixedTransition mixed,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ boolean consumed = mKeyguardHandler.startAnimation(
+ mixed.mTransition, info, startTransaction, finishTransaction, finishCallback);
+ if (!consumed) {
+ return false;
+ }
+ // Sync pip state.
+ if (mPipHandler != null) {
+ // We don't know when to apply `startTransaction` so use a separate transaction here.
+ // This should be fine because these surface properties are independent.
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ mPipHandler.syncPipSurfaceState(info, t, finishTransaction);
+ t.apply();
+ }
+ return true;
+ }
+
@Override
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java
deleted file mode 100644
index d62e660..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.back;
-
-import static org.junit.Assert.assertEquals;
-
-import android.window.BackEvent;
-import android.window.BackMotionEvent;
-
-import org.junit.Before;
-import org.junit.Test;
-
-public class TouchTrackerTest {
- private static final float FAKE_THRESHOLD = 400;
- private static final float INITIAL_X_LEFT_EDGE = 5;
- private static final float INITIAL_X_RIGHT_EDGE = FAKE_THRESHOLD - INITIAL_X_LEFT_EDGE;
- private TouchTracker mTouchTracker;
-
- @Before
- public void setUp() throws Exception {
- mTouchTracker = new TouchTracker();
- mTouchTracker.setProgressThreshold(FAKE_THRESHOLD);
- }
-
- @Test
- public void generatesProgress_onStart() {
- mTouchTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0, BackEvent.EDGE_LEFT);
- BackMotionEvent event = mTouchTracker.createStartEvent(null);
- assertEquals(event.getProgress(), 0f, 0f);
- }
-
- @Test
- public void generatesProgress_leftEdge() {
- mTouchTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0, BackEvent.EDGE_LEFT);
- float touchX = 10;
- float velocityX = 0;
- float velocityY = 0;
-
- // Pre-commit
- mTouchTracker.update(touchX, 0, velocityX, velocityY);
- assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f);
-
- // Post-commit
- touchX += 100;
- mTouchTracker.setTriggerBack(true);
- mTouchTracker.update(touchX, 0, velocityX, velocityY);
- assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f);
-
- // Cancel
- touchX -= 10;
- mTouchTracker.setTriggerBack(false);
- mTouchTracker.update(touchX, 0, velocityX, velocityY);
- assertEquals(getProgress(), 0, 0f);
-
- // Cancel more
- touchX -= 10;
- mTouchTracker.update(touchX, 0, velocityX, velocityY);
- assertEquals(getProgress(), 0, 0f);
-
- // Restart
- touchX += 10;
- mTouchTracker.update(touchX, 0, velocityX, velocityY);
- assertEquals(getProgress(), 0, 0f);
-
- // Restarted, but pre-commit
- float restartX = touchX;
- touchX += 10;
- mTouchTracker.update(touchX, 0, velocityX, velocityY);
- assertEquals(getProgress(), (touchX - restartX) / FAKE_THRESHOLD, 0f);
-
- // Restarted, post-commit
- touchX += 10;
- mTouchTracker.setTriggerBack(true);
- mTouchTracker.update(touchX, 0, velocityX, velocityY);
- assertEquals(getProgress(), (touchX - INITIAL_X_LEFT_EDGE) / FAKE_THRESHOLD, 0f);
- }
-
- @Test
- public void generatesProgress_rightEdge() {
- mTouchTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0, BackEvent.EDGE_RIGHT);
- float touchX = INITIAL_X_RIGHT_EDGE - 10; // Fake right edge
- float velocityX = 0f;
- float velocityY = 0f;
-
- // Pre-commit
- mTouchTracker.update(touchX, 0, velocityX, velocityY);
- assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f);
-
- // Post-commit
- touchX -= 100;
- mTouchTracker.setTriggerBack(true);
- mTouchTracker.update(touchX, 0, velocityX, velocityY);
- assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f);
-
- // Cancel
- touchX += 10;
- mTouchTracker.setTriggerBack(false);
- mTouchTracker.update(touchX, 0, velocityX, velocityY);
- assertEquals(getProgress(), 0, 0f);
-
- // Cancel more
- touchX += 10;
- mTouchTracker.update(touchX, 0, velocityX, velocityY);
- assertEquals(getProgress(), 0, 0f);
-
- // Restart
- touchX -= 10;
- mTouchTracker.update(touchX, 0, velocityX, velocityY);
- assertEquals(getProgress(), 0, 0f);
-
- // Restarted, but pre-commit
- float restartX = touchX;
- touchX -= 10;
- mTouchTracker.update(touchX, 0, velocityX, velocityY);
- assertEquals(getProgress(), (restartX - touchX) / FAKE_THRESHOLD, 0f);
-
- // Restarted, post-commit
- touchX -= 10;
- mTouchTracker.setTriggerBack(true);
- mTouchTracker.update(touchX, 0, velocityX, velocityY);
- assertEquals(getProgress(), (INITIAL_X_RIGHT_EDGE - touchX) / FAKE_THRESHOLD, 0f);
- }
-
- private float getProgress() {
- return mTouchTracker.createProgressEvent().getProgress();
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
new file mode 100644
index 0000000..9088e89
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/TouchTrackerTest.kt
@@ -0,0 +1,181 @@
+/*
+ * 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.wm.shell.back
+
+import android.util.MathUtils
+import android.window.BackEvent
+import org.junit.Assert.assertEquals
+import org.junit.Test
+
+class TouchTrackerTest {
+ private fun linearTouchTracker(): TouchTracker = TouchTracker().apply {
+ setProgressThresholds(MAX_DISTANCE, MAX_DISTANCE, NON_LINEAR_FACTOR)
+ }
+
+ private fun nonLinearTouchTracker(): TouchTracker = TouchTracker().apply {
+ setProgressThresholds(LINEAR_DISTANCE, MAX_DISTANCE, NON_LINEAR_FACTOR)
+ }
+
+ private fun TouchTracker.assertProgress(expected: Float) {
+ val actualProgress = createProgressEvent().progress
+ assertEquals(expected, actualProgress, /* delta = */ 0f)
+ }
+
+ @Test
+ fun generatesProgress_onStart() {
+ val linearTracker = linearTouchTracker()
+ linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
+ val event = linearTracker.createStartEvent(null)
+ assertEquals(0f, event.progress, 0f)
+ }
+
+ @Test
+ fun generatesProgress_leftEdge() {
+ val linearTracker = linearTouchTracker()
+ linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
+ var touchX = 10f
+ val velocityX = 0f
+ val velocityY = 0f
+
+ // Pre-commit
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE)
+
+ // Post-commit
+ touchX += 100f
+ linearTracker.setTriggerBack(true)
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE)
+
+ // Cancel
+ touchX -= 10f
+ linearTracker.setTriggerBack(false)
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress(0f)
+
+ // Cancel more
+ touchX -= 10f
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress(0f)
+
+ // Restart
+ touchX += 10f
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress(0f)
+
+ // Restarted, but pre-commit
+ val restartX = touchX
+ touchX += 10f
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress((touchX - restartX) / MAX_DISTANCE)
+
+ // Restarted, post-commit
+ touchX += 10f
+ linearTracker.setTriggerBack(true)
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / MAX_DISTANCE)
+ }
+
+ @Test
+ fun generatesProgress_rightEdge() {
+ val linearTracker = linearTouchTracker()
+ linearTracker.setGestureStartLocation(INITIAL_X_RIGHT_EDGE, 0f, BackEvent.EDGE_RIGHT)
+ var touchX = INITIAL_X_RIGHT_EDGE - 10 // Fake right edge
+ val velocityX = 0f
+ val velocityY = 0f
+ val target = MAX_DISTANCE
+
+ // Pre-commit
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target)
+
+ // Post-commit
+ touchX -= 100f
+ linearTracker.setTriggerBack(true)
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target)
+
+ // Cancel
+ touchX += 10f
+ linearTracker.setTriggerBack(false)
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress(0f)
+
+ // Cancel more
+ touchX += 10f
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress(0f)
+
+ // Restart
+ touchX -= 10f
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress(0f)
+
+ // Restarted, but pre-commit
+ val restartX = touchX
+ touchX -= 10f
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress((restartX - touchX) / target)
+
+ // Restarted, post-commit
+ touchX -= 10f
+ linearTracker.setTriggerBack(true)
+ linearTracker.update(touchX, 0f, velocityX, velocityY)
+ linearTracker.assertProgress((INITIAL_X_RIGHT_EDGE - touchX) / target)
+ }
+
+ @Test
+ fun generatesNonLinearProgress_leftEdge() {
+ val nonLinearTracker = nonLinearTouchTracker()
+ nonLinearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
+ var touchX = 10f
+ val velocityX = 0f
+ val velocityY = 0f
+ val linearTarget = LINEAR_DISTANCE + (MAX_DISTANCE - LINEAR_DISTANCE) * NON_LINEAR_FACTOR
+
+ // Pre-commit: linear progress
+ nonLinearTracker.update(touchX, 0f, velocityX, velocityY)
+ nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget)
+
+ // Post-commit: still linear progress
+ touchX += 100f
+ nonLinearTracker.setTriggerBack(true)
+ nonLinearTracker.update(touchX, 0f, velocityX, velocityY)
+ nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget)
+
+ // still linear progress
+ touchX = INITIAL_X_LEFT_EDGE + LINEAR_DISTANCE
+ nonLinearTracker.update(touchX, 0f, velocityX, velocityY)
+ nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / linearTarget)
+
+ // non linear progress
+ touchX += 10
+ nonLinearTracker.update(touchX, 0f, velocityX, velocityY)
+ val nonLinearTouch = (touchX - INITIAL_X_LEFT_EDGE) - LINEAR_DISTANCE
+ val nonLinearProgress = nonLinearTouch / NON_LINEAR_DISTANCE
+ val nonLinearTarget = MathUtils.lerp(linearTarget, MAX_DISTANCE, nonLinearProgress)
+ nonLinearTracker.assertProgress((touchX - INITIAL_X_LEFT_EDGE) / nonLinearTarget)
+ }
+
+ companion object {
+ private const val MAX_DISTANCE = 500f
+ private const val LINEAR_DISTANCE = 400f
+ private const val NON_LINEAR_DISTANCE = MAX_DISTANCE - LINEAR_DISTANCE
+ private const val NON_LINEAR_FACTOR = 0.2f
+ private const val INITIAL_X_LEFT_EDGE = 5f
+ private const val INITIAL_X_RIGHT_EDGE = MAX_DISTANCE - INITIAL_X_LEFT_EDGE
+ }
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 4d6dd4b..f5bacb6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -250,6 +250,7 @@
}
}
cachedDevice.onActiveDeviceChanged(isActive, bluetoothProfile);
+ mDeviceManager.onActiveDeviceChanged(cachedDevice);
}
for (BluetoothCallback callback : mCallbacks) {
callback.onActiveDeviceChanged(activeDevice, bluetoothProfile);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index 67e3e03..d55144e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -328,6 +328,13 @@
return false;
}
+ /** Handles when the device been set as active/inactive. */
+ public synchronized void onActiveDeviceChanged(CachedBluetoothDevice cachedBluetoothDevice) {
+ if (cachedBluetoothDevice.isHearingAidDevice()) {
+ mHearingAidDeviceManager.onActiveDeviceChanged(cachedBluetoothDevice);
+ }
+ }
+
public synchronized void onDeviceUnpaired(CachedBluetoothDevice device) {
device.setGroupId(BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
CachedBluetoothDevice mainDevice = mCsipDeviceManager.findMainDevice(device);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
index 4354e0c..e5e5782 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
@@ -224,15 +224,9 @@
// It is necessary to do remove and add for updating the mapping on
// preference and device
mBtManager.getEventManager().dispatchDeviceAdded(mainDevice);
- // Only need to set first device of a set. AudioDeviceInfo for
- // GET_DEVICES_OUTPUTS will not change device.
- setAudioRoutingConfig(cachedDevice);
}
return true;
}
- // Only need to set first device of a set. AudioDeviceInfo for GET_DEVICES_OUTPUTS
- // will not change device.
- setAudioRoutingConfig(cachedDevice);
break;
case BluetoothProfile.STATE_DISCONNECTED:
mainDevice = findMainDevice(cachedDevice);
@@ -258,13 +252,20 @@
return true;
}
- // Only need to clear when last device of a set get disconnected
- clearAudioRoutingConfig();
break;
}
return false;
}
+ void onActiveDeviceChanged(CachedBluetoothDevice device) {
+ if (device.isActiveDevice(BluetoothProfile.HEARING_AID) || device.isActiveDevice(
+ BluetoothProfile.LE_AUDIO)) {
+ setAudioRoutingConfig(device);
+ } else {
+ clearAudioRoutingConfig();
+ }
+ }
+
private void setAudioRoutingConfig(CachedBluetoothDevice device) {
AudioDeviceAttributes hearingDeviceAttributes =
mRoutingHelper.getMatchedHearingDeviceAttributes(device);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index 3361a66..8c316d1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -46,6 +46,7 @@
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@@ -396,6 +397,21 @@
}
@Test
+ public void dispatchActiveDeviceChanged_callExpectedOnActiveDeviceChanged() {
+ mBluetoothEventManager.registerCallback(mBluetoothCallback);
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+ Collections.singletonList(mCachedDevice1));
+
+ mBluetoothEventManager.dispatchActiveDeviceChanged(mCachedDevice1,
+ BluetoothProfile.HEARING_AID);
+
+ verify(mCachedDeviceManager).onActiveDeviceChanged(mCachedDevice1);
+ verify(mBluetoothCallback).onActiveDeviceChanged(mCachedDevice1,
+ BluetoothProfile.HEARING_AID);
+ }
+
+ @Test
public void showUnbondMessage_reasonAuthTimeout_showCorrectedErrorCode() {
mIntent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
index 4b3820e..7e7c76e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java
@@ -17,7 +17,9 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -604,4 +606,20 @@
verify(mDevice2).setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
verify(mDevice2).createBond(BluetoothDevice.TRANSPORT_LE);
}
+
+ @Test
+ public void onActiveDeviceChanged_validHiSyncId_callExpectedFunction() {
+ mHearingAidDeviceManager = spy(new HearingAidDeviceManager(mContext, mLocalBluetoothManager,
+ mCachedDeviceManager.mCachedDevices));
+ doNothing().when(mHearingAidDeviceManager).onActiveDeviceChanged(any());
+ mCachedDeviceManager.mHearingAidDeviceManager = mHearingAidDeviceManager;
+ when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
+ cachedDevice1.setHearingAidInfo(
+ new HearingAidInfo.Builder().setHiSyncId(HISYNCID1).build());
+
+ mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1);
+
+ verify(mHearingAidDeviceManager).onActiveDeviceChanged(cachedDevice1);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index a839136..0d5de88 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -478,37 +478,24 @@
}
@Test
- public void onProfileConnectionStateChanged_connected_callSetStrategies() {
+ public void onActiveDeviceChanged_connected_callSetStrategies() {
when(mHelper.getMatchedHearingDeviceAttributes(mCachedDevice1)).thenReturn(
mHearingDeviceAttribute);
+ when(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(true);
- mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice1,
- BluetoothProfile.STATE_CONNECTED);
+ mHearingAidDeviceManager.onActiveDeviceChanged(mCachedDevice1);
verify(mHelper, atLeastOnce()).setPreferredDeviceRoutingStrategies(
eq(List.of(mAudioStrategy)), any(AudioDeviceAttributes.class), anyInt());
}
@Test
- public void onProfileConnectionStateChanged_disconnected_callSetStrategiesWithAutoValue() {
+ public void onActiveDeviceChanged_disconnected_callSetStrategiesWithAutoValue() {
when(mHelper.getMatchedHearingDeviceAttributes(mCachedDevice1)).thenReturn(
mHearingDeviceAttribute);
+ when(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).thenReturn(false);
- mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice1,
- BluetoothProfile.STATE_DISCONNECTED);
-
- verify(mHelper, atLeastOnce()).setPreferredDeviceRoutingStrategies(
- eq(List.of(mAudioStrategy)), /* hearingDevice= */ isNull(),
- eq(HearingAidAudioRoutingConstants.RoutingValue.AUTO));
- }
- @Test
- public void onProfileConnectionStateChanged_unpairing_callSetStrategiesWithAutoValue() {
- when(mHelper.getMatchedHearingDeviceAttributes(mCachedDevice1)).thenReturn(
- mHearingDeviceAttribute);
-
- when(mCachedDevice1.getUnpairing()).thenReturn(true);
- mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed(mCachedDevice1,
- BluetoothProfile.STATE_DISCONNECTED);
+ mHearingAidDeviceManager.onActiveDeviceChanged(mCachedDevice1);
verify(mHelper, atLeastOnce()).setPreferredDeviceRoutingStrategies(
eq(List.of(mAudioStrategy)), /* hearingDevice= */ isNull(),
diff --git a/packages/SystemUI/docs/device-entry/keyguard.md b/packages/SystemUI/docs/device-entry/keyguard.md
index 8634c95..1898b97 100644
--- a/packages/SystemUI/docs/device-entry/keyguard.md
+++ b/packages/SystemUI/docs/device-entry/keyguard.md
@@ -20,6 +20,10 @@
An indication to power off the device most likely comes from one of two signals: the user presses the power button or the screen timeout has passed. This may [lock the device](#How-the-device-locks)
+#### Long-pressing on keyguard
+
+OEMs may choose to enable a long-press action that displays a button at the bottom of lockscreen. This button links to lockscreen customization. This can be achieved by overriding the `long_press_keyguard_customize_lockscreen_enabled` resource in `packages/SystemUI/res/values/config.xml`.
+
#### On Lockscreen
#### On Lockscreen, occluded by an activity
diff --git a/packages/SystemUI/docs/device-entry/quickaffordance.md b/packages/SystemUI/docs/device-entry/quickaffordance.md
index d662649..afcf846 100644
--- a/packages/SystemUI/docs/device-entry/quickaffordance.md
+++ b/packages/SystemUI/docs/device-entry/quickaffordance.md
@@ -17,7 +17,9 @@
By default, AOSP ships with a "bottom right" and a "bottom left" slot, each with a slot capacity of `1`, allowing only one Quick Affordance on each side of the lock screen.
### Customizing Slots
-OEMs may choose to override the IDs and number of slots and/or override the default capacities. This can be achieved by overridding the `config_keyguardQuickAffordanceSlots` resource in `packages/SystemUI/res/values/config.xml`.
+OEMs may choose to enable customization of slots. An entry point in settings will appear when overriding the `custom_lockscreen_shortcuts_enabled` resource in `packages/SystemUI/res/values/config.xml`.
+
+OEMs may also choose to override the IDs and number of slots and/or override the default capacities. This can be achieved by overridding the `config_keyguardQuickAffordanceSlots` resource in `packages/SystemUI/res/values/config.xml`.
### Default Quick Affordances
OEMs may also choose to predefine default Quick Affordances for each slot. To achieve this, a developer may override the `config_keyguardQuickAffordanceDefaults` resource in `packages/SystemUI/res/values/config.xml`. Note that defaults only work until the user of the device selects a different quick affordance for that slot, even if they select the "None" option.
diff --git a/packages/SystemUI/res/drawable/dream_overlay_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/dream_overlay_bottom_affordance_bg.xml
index 3b67ddd..bab604d 100644
--- a/packages/SystemUI/res/drawable/dream_overlay_bottom_affordance_bg.xml
+++ b/packages/SystemUI/res/drawable/dream_overlay_bottom_affordance_bg.xml
@@ -18,9 +18,7 @@
-->
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
- <solid android:color="?androidprv:attr/colorSurface"/>
<size
android:width="@dimen/dream_overlay_bottom_affordance_height"
android:height="@dimen/dream_overlay_bottom_affordance_width"/>
diff --git a/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml b/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
index 0cd0623..e8a48c7 100644
--- a/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_home_controls_chip.xml
@@ -21,8 +21,6 @@
android:layout_width="@dimen/dream_overlay_bottom_affordance_width"
android:layout_gravity="bottom|start"
android:padding="@dimen/dream_overlay_bottom_affordance_padding"
- android:background="@drawable/dream_overlay_bottom_affordance_bg"
android:scaleType="fitCenter"
android:tint="?android:attr/textColorPrimary"
- android:src="@drawable/controls_icon"
android:contentDescription="@string/quick_controls_title" />
diff --git a/packages/SystemUI/res/raw/sfps_pulse.json b/packages/SystemUI/res/raw/sfps_pulse.json
index 9fe4eb6..2a72dfb 100644
--- a/packages/SystemUI/res/raw/sfps_pulse.json
+++ b/packages/SystemUI/res/raw/sfps_pulse.json
@@ -1 +1 @@
-{"v":"5.7.13","fr":60,"ip":0,"op":300,"w":42,"h":80,"nm":"sfps_pulse_motion_04","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.617,"y":0.539},"o":{"x":0.477,"y":0},"t":0,"s":[28,40,0],"to":[0.365,0,0],"ti":[-1.009,0,0]},{"i":{"x":0.436,"y":1},"o":{"x":0.17,"y":0.547},"t":10,"s":[30.576,40,0],"to":[1.064,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":30,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":60,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":90,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":120,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":150,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":180,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":210,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":240,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":270,"s":[32.503,40,0],"to":[0,0,0],"ti":[0.751,0,0]},{"t":300,"s":[28,40,0]}],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.566,0],[-3.929,-5.503],[-2.751,-6.68],[3.929,0],[-2.751,6.68],[-3.929,5.503]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.218,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue600","cl":"blue600","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[41.878,40,0],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[55,55],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[28.253,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[75]},{"t":30,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-30,"s":[55,55]},{"t":30,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-30,"op":30,"st":-30,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[75]},{"t":60,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[55,55]},{"t":60,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70,"s":[75]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[55,55]},{"t":90,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":30,"op":90,"st":30,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[75]},{"t":120,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[55,55]},{"t":120,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":60,"op":120,"st":60,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":130,"s":[75]},{"t":150,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":90,"s":[55,55]},{"t":150,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":90,"op":150,"st":90,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[75]},{"t":180,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":120,"s":[55,55]},{"t":180,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":120,"op":180,"st":120,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":190,"s":[75]},{"t":210,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":150,"s":[55,55]},{"t":210,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":150,"op":210,"st":150,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":220,"s":[75]},{"t":240,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":180,"s":[55,55]},{"t":240,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":180,"op":240,"st":180,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[75]},{"t":270,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":210,"s":[55,55]},{"t":270,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":210,"op":270,"st":210,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":280,"s":[75]},{"t":300,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":240,"s":[55,55]},{"t":300,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":240,"op":300,"st":240,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":310,"s":[75]},{"t":330,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":270,"s":[55,55]},{"t":330,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.858823537827,0.803921580315,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":270,"op":330,"st":270,"bm":0}],"markers":[]}
\ No newline at end of file
+{"v":"5.7.14","fr":60,"ip":0,"op":300,"w":42,"h":80,"nm":"sfps_pulse_motion_04","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".black","cl":"black","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.617,"y":0.539},"o":{"x":0.477,"y":0},"t":0,"s":[28,40,0],"to":[0.365,0,0],"ti":[-1.009,0,0]},{"i":{"x":0.436,"y":1},"o":{"x":0.17,"y":0.547},"t":10,"s":[30.576,40,0],"to":[1.064,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":30,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":60,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":90,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":120,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":150,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":180,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":210,"s":[32.503,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.3,"y":0},"t":240,"s":[28,40,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":270,"s":[32.503,40,0],"to":[0,0,0],"ti":[0.751,0,0]},{"t":300,"s":[28,40,0]}],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[1.566,0],[-3.929,-5.503],[-2.751,-6.68],[3.929,0],[-2.751,6.68],[-3.929,5.503]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[30.218,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":".blue600","cl":"blue600","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[41.878,40,0],"ix":2,"l":2},"a":{"a":0,"k":[28.253,40,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[55,55],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 2","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[28.253,40],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":600,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":10,"s":[75]},{"t":30,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":-30,"s":[55,55]},{"t":30,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-30,"op":30,"st":-30,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":40,"s":[75]},{"t":60,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":0,"s":[55,55]},{"t":60,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":70,"s":[75]},{"t":90,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":30,"s":[55,55]},{"t":90,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":30,"op":90,"st":30,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":100,"s":[75]},{"t":120,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":60,"s":[55,55]},{"t":120,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":60,"op":120,"st":60,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":130,"s":[75]},{"t":150,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":90,"s":[55,55]},{"t":150,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":90,"op":150,"st":90,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":160,"s":[75]},{"t":180,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":120,"s":[55,55]},{"t":180,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":120,"op":180,"st":120,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":190,"s":[75]},{"t":210,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":150,"s":[55,55]},{"t":210,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":150,"op":210,"st":150,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":220,"s":[75]},{"t":240,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":180,"s":[55,55]},{"t":240,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":180,"op":240,"st":180,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":250,"s":[75]},{"t":270,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":210,"s":[55,55]},{"t":270,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":210,"op":270,"st":210,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":280,"s":[75]},{"t":300,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":240,"s":[55,55]},{"t":300,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":240,"op":300,"st":240,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":".blue400","cl":"blue400","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":310,"s":[75]},{"t":330,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[25.587,40,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.25,0.25],"y":[1,1]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":270,"s":[55,55]},{"t":330,"s":[80,80]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.101960785687,0.450980395079,0.909803926945,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[16.338,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":270,"op":330,"st":270,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index b4e1b66..c3651cf 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -44,6 +44,12 @@
<!-- orientation of the dead zone when touches have recently occurred elsewhere on screen -->
<integer name="navigation_bar_deadzone_orientation">0</integer>
+ <!-- Whether or not lockscreen shortcuts can be customized -->
+ <bool name="custom_lockscreen_shortcuts_enabled">false</bool>
+
+ <!-- Whether or not long-pressing on keyguard will display to customize lockscreen -->
+ <bool name="long_press_keyguard_customize_lockscreen_enabled">false</bool>
+
<bool name="config_dead_zone_flash">false</bool>
<!-- Whether to enable dimming navigation buttons when wallpaper is not visible, should be
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7187de8..d5806ec 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -45,6 +45,9 @@
<dimen name="navigation_edge_action_drag_threshold">16dp</dimen>
<!-- The threshold to progress back animation for edge swipe -->
<dimen name="navigation_edge_action_progress_threshold">412dp</dimen>
+ <!-- This value is used to calculate the target if the screen is wider than the
+ navigation_edge_action_progress_threshold. See BackAnimation#setSwipeThresholds -->
+ <item name="back_progress_non_linear_factor" format="float" type="dimen">0.2</item>
<!-- The minimum display position of the arrow on the screen -->
<dimen name="navigation_edge_arrow_min_y">64dp</dimen>
<!-- The amount by which the arrow is shifted to avoid the finger-->
@@ -1643,6 +1646,19 @@
<dimen name="dream_overlay_bottom_affordance_height">64dp</dimen>
<dimen name="dream_overlay_bottom_affordance_width">64dp</dimen>
<dimen name="dream_overlay_bottom_affordance_radius">32dp</dimen>
+ <dimen name="dream_overlay_bottom_affordance_key_text_shadow_dx">0.5dp</dimen>
+ <dimen name="dream_overlay_bottom_affordance_key_text_shadow_dy">0.5dp</dimen>
+ <dimen name="dream_overlay_bottom_affordance_key_text_shadow_radius">1dp</dimen>
+ <item name="dream_overlay_bottom_affordance_key_shadow_alpha" format="float" type="dimen">
+ 0.35
+ </item>
+ <dimen name="dream_overlay_bottom_affordance_ambient_text_shadow_dx">0.5dp</dimen>
+ <dimen name="dream_overlay_bottom_affordance_ambient_text_shadow_dy">0.5dp</dimen>
+ <dimen name="dream_overlay_bottom_affordance_ambient_text_shadow_radius">2dp</dimen>
+ <item name="dream_overlay_bottom_affordance_ambient_shadow_alpha" format="float" type="dimen">
+ 0.4
+ </item>
+ <dimen name="dream_overlay_bottom_affordance_inset">1dp</dimen>
<dimen name="dream_overlay_bottom_affordance_padding">14dp</dimen>
<dimen name="dream_overlay_complication_clock_time_text_size">86dp</dimen>
<dimen name="dream_overlay_complication_clock_subtitle_text_size">24sp</dimen>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt
index 19d0a3d..6b9274c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt
@@ -15,6 +15,7 @@
*/
package com.android.systemui.shared.shadow
+import android.content.res.ColorStateList
import android.graphics.BlendMode
import android.graphics.Canvas
import android.graphics.Color
@@ -106,6 +107,14 @@
mIconDrawable.draw(canvas)
}
+ override fun getIntrinsicHeight(): Int {
+ return mCanvasSize
+ }
+
+ override fun getIntrinsicWidth(): Int {
+ return mCanvasSize
+ }
+
override fun getOpacity(): Int {
return PixelFormat.TRANSPARENT
}
@@ -121,4 +130,8 @@
override fun setTint(color: Int) {
mIconDrawable.setTint(color)
}
+
+ override fun setTintList(tint: ColorStateList?) {
+ mIconDrawable.setTintList(tint)
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 4bf7be6..c5b14e3 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -108,6 +108,7 @@
}
updateFontSizes()
updateTimeListeners()
+ cachedWeatherData?.let { value.events.onWeatherDataChanged(it) }
value.smallClock.view.addOnAttachStateChangeListener(
object : OnAttachStateChangeListener {
override fun onViewAttachedToWindow(p0: View?) {
@@ -239,6 +240,7 @@
var largeTimeListener: TimeListener? = null
val shouldTimeListenerRun: Boolean
get() = isKeyguardVisible && dozeAmount < DOZE_TICKRATE_THRESHOLD
+ private var cachedWeatherData: WeatherData? = null
private var smallClockIsDark = true
private var largeClockIsDark = true
@@ -305,6 +307,7 @@
}
override fun onWeatherDataChanged(data: WeatherData) {
+ cachedWeatherData = data
clock?.run { events.onWeatherDataChanged(data) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 9007279..0782537 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -20,6 +20,7 @@
import android.app.ActivityTaskManager
import android.content.Context
import android.content.res.Configuration
+import android.graphics.Color
import android.graphics.PixelFormat
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
@@ -459,7 +460,12 @@
addValueCallback(KeyPath(".black", "**"), LottieProperty.COLOR_FILTER) {
PorterDuffColorFilter(chevronFill, PorterDuff.Mode.SRC_ATOP)
}
- } else if (isDarkMode(context)) {
+ } else {
+ if (!isDarkMode(context)) {
+ addValueCallback(KeyPath(".black", "**"), LottieProperty.COLOR_FILTER) {
+ PorterDuffColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP)
+ }
+ }
for (key in listOf(".blue600", ".blue400")) {
addValueCallback(KeyPath(key, "**"), LottieProperty.COLOR_FILTER) {
PorterDuffColorFilter(
diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
index f973aee..4d99282 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
@@ -39,6 +40,7 @@
import com.android.systemui.controls.management.ControlsListingController;
import com.android.systemui.controls.ui.ControlsActivity;
import com.android.systemui.controls.ui.ControlsUiController;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.SystemUser;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.plugins.ActivityStarter;
@@ -56,17 +58,20 @@
* devices at home like lights and thermostats).
*/
public class DreamHomeControlsComplication implements Complication {
+ private final Resources mResources;
private final DreamHomeControlsComplicationComponent.Factory mComponentFactory;
@Inject
public DreamHomeControlsComplication(
+ @Main Resources resources,
DreamHomeControlsComplicationComponent.Factory componentFactory) {
+ mResources = resources;
mComponentFactory = componentFactory;
}
@Override
public ViewHolder createView(ComplicationViewModel model) {
- return mComponentFactory.create().getViewHolder();
+ return mComponentFactory.create(mResources).getViewHolder();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java
index ef18d66..2b5aa7c 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/DreamHomeControlsComplicationComponent.java
@@ -18,12 +18,19 @@
import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.widget.ImageView;
+import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.complication.DreamHomeControlsComplication;
+import com.android.systemui.shared.shadow.DoubleShadowIconDrawable;
+import com.android.systemui.shared.shadow.DoubleShadowTextHelper;
+import dagger.BindsInstance;
import dagger.Module;
import dagger.Provides;
import dagger.Subcomponent;
@@ -59,7 +66,7 @@
*/
@Subcomponent.Factory
interface Factory {
- DreamHomeControlsComplicationComponent create();
+ DreamHomeControlsComplicationComponent create(@BindsInstance Resources resources);
}
/**
@@ -68,6 +75,7 @@
@Module
interface DreamHomeControlsModule {
String DREAM_HOME_CONTROLS_CHIP_VIEW = "dream_home_controls_chip_view";
+ String DREAM_HOME_CONTROLS_BACKGROUND_DRAWABLE = "dream_home_controls_background_drawable";
/**
* Provides the dream home controls chip view.
@@ -75,9 +83,56 @@
@Provides
@DreamHomeControlsComplicationScope
@Named(DREAM_HOME_CONTROLS_CHIP_VIEW)
- static ImageView provideHomeControlsChipView(LayoutInflater layoutInflater) {
- return (ImageView) layoutInflater.inflate(R.layout.dream_overlay_home_controls_chip,
- null, false);
+ static ImageView provideHomeControlsChipView(
+ LayoutInflater layoutInflater,
+ @Named(DREAM_HOME_CONTROLS_BACKGROUND_DRAWABLE) Drawable backgroundDrawable) {
+ final ImageView chip =
+ (ImageView) layoutInflater.inflate(R.layout.dream_overlay_home_controls_chip,
+ null, false);
+ chip.setBackground(backgroundDrawable);
+
+ return chip;
+ }
+
+ @Provides
+ @DreamHomeControlsComplicationScope
+ @Named(DREAM_HOME_CONTROLS_BACKGROUND_DRAWABLE)
+ static Drawable providesHomeControlsBackground(Context context, Resources resources) {
+ final Drawable background = new DoubleShadowIconDrawable(createShadowInfo(
+ resources,
+ R.dimen.dream_overlay_bottom_affordance_key_text_shadow_radius,
+ R.dimen.dream_overlay_bottom_affordance_key_text_shadow_dx,
+ R.dimen.dream_overlay_bottom_affordance_key_text_shadow_dy,
+ R.dimen.dream_overlay_bottom_affordance_key_shadow_alpha
+ ),
+ createShadowInfo(
+ resources,
+ R.dimen.dream_overlay_bottom_affordance_ambient_text_shadow_radius,
+ R.dimen.dream_overlay_bottom_affordance_ambient_text_shadow_dx,
+ R.dimen.dream_overlay_bottom_affordance_ambient_text_shadow_dy,
+ R.dimen.dream_overlay_bottom_affordance_ambient_shadow_alpha
+ ),
+ resources.getDrawable(R.drawable.dream_overlay_bottom_affordance_bg),
+ resources.getDimensionPixelOffset(
+ R.dimen.dream_overlay_bottom_affordance_width),
+ resources.getDimensionPixelSize(R.dimen.dream_overlay_bottom_affordance_inset)
+ );
+
+ background.setTintList(
+ Utils.getColorAttr(context, com.android.internal.R.attr.colorSurface));
+
+ return background;
+ }
+
+ private static DoubleShadowTextHelper.ShadowInfo createShadowInfo(Resources resources,
+ int blurId, int offsetXId, int offsetYId, int alphaId) {
+
+ return new DoubleShadowTextHelper.ShadowInfo(
+ resources.getDimension(blurId),
+ resources.getDimension(offsetXId),
+ resources.getDimension(offsetYId),
+ resources.getFloat(alphaId)
+ );
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 5d403e1..05df31b 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -338,8 +338,7 @@
// TODO(b/280426085): Tracking Bug
@JvmField
- val NEW_BLUETOOTH_REPOSITORY =
- unreleasedFlag(612, "new_bluetooth_repository", teamfood = true)
+ val NEW_BLUETOOTH_REPOSITORY = unreleasedFlag(612, "new_bluetooth_repository")
// 700 - dialer/calls
// TODO(b/254512734): Tracking Bug
@@ -562,7 +561,7 @@
// TODO(b/270987164): Tracking Bug
@JvmField
- val TRACKPAD_GESTURE_FEATURES = releasedFlag(1205, "trackpad_gesture_features")
+ val TRACKPAD_GESTURE_FEATURES = unreleasedFlag(1205, "trackpad_gesture_features", teamfood = true)
// TODO(b/263826204): Tracking Bug
@JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
index ea6700e..ca430da 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractor.kt
@@ -17,12 +17,14 @@
package com.android.systemui.keyguard.domain.interactor
+import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.view.accessibility.AccessibilityManager
import androidx.annotation.VisibleForTesting
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -55,6 +57,7 @@
class KeyguardLongPressInteractor
@Inject
constructor(
+ @Application private val appContext: Context,
@Application private val scope: CoroutineScope,
transitionInteractor: KeyguardTransitionInteractor,
repository: KeyguardRepository,
@@ -169,7 +172,8 @@
private fun isFeatureEnabled(): Boolean {
return featureFlags.isEnabled(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED) &&
- featureFlags.isEnabled(Flags.REVAMPED_WALLPAPER_UI)
+ featureFlags.isEnabled(Flags.REVAMPED_WALLPAPER_UI) &&
+ appContext.resources.getBoolean(R.bool.long_press_keyguard_customize_lockscreen_enabled)
}
/** Updates application state to ask to show the menu. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 8e65c4d..2275337 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -19,12 +19,15 @@
import android.app.AlertDialog
import android.app.admin.DevicePolicyManager
+import android.content.Context
import android.content.Intent
import android.util.Log
import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.R
import com.android.systemui.animation.DialogLaunchAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled
import com.android.systemui.dock.DockManager
@@ -75,6 +78,7 @@
private val devicePolicyManager: DevicePolicyManager,
private val dockManager: DockManager,
@Background private val backgroundDispatcher: CoroutineDispatcher,
+ @Application private val appContext: Context,
) {
private val isUsingRepository: Boolean
get() = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)
@@ -408,7 +412,8 @@
name = Contract.FlagsTable.FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED,
value =
!isFeatureDisabledByDevicePolicy() &&
- featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES),
+ featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES) &&
+ appContext.resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled),
),
KeyguardPickerFlag(
name = Contract.FlagsTable.FLAG_NAME_CUSTOM_CLOCKS_ENABLED,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index edab56e..1fd11bd 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -323,7 +323,6 @@
if (isFlungAwayFromEdge(endX = event.x) ||
previousXTranslation > params.staticTriggerThreshold
) {
- updateArrowState(GestureState.ACTIVE)
updateArrowState(GestureState.FLUNG)
} else {
updateArrowState(GestureState.CANCELLED)
@@ -331,8 +330,11 @@
}
GestureState.INACTIVE -> {
if (isFlungAwayFromEdge(endX = event.x)) {
+ // This is called outside of updateArrowState so that
+ // BackAnimationController can immediately evaluate state
+ // instead of after the flung delay
+ backCallback.setTriggerBack(true)
mainHandler.postDelayed(MIN_DURATION_INACTIVE_BEFORE_FLUNG_ANIMATION) {
- updateArrowState(GestureState.ACTIVE)
updateArrowState(GestureState.FLUNG)
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 42de7f0..5818fd0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -63,6 +63,8 @@
import android.view.WindowManager;
import android.window.BackEvent;
+import androidx.annotation.DimenRes;
+
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.policy.GestureNavigationSettingsObserver;
import com.android.systemui.R;
@@ -225,7 +227,8 @@
// The slop to distinguish between horizontal and vertical motion
private float mTouchSlop;
// The threshold for back swipe full progress.
- private float mBackSwipeProgressThreshold;
+ private float mBackSwipeLinearThreshold;
+ private float mNonLinearFactor;
// Duration after which we consider the event as longpress.
private final int mLongPressTimeout;
private int mStartingQuickstepRotation = -1;
@@ -471,11 +474,19 @@
final float backGestureSlop = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.BACK_GESTURE_SLOP_MULTIPLIER, 0.75f);
mTouchSlop = mViewConfiguration.getScaledTouchSlop() * backGestureSlop;
- mBackSwipeProgressThreshold = res.getDimension(
+ mBackSwipeLinearThreshold = res.getDimension(
R.dimen.navigation_edge_action_progress_threshold);
+ mNonLinearFactor = getDimenFloat(res,
+ R.dimen.back_progress_non_linear_factor);
updateBackAnimationThresholds();
}
+ private float getDimenFloat(Resources res, @DimenRes int resId) {
+ TypedValue typedValue = new TypedValue();
+ res.getValue(resId, typedValue, true);
+ return typedValue.getFloat();
+ }
+
public void updateNavigationBarOverlayExcludeRegion(Rect exclude) {
mNavBarOverlayExcludedBounds.set(exclude);
}
@@ -1116,8 +1127,9 @@
if (mBackAnimation == null) {
return;
}
- mBackAnimation.setSwipeThresholds(
- Math.min(mDisplaySize.x, mBackSwipeProgressThreshold));
+ int maxDistance = mDisplaySize.x;
+ float linearDistance = Math.min(maxDistance, mBackSwipeLinearThreshold);
+ mBackAnimation.setSwipeThresholds(linearDistance, maxDistance, mNonLinearFactor);
}
private boolean sendEvent(int action, int code) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
index 182ece7..6d881d5 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
@@ -132,7 +132,7 @@
entryWidthInterpolator = PathInterpolator(.19f, 1.27f, .71f, .86f)
entryWidthTowardsEdgeInterpolator = PathInterpolator(1f, -3f, 1f, 1.2f)
- activeWidthInterpolator = PathInterpolator(.56f, -0.39f, .18f, 1.46f)
+ activeWidthInterpolator = PathInterpolator(.7f, -0.24f, .48f, 1.21f)
arrowAngleInterpolator = entryWidthInterpolator
horizontalTranslationInterpolator = PathInterpolator(0.2f, 1.0f, 1.0f, 1.0f)
verticalTranslationInterpolator = PathInterpolator(.5f, 1.15f, .41f, .94f)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 31b361f..81fe3ca 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -218,7 +218,7 @@
containerPadding = 0
stackScrollMargin = bottomStableInsets + notificationsBottomMargin
}
- val qsContainerPadding = if (!(isQSCustomizing || isQSDetailShowing)) {
+ val qsContainerPadding = if (!isQSDetailShowing) {
// We also want this padding in the bottom in these cases
if (splitShadeEnabled) {
stackScrollMargin - scrimShadeBottomMargin - footerActionsOffset
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
index 3cbb249..63b0b25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
@@ -29,6 +29,7 @@
import android.content.ComponentName;
import android.content.Context;
+import android.content.res.Resources;
import android.testing.AndroidTestingRunner;
import android.view.View;
@@ -73,6 +74,9 @@
private Context mContext;
@Mock
+ private Resources mResources;
+
+ @Mock
private ControlsComponent mControlsComponent;
@Mock
@@ -118,7 +122,7 @@
@Test
public void complicationType() {
final DreamHomeControlsComplication complication =
- new DreamHomeControlsComplication(mComponentFactory);
+ new DreamHomeControlsComplication(mResources, mComponentFactory);
assertThat(complication.getRequiredTypeAvailability()).isEqualTo(
COMPLICATION_TYPE_HOME_CONTROLS);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 9cf988e..8ee7d3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -30,6 +30,7 @@
import android.view.SurfaceControlViewHost
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.R
import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogLaunchAnimator
@@ -67,6 +68,7 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -103,6 +105,7 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ overrideResource(R.bool.custom_lockscreen_shortcuts_enabled, true)
whenever(previewRenderer.surfacePackage).thenReturn(previewSurfacePackage)
whenever(previewRendererFactory.create(any())).thenReturn(previewRenderer)
whenever(backgroundHandler.looper).thenReturn(TestableLooper.get(this).looper)
@@ -195,6 +198,7 @@
devicePolicyManager = devicePolicyManager,
dockManager = dockManager,
backgroundDispatcher = testDispatcher,
+ appContext = mContext,
)
underTest.previewManager =
KeyguardRemotePreviewManager(
@@ -216,6 +220,13 @@
)
}
+ @After
+ fun tearDown() {
+ mContext
+ .getOrCreateTestableResources()
+ .removeOverride(R.bool.custom_lockscreen_shortcuts_enabled)
+ }
+
@Test
fun onAttachInfo_reportsContext() {
val callback: SystemUIAppComponentFactoryBase.ContextAvailableCallback = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
index dfef947..5de24e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
@@ -21,6 +21,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.R
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
@@ -39,6 +40,7 @@
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -65,6 +67,7 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ overrideResource(R.bool.long_press_keyguard_customize_lockscreen_enabled, true)
whenever(accessibilityManager.getRecommendedTimeoutMillis(anyInt(), anyInt())).thenAnswer {
it.arguments[0]
}
@@ -76,6 +79,13 @@
runBlocking { createUnderTest() }
}
+ @After
+ fun tearDown() {
+ mContext
+ .getOrCreateTestableResources()
+ .removeOverride(R.bool.long_press_keyguard_customize_lockscreen_enabled)
+ }
+
@Test
fun isEnabled() =
testScope.runTest {
@@ -108,6 +118,17 @@
}
@Test
+ fun isEnabled_alwaysFalseWhenConfigEnabledBooleanIsFalse() =
+ testScope.runTest {
+ overrideResource(R.bool.long_press_keyguard_customize_lockscreen_enabled, false)
+ createUnderTest()
+ val isEnabled by collectLastValue(underTest.isLongPressHandlingEnabled)
+ runCurrent()
+
+ assertThat(isEnabled).isFalse()
+ }
+
+ @Test
fun longPressed_menuClicked_showsSettings() =
testScope.runTest {
val isMenuVisible by collectLastValue(underTest.isMenuVisible)
@@ -267,6 +288,7 @@
) {
underTest =
KeyguardLongPressInteractor(
+ appContext = mContext,
scope = testScope.backgroundScope,
transitionInteractor =
KeyguardTransitionInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index a75e11a..fb21847 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -340,6 +340,7 @@
devicePolicyManager = devicePolicyManager,
dockManager = dockManager,
backgroundDispatcher = testDispatcher,
+ appContext = mContext,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 3336e3b..5d2c3ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -200,6 +200,7 @@
devicePolicyManager = devicePolicyManager,
dockManager = dockManager,
backgroundDispatcher = testDispatcher,
+ appContext = mContext,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 69d43af..8a36dbc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -211,6 +211,7 @@
)
val keyguardLongPressInteractor =
KeyguardLongPressInteractor(
+ appContext = mContext,
scope = testScope.backgroundScope,
transitionInteractor =
KeyguardTransitionInteractor(
@@ -240,6 +241,7 @@
devicePolicyManager = devicePolicyManager,
dockManager = dockManager,
backgroundDispatcher = testDispatcher,
+ appContext = mContext,
),
bottomAreaInteractor = KeyguardBottomAreaInteractor(repository = repository),
burnInHelperWrapper = burnInHelperWrapper,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
index dfb1bce..168cbb7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
@@ -229,25 +229,6 @@
}
@Test
- fun testCustomizingInSinglePaneShade() {
- disableSplitShade()
- controller.setCustomizerShowing(true)
-
- // always sets spacings to 0
- given(taskbarVisible = false,
- navigationMode = GESTURES_NAVIGATION,
- insets = windowInsets().withStableBottom())
- then(expectedContainerPadding = 0,
- expectedNotificationsMargin = 0)
-
- given(taskbarVisible = false,
- navigationMode = BUTTONS_NAVIGATION,
- insets = emptyInsets())
- then(expectedContainerPadding = 0,
- expectedNotificationsMargin = 0)
- }
-
- @Test
fun testDetailShowingInSinglePaneShade() {
disableSplitShade()
controller.setDetailShowing(true)
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index d9b3157..d6e692c 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -62,6 +62,7 @@
import android.os.BundleMerger;
import android.os.Handler;
import android.os.Message;
+import android.os.PowerExemptionManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -878,12 +879,20 @@
mLocalHandler.sendMessageDelayed(
Message.obtain(mLocalHandler, MSG_BG_ACTIVITY_START_TIMEOUT, args), timeout);
}
-
if (r.options != null && r.options.getTemporaryAppAllowlistDuration() > 0) {
- mService.tempAllowlistUidLocked(queue.uid,
- r.options.getTemporaryAppAllowlistDuration(),
- r.options.getTemporaryAppAllowlistReasonCode(), r.toShortString(),
- r.options.getTemporaryAppAllowlistType(), r.callingUid);
+ if (r.options.getTemporaryAppAllowlistType()
+ == PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED) {
+ // Only delay freezer, don't add to any temp allowlist
+ // TODO: Add a unit test
+ mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(app,
+ CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER,
+ r.options.getTemporaryAppAllowlistDuration());
+ } else {
+ mService.tempAllowlistUidLocked(queue.uid,
+ r.options.getTemporaryAppAllowlistDuration(),
+ r.options.getTemporaryAppAllowlistReasonCode(), r.toShortString(),
+ r.options.getTemporaryAppAllowlistType(), r.callingUid);
+ }
}
if (DEBUG_BROADCAST) logv("Scheduling " + r + " to warm " + app);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 3e82d55..7773190 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -47,6 +47,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import android.annotation.IntDef;
+import android.annotation.UptimeMillisLong;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal.OomAdjReason;
import android.app.ActivityThread;
@@ -1278,14 +1279,35 @@
return true;
}
+ /**
+ * Returns the earliest time (relative) from now that the app can be frozen.
+ * @param app The app to update
+ * @param delayMillis How much to delay freezing by
+ */
+ @GuardedBy("mProcLock")
+ private long updateEarliestFreezableTime(ProcessRecord app, long delayMillis) {
+ final long now = SystemClock.uptimeMillis();
+ app.mOptRecord.setEarliestFreezableTime(
+ Math.max(app.mOptRecord.getEarliestFreezableTime(), now + delayMillis));
+ return app.mOptRecord.getEarliestFreezableTime() - now;
+ }
+
// This will ensure app will be out of the freezer for at least mFreezerDebounceTimeout.
@GuardedBy("mAm")
void unfreezeTemporarily(ProcessRecord app, @UnfreezeReason int reason) {
+ unfreezeTemporarily(app, reason, mFreezerDebounceTimeout);
+ }
+
+ // This will ensure app will be out of the freezer for at least mFreezerDebounceTimeout.
+ @GuardedBy("mAm")
+ void unfreezeTemporarily(ProcessRecord app, @UnfreezeReason int reason, long delayMillis) {
if (mUseFreezer) {
synchronized (mProcLock) {
+ // Move the earliest freezable time further, if necessary
+ final long delay = updateEarliestFreezableTime(app, delayMillis);
if (app.mOptRecord.isFrozen() || app.mOptRecord.isPendingFreeze()) {
unfreezeAppLSP(app, reason);
- freezeAppAsyncLSP(app);
+ freezeAppAsyncLSP(app, delay);
}
}
}
@@ -1293,11 +1315,17 @@
@GuardedBy({"mAm", "mProcLock"})
void freezeAppAsyncLSP(ProcessRecord app) {
- freezeAppAsyncInternalLSP(app, mFreezerDebounceTimeout, false);
+ freezeAppAsyncLSP(app, updateEarliestFreezableTime(app, mFreezerDebounceTimeout));
}
@GuardedBy({"mAm", "mProcLock"})
- void freezeAppAsyncInternalLSP(ProcessRecord app, long delayMillis, boolean force) {
+ private void freezeAppAsyncLSP(ProcessRecord app, @UptimeMillisLong long delayMillis) {
+ freezeAppAsyncInternalLSP(app, delayMillis, false);
+ }
+
+ @GuardedBy({"mAm", "mProcLock"})
+ void freezeAppAsyncInternalLSP(ProcessRecord app, @UptimeMillisLong long delayMillis,
+ boolean force) {
final ProcessCachedOptimizerRecord opt = app.mOptRecord;
if (opt.isPendingFreeze()) {
// Skip redundant DO_FREEZE message
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index e8c8f6d..7841b69 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import android.annotation.UptimeMillisLong;
import android.app.ActivityManagerInternal.OomAdjReason;
import com.android.internal.annotations.GuardedBy;
@@ -119,6 +120,12 @@
@GuardedBy("mProcLock")
private boolean mPendingFreeze;
+ /**
+ * This is the soonest the process can be allowed to freeze, in uptime millis
+ */
+ @GuardedBy("mProcLock")
+ private @UptimeMillisLong long mEarliestFreezableTimeMillis;
+
@GuardedBy("mProcLock")
long getLastCompactTime() {
return mLastCompactTime;
@@ -264,6 +271,16 @@
}
@GuardedBy("mProcLock")
+ @UptimeMillisLong long getEarliestFreezableTime() {
+ return mEarliestFreezableTimeMillis;
+ }
+
+ @GuardedBy("mProcLock")
+ void setEarliestFreezableTime(@UptimeMillisLong long earliestFreezableTimeMillis) {
+ mEarliestFreezableTimeMillis = earliestFreezableTimeMillis;
+ }
+
+ @GuardedBy("mProcLock")
boolean isFreezeExempt() {
return mFreezeExempt;
}
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index b22ece3..9e66bfe 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -175,7 +175,7 @@
// while-in-use permissions in FGS started from background might be restricted.
boolean mAllowWhileInUsePermissionInFgs;
@PowerExemptionManager.ReasonCode
- int mAllowWhileInUsePermissionInFgsReason;
+ int mAllowWhileInUsePermissionInFgsReason = REASON_DENIED;
// Integer version of mAllowWhileInUsePermissionInFgs that we keep track to compare
// the old and new logics.
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 5771a04..34ad91c4 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1560,7 +1560,7 @@
// VirtualDisplay has been successfully constructed.
session.setVirtualDisplayId(displayId);
// Don't start mirroring until user re-grants consent.
- session.setWaitingToRecord(waitForPermissionConsent);
+ session.setWaitingForConsent(waitForPermissionConsent);
// We set the content recording session here on the server side instead of using
// a second AIDL call in MediaProjection. By ensuring that a virtual display has
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 377b8cf..38631c8 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -406,7 +406,7 @@
return;
}
if (mProjectionGrant.mSession == null
- || !mProjectionGrant.mSession.isWaitingToRecord()) {
+ || !mProjectionGrant.mSession.isWaitingForConsent()) {
Slog.w(TAG, "Reusing token: Ignore consent result " + consentResult
+ " if not waiting for the result.");
return;
@@ -445,7 +445,7 @@
*/
private void setReviewedConsentSessionLocked(@Nullable ContentRecordingSession session) {
if (session != null) {
- session.setWaitingToRecord(false);
+ session.setWaitingForConsent(false);
session.setVirtualDisplayId(mProjectionGrant.mVirtualDisplayId);
}
@@ -490,7 +490,7 @@
// Supposedly the package has re-used the user's consent; confirm the provided details
// against the current projection token before re-using the current projection.
if (mProjectionGrant == null || mProjectionGrant.mSession == null
- || !mProjectionGrant.mSession.isWaitingToRecord()) {
+ || !mProjectionGrant.mSession.isWaitingForConsent()) {
Slog.e(TAG, "Reusing token: Not possible to reuse the current projection "
+ "instance");
return null;
diff --git a/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
index 73ab782..712a696 100644
--- a/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
+++ b/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
@@ -23,6 +23,7 @@
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
+import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.content.Context;
import android.os.Handler;
@@ -48,6 +49,7 @@
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
+import java.util.function.LongSupplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -62,15 +64,14 @@
private static final String SUBSYSTEM_SENSOR_STRING = "Sensor";
private static final String SUBSYSTEM_CELLULAR_DATA_STRING = "Cellular_data";
private static final String TRACE_TRACK_WAKEUP_ATTRIBUTION = "wakeup_attribution";
- @VisibleForTesting
- static final long WAKEUP_REASON_HALF_WINDOW_MS = 500;
+
private static final long WAKEUP_WRITE_DELAY_MS = TimeUnit.SECONDS.toMillis(30);
private final Handler mHandler;
private final IrqDeviceMap mIrqDeviceMap;
@VisibleForTesting
final Config mConfig = new Config();
- private final WakingActivityHistory mRecentWakingActivity = new WakingActivityHistory();
+ private final WakingActivityHistory mRecentWakingActivity;
@VisibleForTesting
final TimeSparseArray<Wakeup> mWakeupEvents = new TimeSparseArray<>();
@@ -85,6 +86,8 @@
public CpuWakeupStats(Context context, int mapRes, Handler handler) {
mIrqDeviceMap = IrqDeviceMap.getInstance(context, mapRes);
+ mRecentWakingActivity = new WakingActivityHistory(
+ () -> mConfig.WAKING_ACTIVITY_RETENTION_MS);
mHandler = handler;
}
@@ -202,15 +205,14 @@
/** Notes a wakeup reason as reported by SuspendControlService to battery stats. */
public synchronized void noteWakeupTimeAndReason(long elapsedRealtime, long uptime,
String rawReason) {
- final Wakeup parsedWakeup = Wakeup.parseWakeup(rawReason, elapsedRealtime, uptime);
+ final Wakeup parsedWakeup = Wakeup.parseWakeup(rawReason, elapsedRealtime, uptime,
+ mIrqDeviceMap);
if (parsedWakeup == null) {
+ // This wakeup is unsupported for attribution. Exit.
return;
}
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 retentionDuration. Note that
// the last wakeup and its attribution (if computed) is always stored, even if that wakeup
@@ -244,25 +246,22 @@
}
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;
- }
+ final SparseBooleanArray subsystems = wakeup.mResponsibleSubsystems;
SparseArray<SparseIntArray> attribution = mWakeupAttribution.get(wakeup.mElapsedMillis);
if (attribution == null) {
attribution = new SparseArray<>();
mWakeupAttribution.put(wakeup.mElapsedMillis, attribution);
}
+ final long matchingWindowMillis = mConfig.WAKEUP_MATCHING_WINDOW_MS;
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
+ // Blame all activity that happened matchingWindowMillis 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 long startTime = wakeup.mElapsedMillis - matchingWindowMillis;
+ final long endTime = wakeup.mElapsedMillis + matchingWindowMillis;
final SparseIntArray uidsToBlame = mRecentWakingActivity.removeBetween(subsystem,
startTime, endTime);
@@ -272,18 +271,16 @@
private synchronized boolean attemptAttributionWith(int subsystem, long activityElapsed,
SparseIntArray uidProcStates) {
+ final long matchingWindowMillis = mConfig.WAKEUP_MATCHING_WINDOW_MS;
+
final int startIdx = mWakeupEvents.closestIndexOnOrAfter(
- activityElapsed - WAKEUP_REASON_HALF_WINDOW_MS);
+ activityElapsed - matchingWindowMillis);
final int endIdx = mWakeupEvents.closestIndexOnOrBefore(
- activityElapsed + WAKEUP_REASON_HALF_WINDOW_MS);
+ activityElapsed + matchingWindowMillis);
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;
- }
+ final SparseBooleanArray subsystems = wakeup.mResponsibleSubsystems;
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
@@ -405,11 +402,13 @@
*/
@VisibleForTesting
static final class WakingActivityHistory {
- private static final long WAKING_ACTIVITY_RETENTION_MS = TimeUnit.MINUTES.toMillis(10);
-
+ private LongSupplier mRetentionSupplier;
@VisibleForTesting
- final SparseArray<TimeSparseArray<SparseIntArray>> mWakingActivity =
- new SparseArray<>();
+ final SparseArray<TimeSparseArray<SparseIntArray>> mWakingActivity = new SparseArray<>();
+
+ WakingActivityHistory(LongSupplier retentionSupplier) {
+ mRetentionSupplier = retentionSupplier;
+ }
void recordActivity(int subsystem, long elapsedRealtime, SparseIntArray uidProcStates) {
if (uidProcStates == null) {
@@ -433,27 +432,13 @@
}
}
}
- // 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.
+ // Limit activity history per subsystem to the last retention period as supplied by
+ // mRetentionSupplier. Note that the last activity is always present, even if it
+ // occurred before the retention period.
final int endIdx = wakingActivity.closestIndexOnOrBefore(
- elapsedRealtime - WAKING_ACTIVITY_RETENTION_MS);
+ elapsedRealtime - mRetentionSupplier.getAsLong());
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<SparseIntArray> 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.
+ wakingActivity.removeAt(i);
}
}
@@ -515,33 +500,6 @@
}
}
- 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:
@@ -598,15 +556,19 @@
long mElapsedMillis;
long mUptimeMillis;
IrqDevice[] mDevices;
+ SparseBooleanArray mResponsibleSubsystems;
- private Wakeup(int type, IrqDevice[] devices, long elapsedMillis, long uptimeMillis) {
+ private Wakeup(int type, IrqDevice[] devices, long elapsedMillis, long uptimeMillis,
+ SparseBooleanArray responsibleSubsystems) {
mType = type;
mDevices = devices;
mElapsedMillis = elapsedMillis;
mUptimeMillis = uptimeMillis;
+ mResponsibleSubsystems = responsibleSubsystems;
}
- static Wakeup parseWakeup(String rawReason, long elapsedMillis, long uptimeMillis) {
+ static Wakeup parseWakeup(String rawReason, long elapsedMillis, long uptimeMillis,
+ IrqDeviceMap deviceMap) {
final String[] components = rawReason.split(":");
if (ArrayUtils.isEmpty(components) || components[0].startsWith(ABORT_REASON_PREFIX)) {
// Accounting of aborts is not supported yet.
@@ -616,6 +578,7 @@
int type = TYPE_IRQ;
int parsedDeviceCount = 0;
final IrqDevice[] parsedDevices = new IrqDevice[components.length];
+ final SparseBooleanArray responsibleSubsystems = new SparseBooleanArray();
for (String component : components) {
final Matcher matcher = sIrqPattern.matcher(component.trim());
@@ -635,13 +598,35 @@
continue;
}
parsedDevices[parsedDeviceCount++] = new IrqDevice(line, device);
+
+ final List<String> rawSubsystems = deviceMap.getSubsystemsForDevice(device);
+ 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 map.
+ responsibleSubsystems.put(subsystem, true);
+ anyKnownSubsystem = true;
+ }
+ }
+ }
+ if (!anyKnownSubsystem) {
+ responsibleSubsystems.put(CPU_WAKEUP_SUBSYSTEM_UNKNOWN, true);
+ }
}
}
if (parsedDeviceCount == 0) {
return null;
}
+ if (responsibleSubsystems.size() == 1 && responsibleSubsystems.get(
+ CPU_WAKEUP_SUBSYSTEM_UNKNOWN, false)) {
+ // There is no attributable subsystem here, so we do not support it.
+ return null;
+ }
return new Wakeup(type, Arrays.copyOf(parsedDevices, parsedDeviceCount), elapsedMillis,
- uptimeMillis);
+ uptimeMillis, responsibleSubsystems);
}
@Override
@@ -651,6 +636,7 @@
+ ", mElapsedMillis=" + mElapsedMillis
+ ", mUptimeMillis=" + mUptimeMillis
+ ", mDevices=" + Arrays.toString(mDevices)
+ + ", mResponsibleSubsystems=" + mResponsibleSubsystems
+ '}';
}
@@ -672,18 +658,28 @@
static final class Config implements DeviceConfig.OnPropertiesChangedListener {
static final String KEY_WAKEUP_STATS_RETENTION_MS = "wakeup_stats_retention_ms";
+ static final String KEY_WAKEUP_MATCHING_WINDOW_MS = "wakeup_matching_window_ms";
+ static final String KEY_WAKING_ACTIVITY_RETENTION_MS = "waking_activity_retention_ms";
private static final String[] PROPERTY_NAMES = {
KEY_WAKEUP_STATS_RETENTION_MS,
+ KEY_WAKEUP_MATCHING_WINDOW_MS,
+ KEY_WAKING_ACTIVITY_RETENTION_MS,
};
static final long DEFAULT_WAKEUP_STATS_RETENTION_MS = TimeUnit.DAYS.toMillis(3);
+ private static final long DEFAULT_WAKEUP_MATCHING_WINDOW_MS = TimeUnit.SECONDS.toMillis(1);
+ private static final long DEFAULT_WAKING_ACTIVITY_RETENTION_MS =
+ TimeUnit.MINUTES.toMillis(5);
/**
* Wakeup stats are retained only for this duration.
*/
public volatile long WAKEUP_STATS_RETENTION_MS = DEFAULT_WAKEUP_STATS_RETENTION_MS;
+ public volatile long WAKEUP_MATCHING_WINDOW_MS = DEFAULT_WAKEUP_MATCHING_WINDOW_MS;
+ public volatile long WAKING_ACTIVITY_RETENTION_MS = DEFAULT_WAKING_ACTIVITY_RETENTION_MS;
+ @SuppressLint("MissingPermission")
void register(Executor executor) {
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_BATTERY_STATS,
executor, this);
@@ -702,6 +698,15 @@
WAKEUP_STATS_RETENTION_MS = properties.getLong(
KEY_WAKEUP_STATS_RETENTION_MS, DEFAULT_WAKEUP_STATS_RETENTION_MS);
break;
+ case KEY_WAKEUP_MATCHING_WINDOW_MS:
+ WAKEUP_MATCHING_WINDOW_MS = properties.getLong(
+ KEY_WAKEUP_MATCHING_WINDOW_MS, DEFAULT_WAKEUP_MATCHING_WINDOW_MS);
+ break;
+ case KEY_WAKING_ACTIVITY_RETENTION_MS:
+ WAKING_ACTIVITY_RETENTION_MS = properties.getLong(
+ KEY_WAKING_ACTIVITY_RETENTION_MS,
+ DEFAULT_WAKING_ACTIVITY_RETENTION_MS);
+ break;
}
}
}
@@ -711,7 +716,19 @@
pw.increaseIndent();
- pw.print(KEY_WAKEUP_STATS_RETENTION_MS, WAKEUP_STATS_RETENTION_MS);
+ pw.print(KEY_WAKEUP_STATS_RETENTION_MS);
+ pw.print("=");
+ TimeUtils.formatDuration(WAKEUP_STATS_RETENTION_MS, pw);
+ pw.println();
+
+ pw.print(KEY_WAKEUP_MATCHING_WINDOW_MS);
+ pw.print("=");
+ TimeUtils.formatDuration(WAKEUP_MATCHING_WINDOW_MS, pw);
+ pw.println();
+
+ pw.print(KEY_WAKING_ACTIVITY_RETENTION_MS);
+ pw.print("=");
+ TimeUtils.formatDuration(WAKING_ACTIVITY_RETENTION_MS, pw);
pw.println();
pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java
index c05d148..87ebdbf 100644
--- a/services/core/java/com/android/server/tv/TvInputHal.java
+++ b/services/core/java/com/android/server/tv/TvInputHal.java
@@ -234,6 +234,7 @@
int type = msg.arg2;
Bundle data = (Bundle) msg.obj;
mCallback.onTvMessage(deviceId, type, data);
+ break;
}
default:
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index b808a55..6a7e764 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -276,6 +276,12 @@
return;
}
+ if (mContentRecordingSession.isWaitingForConsent()) {
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING, "Content Recording: waiting to record, so do "
+ + "nothing");
+ return;
+ }
+
mRecordedWindowContainer = retrieveRecordedWindowContainer();
if (mRecordedWindowContainer == null) {
// Either the token is missing, or the window associated with the token is missing.
diff --git a/services/core/java/com/android/server/wm/ContentRecordingController.java b/services/core/java/com/android/server/wm/ContentRecordingController.java
index 4da55e2..f24ba5a 100644
--- a/services/core/java/com/android/server/wm/ContentRecordingController.java
+++ b/services/core/java/com/android/server/wm/ContentRecordingController.java
@@ -56,8 +56,8 @@
* Updates the current recording session.
* <p>Handles the following scenarios:
* <ul>
- * <li>Invalid scenarios: The incoming session is malformed, or the incoming session is
- * identical to the current session</li>
+ * <li>Invalid scenarios: The incoming session is malformed.</li>
+ * <li>Ignored scenario: the incoming session is identical to the current session.</li>
* <li>Start Scenario: Starting a new session. Recording begins immediately.</li>
* <li>Takeover Scenario: Occurs during a Start Scenario, if a pre-existing session was
* in-progress. For example, recording on VirtualDisplay "app_foo" was ongoing. A
@@ -66,6 +66,8 @@
* begin.</li>
* <li>Stopping scenario: The incoming session is null and there is currently an ongoing
* session. The controller stops recording.</li>
+ * <li>Updating scenario: There is an update for the same display, where recording
+ * was previously not taking place but is now permitted to go ahead.</li>
* </ul>
*
* @param incomingSession The incoming recording session (either an update to a current session
@@ -78,20 +80,28 @@
if (incomingSession != null && !ContentRecordingSession.isValid(incomingSession)) {
return;
}
- // Invalid scenario: ignore identical incoming session.
+ final boolean hasSessionUpdatedWithConsent =
+ mSession != null && incomingSession != null && mSession.isWaitingForConsent()
+ && !incomingSession.isWaitingForConsent();
if (ContentRecordingSession.isProjectionOnSameDisplay(mSession, incomingSession)) {
- // TODO(242833866): if incoming session is no longer waiting to record, allow
- // the update through.
-
- ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
- "Content Recording: Ignoring session on same display %d, with an existing "
- + "session %s",
- incomingSession.getVirtualDisplayId(), mSession.getVirtualDisplayId());
- return;
+ if (hasSessionUpdatedWithConsent) {
+ // Updating scenario: accept an incoming session updating the current display.
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+ "Content Recording: Accept session updating same display %d with granted "
+ + "consent, with an existing session %s",
+ incomingSession.getVirtualDisplayId(), mSession.getVirtualDisplayId());
+ } else {
+ // Ignored scenario: ignore identical incoming session.
+ ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
+ "Content Recording: Ignoring session on same display %d, with an existing "
+ + "session %s",
+ incomingSession.getVirtualDisplayId(), mSession.getVirtualDisplayId());
+ return;
+ }
}
DisplayContent incomingDisplayContent = null;
- // Start scenario: recording begins immediately.
if (incomingSession != null) {
+ // Start scenario: recording begins immediately.
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Content Recording: Handle incoming session on display %d, with a "
+ "pre-existing session %s", incomingSession.getVirtualDisplayId(),
@@ -106,11 +116,14 @@
return;
}
incomingDisplayContent.setContentRecordingSession(incomingSession);
- // TODO(b/270118861): When user grants consent to re-use, explicitly ask ContentRecorder
- // to update, since no config/display change arrives. Mark recording as black.
+ // Updating scenario: Explicitly ask ContentRecorder to update, since no config or
+ // display change will trigger an update from the DisplayContent.
+ if (hasSessionUpdatedWithConsent) {
+ incomingDisplayContent.updateRecording();
+ }
}
// Takeover and stopping scenario: stop recording on the pre-existing session.
- if (mSession != null) {
+ if (mSession != null && !hasSessionUpdatedWithConsent) {
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Content Recording: Pause the recording session on display %s",
mDisplayContent.getDisplayId());
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 251a087..fb6d036 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -6693,9 +6693,10 @@
/**
* Start recording if this DisplayContent no longer has content. Stop recording if it now
- * has content or the display is not on.
+ * has content or the display is not on. Update recording if the content has changed (for
+ * example, the user has granted consent to token re-use, so we can now start mirroring).
*/
- @VisibleForTesting void updateRecording() {
+ void updateRecording() {
if (mContentRecorder == null || !mContentRecorder.isContentRecordingSessionSet()) {
if (!setDisplayMirroring()) {
return;
diff --git a/services/core/jni/com_android_server_display_DisplayControl.cpp b/services/core/jni/com_android_server_display_DisplayControl.cpp
index 72829c0..5f95c28 100644
--- a/services/core/jni/com_android_server_display_DisplayControl.cpp
+++ b/services/core/jni/com_android_server_display_DisplayControl.cpp
@@ -99,7 +99,10 @@
// Extract unique HDR output types.
std::set<int> hdrOutputTypes;
for (const auto& hdrConversionCapability : hdrConversionCapabilities) {
- hdrOutputTypes.insert(hdrConversionCapability.outputType);
+ // Filter out the value for SDR which is 0.
+ if (hdrConversionCapability.outputType > 0) {
+ hdrOutputTypes.insert(hdrConversionCapability.outputType);
+ }
}
jintArray array = env->NewIntArray(hdrOutputTypes.size());
if (array == nullptr) {
diff --git a/services/core/jni/tvinput/JTvInputHal.cpp b/services/core/jni/tvinput/JTvInputHal.cpp
index a3eb390..6782b5c 100644
--- a/services/core/jni/tvinput/JTvInputHal.cpp
+++ b/services/core/jni/tvinput/JTvInputHal.cpp
@@ -16,6 +16,8 @@
#include "JTvInputHal.h"
+#include <nativehelper/ScopedLocalRef.h>
+
namespace android {
JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, std::shared_ptr<ITvInputWrapper> tvInput,
@@ -278,21 +280,27 @@
void JTvInputHal::onTvMessage(int deviceId, int streamId, AidlTvMessageEventType type,
AidlTvMessage& message, signed char data[], int dataLength) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
- jobject bundle = env->NewObject(gBundleClassInfo.clazz, gBundleClassInfo.constructor);
- const jsize len = static_cast<jsize>(dataLength);
- jbyteArray convertedData = env->NewByteArray(len);
- env->SetByteArrayRegion(convertedData, 0, len, reinterpret_cast<const jbyte*>(data));
- env->CallObjectMethod(bundle, gBundleClassInfo.putString,
- "android.media.tv.TvInputManager.subtype", message.subType.c_str());
- env->CallObjectMethod(bundle, gBundleClassInfo.putByteArray,
- "android.media.tv.TvInputManager.raw_data", convertedData);
- env->CallObjectMethod(bundle, gBundleClassInfo.putInt,
- "android.media.tv.TvInputManager.group_id", message.groupId);
- env->CallObjectMethod(bundle, gBundleClassInfo.putInt,
- "android.media.tv.TvInputManager.stream_id", streamId);
+ ScopedLocalRef<jobject> bundle(env,
+ env->NewObject(gBundleClassInfo.clazz,
+ gBundleClassInfo.constructor));
+ ScopedLocalRef<jbyteArray> convertedData(env, env->NewByteArray(dataLength));
+ env->SetByteArrayRegion(convertedData.get(), 0, dataLength, reinterpret_cast<jbyte*>(data));
+ std::string key = "android.media.tv.TvInputManager.raw_data";
+ ScopedLocalRef<jstring> jkey(env, env->NewStringUTF(key.c_str()));
+ env->CallVoidMethod(bundle.get(), gBundleClassInfo.putByteArray, jkey.get(),
+ convertedData.get());
+ ScopedLocalRef<jstring> subtype(env, env->NewStringUTF(message.subType.c_str()));
+ key = "android.media.tv.TvInputManager.subtype";
+ jkey = ScopedLocalRef<jstring>(env, env->NewStringUTF(key.c_str()));
+ env->CallVoidMethod(bundle.get(), gBundleClassInfo.putString, jkey.get(), subtype.get());
+ key = "android.media.tv.TvInputManager.group_id";
+ jkey = ScopedLocalRef<jstring>(env, env->NewStringUTF(key.c_str()));
+ env->CallVoidMethod(bundle.get(), gBundleClassInfo.putInt, jkey.get(), message.groupId);
+ key = "android.media.tv.TvInputManager.stream_id";
+ jkey = ScopedLocalRef<jstring>(env, env->NewStringUTF(key.c_str()));
+ env->CallVoidMethod(bundle.get(), gBundleClassInfo.putInt, jkey.get(), streamId);
env->CallVoidMethod(mThiz, gTvInputHalClassInfo.tvMessageReceived, deviceId,
- static_cast<int>(type), bundle);
- env->DeleteLocalRef(convertedData);
+ static_cast<jint>(type), bundle.get());
}
void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded) {
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index d9467a5..c7a71ee 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -39,7 +39,7 @@
"PackageManagerServiceHostTestsIntentVerifyUtils",
"block_device_writer_jar",
],
- test_suites: ["general-tests"],
+ test_suites: ["device-tests"],
data: [
":PackageManagerTestApex",
":PackageManagerTestApexApp",
diff --git a/services/tests/PackageManagerServiceTests/host/AndroidTest.xml b/services/tests/PackageManagerServiceTests/host/AndroidTest.xml
index 2382548..f594f6f 100644
--- a/services/tests/PackageManagerServiceTests/host/AndroidTest.xml
+++ b/services/tests/PackageManagerServiceTests/host/AndroidTest.xml
@@ -30,6 +30,7 @@
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="install-arg" value="-t" />
+ <option name="install-arg" value="-g" />
<option name="test-file-name" value="PackageManagerServiceServerTests.apk" />
</target_preparer>
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 8981160..021f2d1 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -323,6 +323,61 @@
}
@Test
+ public void testMultipleBrightnessEvents() {
+ final float brightnessOne = 0.2f;
+ final float brightnessTwo = 0.4f;
+ final float brightnessThree = 0.6f;
+ final float brightnessFour = 0.3f;
+ final String displayId = "1234";
+ final float[] luxValues = new float[]{1.0f};
+
+ startTracker(mTracker);
+ final long sensorTime = TimeUnit.NANOSECONDS.toMillis(mInjector.elapsedRealtimeNanos());
+ final long sensorTime2 = sensorTime + TimeUnit.SECONDS.toMillis(20);
+ final long sensorTime3 = sensorTime2 + TimeUnit.SECONDS.toMillis(30);
+ final long sensorTime4 = sensorTime3 + TimeUnit.SECONDS.toMillis(40);
+ final long originalTime = mInjector.currentTimeMillis();
+
+ mInjector.incrementTime(TimeUnit.SECONDS.toMillis(2));
+ notifyBrightnessChanged(mTracker, brightnessOne, displayId, luxValues,
+ new long[] {sensorTime});
+
+ mInjector.incrementTime(TimeUnit.SECONDS.toMillis(20));
+ notifyBrightnessChanged(mTracker, brightnessTwo, displayId, luxValues,
+ new long[] {sensorTime2});
+
+ mInjector.incrementTime(TimeUnit.SECONDS.toMillis(30));
+ notifyBrightnessChanged(mTracker, brightnessThree, displayId, luxValues,
+ new long[] {sensorTime3});
+
+ mInjector.incrementTime(TimeUnit.SECONDS.toMillis(40));
+ notifyBrightnessChanged(mTracker, brightnessFour, displayId, luxValues,
+ new long[] {sensorTime4});
+ mTracker.stop();
+ List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
+ assertEquals(4, events.size());
+ BrightnessChangeEvent eventOne = events.get(0);
+ assertEquals(brightnessOne, eventOne.brightness, FLOAT_DELTA);
+ assertEquals(originalTime,
+ eventOne.luxTimestamps[0]);
+
+ BrightnessChangeEvent eventTwo = events.get(1);
+ assertEquals(brightnessTwo, eventTwo.brightness, FLOAT_DELTA);
+ assertEquals(originalTime + TimeUnit.SECONDS.toMillis(20),
+ eventTwo.luxTimestamps[0]);
+
+ BrightnessChangeEvent eventThree = events.get(2);
+ assertEquals(brightnessThree, eventThree.brightness, FLOAT_DELTA);
+ assertEquals(originalTime + TimeUnit.SECONDS.toMillis(50),
+ eventThree.luxTimestamps[0]);
+
+ BrightnessChangeEvent eventFour = events.get(3);
+ assertEquals(brightnessFour, eventFour.brightness, FLOAT_DELTA);
+ assertEquals(originalTime + TimeUnit.SECONDS.toMillis(90),
+ eventFour.luxTimestamps[0]);
+ }
+
+ @Test
public void testBrightnessFullPopulatedEvent() {
final int initialBrightness = 230;
final int brightness = 130;
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index acfc073..5438750 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -1079,7 +1079,7 @@
verify(mMockProjectionService, atLeastOnce()).setContentRecordingSession(
mContentRecordingSessionCaptor.capture(), nullable(IMediaProjection.class));
ContentRecordingSession session = mContentRecordingSessionCaptor.getValue();
- assertThat(session.isWaitingToRecord()).isTrue();
+ assertThat(session.isWaitingForConsent()).isTrue();
}
@Test
@@ -1114,7 +1114,7 @@
assertThat(session.getContentToRecord()).isEqualTo(RECORD_CONTENT_DISPLAY);
assertThat(session.getVirtualDisplayId()).isEqualTo(displayId);
assertThat(session.getDisplayToRecord()).isEqualTo(displayToRecord);
- assertThat(session.isWaitingToRecord()).isFalse();
+ assertThat(session.isWaitingForConsent()).isFalse();
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index 275533f..b91a6cb 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -139,7 +139,7 @@
doReturn(mPackageManager).when(mContext).getPackageManager();
mClock = new OffsettableClock.Stopped();
- mWaitingDisplaySession.setWaitingToRecord(true);
+ mWaitingDisplaySession.setWaitingForConsent(true);
mWaitingDisplaySession.setVirtualDisplayId(5);
mAppInfo.targetSdkVersion = 32;
@@ -484,7 +484,7 @@
mSessionCaptor.capture());
// Examine latest value.
final ContentRecordingSession capturedSession = mSessionCaptor.getValue();
- assertThat(capturedSession.isWaitingToRecord()).isFalse();
+ assertThat(capturedSession.isWaitingForConsent()).isFalse();
assertThat(capturedSession.getVirtualDisplayId()).isEqualTo(INVALID_DISPLAY);
}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
index 76b6a82..d181419 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
@@ -23,8 +23,6 @@
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
-import static com.android.server.power.stats.wakeups.CpuWakeupStats.WAKEUP_REASON_HALF_WINDOW_MS;
-
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
@@ -55,7 +53,7 @@
private static final String KERNEL_REASON_SENSOR_IRQ = "15 test.sensor.device";
private static final String KERNEL_REASON_CELLULAR_DATA_IRQ = "18 test.cellular_data.device";
private static final String KERNEL_REASON_UNKNOWN_IRQ = "140 test.unknown.device";
- private static final String KERNEL_REASON_UNKNOWN = "free-form-reason test.alarm.device";
+ private static final String KERNEL_REASON_UNKNOWN_FORMAT = "free-form-reason test.alarm.device";
private static final String KERNEL_REASON_ALARM_ABNORMAL = "-1 test.alarm.device";
private static final String KERNEL_REASON_ABORT = "Abort: due to test.alarm.device";
@@ -85,30 +83,29 @@
@Test
public void removesOldWakeups() {
- // The xml resource doesn't matter for this test.
- final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_1, mHandler);
+ final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long retention = obj.mConfig.WAKEUP_STATS_RETENTION_MS;
final Set<Long> timestamps = new HashSet<>();
final long firstWakeup = 453192;
- obj.noteWakeupTimeAndReason(firstWakeup, 32, KERNEL_REASON_UNKNOWN_IRQ);
+ // Reasons do not matter for this test, as long as they map to known subsystems
+ obj.noteWakeupTimeAndReason(firstWakeup, 32, KERNEL_REASON_ALARM_IRQ);
timestamps.add(firstWakeup);
for (int i = 1; i < 1000; i++) {
final long delta = mRandom.nextLong(retention);
if (timestamps.add(firstWakeup + delta)) {
- obj.noteWakeupTimeAndReason(firstWakeup + delta, i, KERNEL_REASON_UNKNOWN_IRQ);
+ obj.noteWakeupTimeAndReason(firstWakeup + delta, i, KERNEL_REASON_SENSOR_IRQ);
}
}
assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size());
- obj.noteWakeupTimeAndReason(firstWakeup + retention + 1242, 231,
- KERNEL_REASON_UNKNOWN_IRQ);
+ obj.noteWakeupTimeAndReason(firstWakeup + retention, 231, KERNEL_REASON_WIFI_IRQ);
assertThat(obj.mWakeupEvents.size()).isEqualTo(timestamps.size());
for (int i = 0; i < 100; i++) {
final long now = mRandom.nextLong(retention + 1, 100 * retention);
- obj.noteWakeupTimeAndReason(now, i, KERNEL_REASON_UNKNOWN_IRQ);
+ obj.noteWakeupTimeAndReason(now, i, KERNEL_REASON_SOUND_TRIGGER_IRQ);
assertThat(obj.mWakeupEvents.closestIndexOnOrBefore(now - retention)).isLessThan(0);
}
}
@@ -201,9 +198,9 @@
// Outside the window, so should be ignored.
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
- wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
+ wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_1);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
- wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
+ wakeupTime + obj.mConfig.WAKEUP_MATCHING_WINDOW_MS + 1, TEST_UID_2);
// Should be attributed
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3, TEST_UID_5);
@@ -234,9 +231,9 @@
// Outside the window, so should be ignored.
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
- wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
+ wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_1);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
- wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
+ wakeupTime + obj.mConfig.WAKEUP_MATCHING_WINDOW_MS + 1, TEST_UID_2);
// Should be attributed
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER, wakeupTime + 5, TEST_UID_3,
TEST_UID_5);
@@ -268,9 +265,9 @@
// Outside the window, so should be ignored.
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI,
- wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
+ wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_1);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI,
- wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
+ wakeupTime + obj.mConfig.WAKEUP_MATCHING_WINDOW_MS + 1, TEST_UID_2);
// Should be attributed
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime + 3, TEST_UID_4, TEST_UID_5);
@@ -300,9 +297,9 @@
// Alarm activity
// Outside the window, so should be ignored.
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
- wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
+ wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_1);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM,
- wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
+ wakeupTime + obj.mConfig.WAKEUP_MATCHING_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,
@@ -311,9 +308,9 @@
// Wifi activity
// Outside the window, so should be ignored.
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI,
- wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_4);
+ wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_4);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI,
- wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_3);
+ wakeupTime + obj.mConfig.WAKEUP_MATCHING_WINDOW_MS + 1, TEST_UID_3);
// Should be attributed
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime + 2, TEST_UID_1);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 1, TEST_UID_2,
@@ -347,33 +344,67 @@
}
@Test
- public void unknownIrqAttribution() {
+ public void unknownIrqSoloIgnored() {
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long wakeupTime = 92123410;
obj.noteWakeupTimeAndReason(wakeupTime, 24, KERNEL_REASON_UNKNOWN_IRQ);
- assertThat(obj.mWakeupEvents.size()).isEqualTo(1);
+ assertThat(obj.mWakeupEvents.size()).isEqualTo(0);
- // Unrelated subsystems, should not be attributed
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 3, TEST_UID_4,
TEST_UID_5);
- final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
- assertThat(attribution).isNotNull();
- assertThat(attribution.size()).isEqualTo(1);
- assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isTrue();
- final SparseIntArray uidProcStates = attribution.get(CPU_WAKEUP_SUBSYSTEM_UNKNOWN);
- assertThat(uidProcStates == null || uidProcStates.size() == 0).isTrue();
+ // Any nearby activity should not end up in the attribution map.
+ assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
}
@Test
- public void unknownWakeupIgnored() {
+ public void unknownAndWifiAttribution() {
+ final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
+ final long wakeupTime = 92123410;
+
+ populateDefaultProcStates(obj);
+
+ obj.noteWakeupTimeAndReason(wakeupTime, 24,
+ KERNEL_REASON_UNKNOWN_IRQ + ":" + KERNEL_REASON_WIFI_IRQ);
+
+ // Wifi activity
+ // Outside the window, so should be ignored.
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI,
+ wakeupTime - obj.mConfig.WAKEUP_MATCHING_WINDOW_MS - 1, TEST_UID_4);
+ // Should be attributed
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime + 2, TEST_UID_1);
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 1, TEST_UID_3,
+ TEST_UID_5);
+
+ // Unrelated, should be ignored.
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
+
+ final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+ assertThat(attribution).isNotNull();
+ assertThat(attribution.size()).isEqualTo(2);
+ assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_WIFI)).isTrue();
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_1)).isEqualTo(
+ TEST_PROC_STATE_1);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_2)).isLessThan(0);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_3)).isEqualTo(
+ TEST_PROC_STATE_3);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_4)).isLessThan(0);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo(
+ TEST_PROC_STATE_5);
+ assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isTrue();
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isNull();
+ assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isFalse();
+ }
+
+ @Test
+ public void unknownFormatWakeupIgnored() {
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long wakeupTime = 72123210;
- obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_UNKNOWN);
+ obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_UNKNOWN_FORMAT);
// Should be ignored as this type of wakeup is not known.
assertThat(obj.mWakeupEvents.size()).isEqualTo(0);
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/WakingActivityHistoryTest.java b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/WakingActivityHistoryTest.java
index cba7dbe..b02618e 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/WakingActivityHistoryTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/WakingActivityHistoryTest.java
@@ -28,8 +28,11 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.concurrent.ThreadLocalRandom;
+
@RunWith(AndroidJUnit4.class)
public class WakingActivityHistoryTest {
+ private volatile long mTestRetention = 54;
private static boolean areSame(SparseIntArray a, SparseIntArray b) {
if (a == b) {
@@ -55,7 +58,7 @@
@Test
public void recordActivityAppendsUids() {
- final WakingActivityHistory history = new WakingActivityHistory();
+ final WakingActivityHistory history = new WakingActivityHistory(() -> Long.MAX_VALUE);
final int subsystem = 42;
final long timestamp = 54;
@@ -100,7 +103,7 @@
@Test
public void recordActivityDoesNotDeleteExistingUids() {
- final WakingActivityHistory history = new WakingActivityHistory();
+ final WakingActivityHistory history = new WakingActivityHistory(() -> Long.MAX_VALUE);
final int subsystem = 42;
long timestamp = 101;
@@ -151,4 +154,120 @@
assertThat(recordedUids.get(62, -1)).isEqualTo(31);
assertThat(recordedUids.get(85, -1)).isEqualTo(39);
}
+
+ @Test
+ public void removeBetween() {
+ final WakingActivityHistory history = new WakingActivityHistory(() -> Long.MAX_VALUE);
+
+ final int subsystem = 43;
+
+ final SparseIntArray uids = new SparseIntArray();
+ uids.put(1, 17);
+ uids.put(15, 2);
+ uids.put(62, 31);
+ history.recordActivity(subsystem, 123, uids);
+
+ uids.put(54, 91);
+ history.recordActivity(subsystem, 150, uids);
+
+ uids.put(101, 32);
+ uids.delete(1);
+ history.recordActivity(subsystem, 191, uids);
+
+ SparseIntArray removedUids = history.removeBetween(subsystem, 100, 122);
+ assertThat(removedUids).isNull();
+ assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(3);
+
+ removedUids = history.removeBetween(subsystem, 124, 149);
+ assertThat(removedUids).isNull();
+ assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(3);
+
+ removedUids = history.removeBetween(subsystem, 151, 190);
+ assertThat(removedUids).isNull();
+ assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(3);
+
+ removedUids = history.removeBetween(subsystem, 192, 240);
+ assertThat(removedUids).isNull();
+ assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(3);
+
+
+ // Removing from a different subsystem should do nothing.
+ removedUids = history.removeBetween(subsystem + 1, 0, 300);
+ assertThat(removedUids).isNull();
+ assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(3);
+
+ removedUids = history.removeBetween(subsystem, 0, 300);
+ assertThat(removedUids.size()).isEqualTo(5);
+ assertThat(removedUids.get(1, -1)).isEqualTo(17);
+ assertThat(removedUids.get(15, -1)).isEqualTo(2);
+ assertThat(removedUids.get(62, -1)).isEqualTo(31);
+ assertThat(removedUids.get(54, -1)).isEqualTo(91);
+ assertThat(removedUids.get(101, -1)).isEqualTo(32);
+
+ assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(0);
+
+ history.recordActivity(subsystem, 23, uids);
+ uids.put(31, 123);
+ history.recordActivity(subsystem, 49, uids);
+ uids.put(177, 432);
+ history.recordActivity(subsystem, 89, uids);
+
+ removedUids = history.removeBetween(subsystem, 23, 23);
+ assertThat(removedUids.size()).isEqualTo(4);
+ assertThat(removedUids.get(15, -1)).isEqualTo(2);
+ assertThat(removedUids.get(62, -1)).isEqualTo(31);
+ assertThat(removedUids.get(54, -1)).isEqualTo(91);
+ assertThat(removedUids.get(101, -1)).isEqualTo(32);
+
+ assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(2);
+
+ removedUids = history.removeBetween(subsystem, 49, 54);
+ assertThat(removedUids.size()).isEqualTo(5);
+ assertThat(removedUids.get(15, -1)).isEqualTo(2);
+ assertThat(removedUids.get(62, -1)).isEqualTo(31);
+ assertThat(removedUids.get(54, -1)).isEqualTo(91);
+ assertThat(removedUids.get(101, -1)).isEqualTo(32);
+ assertThat(removedUids.get(31, -1)).isEqualTo(123);
+
+ assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(1);
+
+ removedUids = history.removeBetween(subsystem, 23, 89);
+ assertThat(removedUids.size()).isEqualTo(6);
+ assertThat(removedUids.get(15, -1)).isEqualTo(2);
+ assertThat(removedUids.get(62, -1)).isEqualTo(31);
+ assertThat(removedUids.get(54, -1)).isEqualTo(91);
+ assertThat(removedUids.get(101, -1)).isEqualTo(32);
+ assertThat(removedUids.get(31, -1)).isEqualTo(123);
+ assertThat(removedUids.get(177, -1)).isEqualTo(432);
+
+ assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(0);
+ }
+
+ @Test
+ public void deletesActivityPastRetention() {
+ final WakingActivityHistory history = new WakingActivityHistory(() -> mTestRetention);
+ final int subsystem = 49;
+
+ mTestRetention = 454;
+
+ final long firstTime = 342;
+ for (int i = 0; i < mTestRetention; i++) {
+ history.recordActivity(subsystem, firstTime + i, new SparseIntArray());
+ }
+ assertThat(history.mWakingActivity.get(subsystem)).isNotNull();
+ assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(mTestRetention);
+
+ history.recordActivity(subsystem, firstTime + mTestRetention + 7, new SparseIntArray());
+ assertThat(history.mWakingActivity.get(subsystem).size()).isEqualTo(mTestRetention - 7);
+
+ final ThreadLocalRandom random = ThreadLocalRandom.current();
+
+ for (int i = 0; i < 100; i++) {
+ final long time = random.nextLong(firstTime + mTestRetention + 100,
+ 456 * mTestRetention);
+ history.recordActivity(subsystem, time, new SparseIntArray());
+ assertThat(history.mWakingActivity.get(subsystem).closestIndexOnOrBefore(
+ time - mTestRetention)).isLessThan(0);
+ }
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index ad9f710..bbec091 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -79,6 +79,8 @@
private Task mTask;
private final ContentRecordingSession mDisplaySession =
ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
+ private final ContentRecordingSession mWaitingDisplaySession =
+ ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
private ContentRecordingSession mTaskSession;
private static Point sSurfaceSize;
private ContentRecorder mContentRecorder;
@@ -120,6 +122,10 @@
mTaskSession = ContentRecordingSession.createTaskSession(sTaskWindowContainerToken);
mTaskSession.setVirtualDisplayId(displayId);
+ // GIVEN a session is waiting for the user to review consent.
+ mWaitingDisplaySession.setVirtualDisplayId(displayId);
+ mWaitingDisplaySession.setWaitingForConsent(true);
+
mConfigListener = new ConfigListener();
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
mContext.getMainExecutor(), mConfigListener);
@@ -221,6 +227,18 @@
}
@Test
+ public void testUpdateRecording_waitingForConsent() {
+ mContentRecorder.setContentRecordingSession(mWaitingDisplaySession);
+ mContentRecorder.updateRecording();
+ assertThat(mContentRecorder.isCurrentlyRecording()).isFalse();
+
+
+ mContentRecorder.setContentRecordingSession(mDisplaySession);
+ mContentRecorder.updateRecording();
+ assertThat(mContentRecorder.isCurrentlyRecording()).isTrue();
+ }
+
+ @Test
public void testOnConfigurationChanged_neverRecording() {
mContentRecorder.onConfigurationChanged(ORIENTATION_PORTRAIT);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java
index 6cda038..52226c2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecordingControllerTests.java
@@ -24,10 +24,8 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.view.ContentRecordingSession;
@@ -36,6 +34,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
/**
* Tests for the {@link ContentRecordingController} class.
@@ -49,12 +49,20 @@
public class ContentRecordingControllerTests extends WindowTestsBase {
private final ContentRecordingSession mDefaultSession =
ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
+ private final ContentRecordingSession mWaitingDisplaySession =
+ ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
private int mVirtualDisplayId;
private DisplayContent mVirtualDisplayContent;
+ private WindowContainer.RemoteToken mRootTaskToken;
+
+ @Mock
+ private WindowContainer mTaskWindowContainer;
@Before
public void setup() {
+ MockitoAnnotations.initMocks(this);
+
// GIVEN the VirtualDisplay associated with the session (so the display has state ON).
mVirtualDisplayContent = new TestDisplayContent.Builder(mAtm, 500, 600).build();
mVirtualDisplayId = mVirtualDisplayContent.getDisplayId();
@@ -62,6 +70,11 @@
spyOn(mVirtualDisplayContent);
mDefaultSession.setVirtualDisplayId(mVirtualDisplayId);
+ mWaitingDisplaySession.setVirtualDisplayId(mVirtualDisplayId);
+ mWaitingDisplaySession.setWaitingForConsent(true);
+
+ mRootTaskToken = new WindowContainer.RemoteToken(mTaskWindowContainer);
+ mTaskWindowContainer.mRemoteToken = mRootTaskToken;
}
@Test
@@ -92,7 +105,7 @@
}
@Test
- public void testSetContentRecordingSessionLocked_newDisplaySession_accepted() {
+ public void testSetContentRecordingSessionLocked_newSession_accepted() {
ContentRecordingController controller = new ContentRecordingController();
// GIVEN a valid display session.
// WHEN updating the session.
@@ -104,15 +117,37 @@
}
@Test
- public void testSetContentRecordingSessionLocked_updateCurrentDisplaySession_notAccepted() {
+ public void testSetContentRecordingSessionLocked_updateSession_noLongerWaiting_accepted() {
+ ContentRecordingController controller = new ContentRecordingController();
+ // GIVEN a valid display session already in place.
+ controller.setContentRecordingSessionLocked(mWaitingDisplaySession, mWm);
+ verify(mVirtualDisplayContent, atLeastOnce()).setContentRecordingSession(
+ mWaitingDisplaySession);
+
+ // WHEN updating the session on the same display, so no longer waiting to record.
+ ContentRecordingSession sessionUpdate = ContentRecordingSession.createTaskSession(
+ mRootTaskToken.toWindowContainerToken().asBinder());
+ sessionUpdate.setVirtualDisplayId(mVirtualDisplayId);
+ sessionUpdate.setWaitingForConsent(false);
+ controller.setContentRecordingSessionLocked(sessionUpdate, mWm);
+
+ ContentRecordingSession resultingSession = controller.getContentRecordingSessionLocked();
+ // THEN the session was accepted.
+ assertThat(resultingSession).isEqualTo(sessionUpdate);
+ verify(mVirtualDisplayContent, atLeastOnce()).setContentRecordingSession(sessionUpdate);
+ verify(mVirtualDisplayContent).updateRecording();
+ }
+
+ @Test
+ public void testSetContentRecordingSessionLocked_invalidUpdateSession_notWaiting_notAccepted() {
ContentRecordingController controller = new ContentRecordingController();
// GIVEN a valid display session already in place.
controller.setContentRecordingSessionLocked(mDefaultSession, mWm);
verify(mVirtualDisplayContent, atLeastOnce()).setContentRecordingSession(mDefaultSession);
// WHEN updating the session on the same display.
- ContentRecordingSession sessionUpdate =
- ContentRecordingSession.createTaskSession(mock(IBinder.class));
+ ContentRecordingSession sessionUpdate = ContentRecordingSession.createTaskSession(
+ mRootTaskToken.toWindowContainerToken().asBinder());
sessionUpdate.setVirtualDisplayId(mVirtualDisplayId);
controller.setContentRecordingSessionLocked(sessionUpdate, mWm);
@@ -123,7 +158,7 @@
}
@Test
- public void testSetContentRecordingSessionLocked_disableCurrentDisplaySession_accepted() {
+ public void testSetContentRecordingSessionLocked_disableCurrentSession_accepted() {
ContentRecordingController controller = new ContentRecordingController();
// GIVEN a valid display session already in place.
controller.setContentRecordingSessionLocked(mDefaultSession, mWm);
@@ -141,7 +176,7 @@
}
@Test
- public void testSetContentRecordingSessionLocked_takeOverCurrentDisplaySession_accepted() {
+ public void testSetContentRecordingSessionLocked_takeOverCurrentSession_accepted() {
ContentRecordingController controller = new ContentRecordingController();
// GIVEN a valid display session already in place.
controller.setContentRecordingSessionLocked(mDefaultSession, mWm);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 72f2c1d..cf236dd 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -2540,10 +2540,15 @@
@Override
public void reportChooserSelection(@NonNull String packageName, int userId,
- String contentType, String[] annotations, String action) {
+ @NonNull String contentType, String[] annotations, @NonNull String action) {
if (packageName == null) {
throw new IllegalArgumentException("Package selection must not be null.");
}
+ // A valid contentType and action must be provided for chooser selection events.
+ if (contentType == null || contentType.isBlank()
+ || action == null || action.isBlank()) {
+ return;
+ }
// Verify if this package exists before reporting an event for it.
if (mPackageManagerInternal.getPackageUid(packageName, 0, userId) < 0) {
Slog.w(TAG, "Event report user selecting an invalid package");
diff --git a/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java b/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java
index 7d9a6a5..d16e90e 100644
--- a/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java
+++ b/tests/Internal/src/com/android/internal/app/AppLocaleCollectorTest.java
@@ -37,9 +37,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
import java.util.Locale;
import java.util.Set;
@@ -54,7 +54,7 @@
private LocaleStore.LocaleInfo mAppCurrentLocale;
private Set<LocaleInfo> mAllAppActiveLocales;
private Set<LocaleInfo> mImeLocales;
- private List<LocaleInfo> mSystemCurrentLocales;
+ private Set<LocaleInfo> mSystemCurrentLocales;
private Set<LocaleInfo> mSystemSupportedLocales;
private AppLocaleStore.AppLocaleResult mResult;
private static final String PKG1 = "pkg1";
@@ -73,14 +73,6 @@
public void setUp() throws Exception {
mAppLocaleCollector = spy(
new AppLocaleCollector(InstrumentationRegistry.getContext(), PKG1));
-
- mAppCurrentLocale = createLocaleInfo("en-US", CURRENT);
- mAllAppActiveLocales = initAllAppActivatedLocales();
- mImeLocales = initImeLocales();
- mSystemSupportedLocales = initSystemSupportedLocales();
- mSystemCurrentLocales = initSystemCurrentLocales();
- mResult = new AppLocaleStore.AppLocaleResult(GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG,
- initAppSupportedLocale());
}
@Test
@@ -88,7 +80,7 @@
LocaleList.setDefault(
LocaleList.forLanguageTags("en-US-u-mu-fahrenhe,ar-JO-u-mu-fahrenhe-nu-latn"));
- List<LocaleStore.LocaleInfo> list =
+ Set<LocaleStore.LocaleInfo> list =
mAppLocaleCollector.getSystemCurrentLocales();
LocaleList expected = LocaleList.forLanguageTags("en-US,ar-JO-u-nu-latn");
@@ -99,7 +91,69 @@
}
@Test
- public void testGetSupportedLocaleList() {
+ public void testGetSupportedLocaleList_filterNonAppsupportedSystemLanguage() {
+ mAppCurrentLocale = createLocaleInfo("en-US", CURRENT);
+
+ // App supports five locales
+ HashSet<Locale> appSupported =
+ getAppSupportedLocales(new String[] {
+ "en-US",
+ "fr",
+ "ar",
+ "es",
+ "bn"
+ });
+ // There are six locales in system current locales.
+ mSystemCurrentLocales = getSystemCurrentLocales(new String[] {
+ "en-US",
+ "fr-FR",
+ "ar-JO",
+ "ca-AD",
+ "da-DK",
+ "es-US"
+ });
+ mAllAppActiveLocales = Collections.emptySet();
+ mImeLocales = Collections.emptySet();
+ mSystemSupportedLocales = Collections.emptySet();
+ mResult = new AppLocaleStore.AppLocaleResult(GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG,
+ appSupported);
+
+ doReturn(mAppCurrentLocale).when(mAppLocaleCollector).getAppCurrentLocale();
+ doReturn(mResult).when(mAppLocaleCollector).getAppSupportedLocales();
+ doReturn(mAllAppActiveLocales).when(mAppLocaleCollector).getAllAppActiveLocales();
+ doReturn(mImeLocales).when(mAppLocaleCollector).getActiveImeLocales();
+ doReturn(mSystemSupportedLocales).when(mAppLocaleCollector).getSystemSupportedLocale(
+ anyObject(), eq(null), eq(true));
+ doReturn(mSystemCurrentLocales).when(
+ mAppLocaleCollector).getSystemCurrentLocales();
+
+ Set<LocaleInfo> result = mAppLocaleCollector.getSupportedLocaleList(null, true, false);
+
+ // The result would show four rather than six locales in the suggested region.
+ HashMap<String, Integer> expectedResult = new HashMap<>();
+ expectedResult.put("en-US", CURRENT); // The locale current App activates.
+ expectedResult.put("ar-JO", SYSTEM_AVAILABLE);
+ expectedResult.put("fr-FR", SYSTEM_AVAILABLE);
+ expectedResult.put("es-US", SYSTEM_AVAILABLE);
+ expectedResult.put(createLocaleInfo("", SYSTEM).getId(), SYSTEM); // System language title
+
+ assertEquals(result.size(), expectedResult.size());
+ for (LocaleStore.LocaleInfo info: result) {
+ int suggestionFlags = expectedResult.getOrDefault(info.getId(), -1);
+ assertEquals(info.mSuggestionFlags, suggestionFlags);
+ }
+ }
+
+ @Test
+ public void testGetSupportedLocaleList_withActiveLocalesFromOtherAppAndIme() {
+ mAppCurrentLocale = createLocaleInfo("en-US", CURRENT);
+ mAllAppActiveLocales = initAllAppActivatedLocales();
+ mImeLocales = initImeLocales();
+ mSystemSupportedLocales = initSystemSupportedLocales();
+ mSystemCurrentLocales = initSystemCurrentLocales();
+ mResult = new AppLocaleStore.AppLocaleResult(GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG,
+ initAppSupportedLocale());
+
doReturn(mAppCurrentLocale).when(mAppLocaleCollector).getAppCurrentLocale();
doReturn(mResult).when(mAppLocaleCollector).getAppSupportedLocales();
doReturn(mAllAppActiveLocales).when(mAppLocaleCollector).getAllAppActiveLocales();
@@ -147,8 +201,8 @@
);
}
- private List<LocaleInfo> initSystemCurrentLocales() {
- return List.of(createLocaleInfo("zh-Hant-TW", SYSTEM_AVAILABLE),
+ private Set<LocaleInfo> initSystemCurrentLocales() {
+ return Set.of(createLocaleInfo("zh-Hant-TW", SYSTEM_AVAILABLE),
createLocaleInfo("ja-JP", SYSTEM_AVAILABLE),
// will be filtered because current App activates this locale.
createLocaleInfo("en-US", SYSTEM_AVAILABLE));
@@ -188,6 +242,22 @@
return hs;
}
+ private Set<LocaleStore.LocaleInfo> getSystemCurrentLocales(String []languageTags) {
+ HashSet<LocaleStore.LocaleInfo> hs = new HashSet<>(languageTags.length);
+ for (String tag:languageTags) {
+ hs.add(createLocaleInfo(tag, SYSTEM_AVAILABLE));
+ }
+ return hs;
+ }
+
+ private HashSet<Locale> getAppSupportedLocales(String []languageTags) {
+ HashSet<Locale> hs = new HashSet<>(languageTags.length);
+ for (String language:languageTags) {
+ hs.add(Locale.forLanguageTag(language));
+ }
+ return hs;
+ }
+
private LocaleInfo createLocaleInfo(String languageTag, int suggestionFlag) {
LocaleInfo localeInfo = LocaleStore.fromLocale(Locale.forLanguageTag(languageTag));
localeInfo.mSuggestionFlags = suggestionFlag;