Merge changes I339886d1,Id53bfc8c into udc-dev
* changes:
Fix themes of the bubble manage button
Revert "Use ComponentCallbacks instead of ConfigurationChangeListener"
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/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 3f552b6..ea7460d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -2600,34 +2600,43 @@
} else {
numSystemStops++;
}
- final int backoffAttempts = Math.max(1,
- numFailures + numSystemStops / mConstants.SYSTEM_STOP_TO_FAILURE_RATIO);
- long delayMillis;
+ final int backoffAttempts =
+ numFailures + numSystemStops / mConstants.SYSTEM_STOP_TO_FAILURE_RATIO;
+ final long earliestRuntimeMs;
- switch (job.getBackoffPolicy()) {
- case JobInfo.BACKOFF_POLICY_LINEAR: {
- long backoff = initialBackoffMillis;
- if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME_MS) {
- backoff = mConstants.MIN_LINEAR_BACKOFF_TIME_MS;
+ if (backoffAttempts == 0) {
+ earliestRuntimeMs = JobStatus.NO_EARLIEST_RUNTIME;
+ } else {
+ long delayMillis;
+ switch (job.getBackoffPolicy()) {
+ case JobInfo.BACKOFF_POLICY_LINEAR: {
+ long backoff = initialBackoffMillis;
+ if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME_MS) {
+ backoff = mConstants.MIN_LINEAR_BACKOFF_TIME_MS;
+ }
+ delayMillis = backoff * backoffAttempts;
}
- delayMillis = backoff * backoffAttempts;
- } break;
- default:
- if (DEBUG) {
- Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
+ break;
+ default:
+ if (DEBUG) {
+ Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
+ }
+ // Intentional fallthrough.
+ case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
+ long backoff = initialBackoffMillis;
+ if (backoff < mConstants.MIN_EXP_BACKOFF_TIME_MS) {
+ backoff = mConstants.MIN_EXP_BACKOFF_TIME_MS;
+ }
+ delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
}
- case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
- long backoff = initialBackoffMillis;
- if (backoff < mConstants.MIN_EXP_BACKOFF_TIME_MS) {
- backoff = mConstants.MIN_EXP_BACKOFF_TIME_MS;
- }
- delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
- } break;
+ break;
+ }
+ delayMillis =
+ Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
+ earliestRuntimeMs = elapsedNowMillis + delayMillis;
}
- delayMillis =
- Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
JobStatus newJob = new JobStatus(failureToReschedule,
- elapsedNowMillis + delayMillis,
+ earliestRuntimeMs,
JobStatus.NO_LATEST_RUNTIME, numFailures, numSystemStops,
failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis(),
failureToReschedule.getCumulativeExecutionTimeMs());
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/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index d85b2cd..0e78275 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
+import android.os.UserManager.EnforcingUser;
import java.util.List;
import java.util.Set;
@@ -326,4 +327,10 @@
*/
public abstract List<Bundle> getApplicationRestrictionsPerAdminForUser(
String packageName, @UserIdInt int userId);
+
+ /**
+ * Returns a list of users who set a user restriction on a given user.
+ */
+ public abstract List<EnforcingUser> getUserRestrictionSources(String restriction,
+ @UserIdInt int userId);
}
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/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 820bb1b..4f6bcb6 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -185,37 +185,41 @@
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
try {
for (File file : files) {
- if (isApkFile(file)) {
- final ParseResult<ApkLite> result = parseApkLite(input, file, flags);
- if (result.isError()) {
- return input.error(result);
- }
+ if (!isApkFile(file)) {
+ continue;
+ }
- final ApkLite lite = result.getResult();
- // Assert that all package names and version codes are
- // consistent with the first one we encounter.
- if (packageName == null) {
- packageName = lite.getPackageName();
- versionCode = lite.getVersionCode();
- } else {
- if (!packageName.equals(lite.getPackageName())) {
- return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Inconsistent package " + lite.getPackageName() + " in " + file
- + "; expected " + packageName);
- }
- if (versionCode != lite.getVersionCode()) {
- return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Inconsistent version " + lite.getVersionCode() + " in " + file
- + "; expected " + versionCode);
- }
- }
+ final ParseResult<ApkLite> result = parseApkLite(input, file, flags);
+ if (result.isError()) {
+ return input.error(result);
+ }
- // Assert that each split is defined only oncuses-static-libe
- if (apks.put(lite.getSplitName(), lite) != null) {
+ final ApkLite lite = result.getResult();
+ // Assert that all package names and version codes are
+ // consistent with the first one we encounter.
+ if (packageName == null) {
+ packageName = lite.getPackageName();
+ versionCode = lite.getVersionCode();
+ } else {
+ if (!packageName.equals(lite.getPackageName())) {
return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Split name " + lite.getSplitName()
- + " defined more than once; most recent was " + file);
+ "Inconsistent package " + lite.getPackageName() + " in " + file
+ + "; expected " + packageName);
}
+ if (versionCode != lite.getVersionCode()) {
+ return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Inconsistent version " + lite.getVersionCode() + " in " + file
+ + "; expected " + versionCode);
+ }
+ }
+
+ // Assert that each split is defined only once
+ ApkLite prev = apks.put(lite.getSplitName(), lite);
+ if (prev != null) {
+ return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
+ "Split name " + lite.getSplitName()
+ + " defined more than once; most recent was " + file
+ + ", previous was " + prev.getPath());
}
}
baseApk = apks.remove(null);
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index e908ced..48b5cac 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -212,14 +212,7 @@
@GuardedBy("mLock")
private boolean mFoldedDeviceState;
- private final CameraManager.DeviceStateListener mFoldStateListener =
- new CameraManager.DeviceStateListener() {
- @Override
- public final void onDeviceStateChanged(boolean folded) {
- synchronized (mLock) {
- mFoldedDeviceState = folded;
- }
- }};
+ private CameraManager.DeviceStateListener mFoldStateListener;
private static final String TAG = "CameraCharacteristics";
@@ -245,7 +238,18 @@
/**
* Return the device state listener for this Camera characteristics instance
*/
- CameraManager.DeviceStateListener getDeviceStateListener() { return mFoldStateListener; }
+ CameraManager.DeviceStateListener getDeviceStateListener() {
+ if (mFoldStateListener == null) {
+ mFoldStateListener = new CameraManager.DeviceStateListener() {
+ @Override
+ public final void onDeviceStateChanged(boolean folded) {
+ synchronized (mLock) {
+ mFoldedDeviceState = folded;
+ }
+ }};
+ }
+ return mFoldStateListener;
+ }
/**
* Overrides the property value
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 51501b5..85f8ca6 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1836,6 +1836,7 @@
ctx.getSystemService(DeviceStateManager.class).registerCallback(
new HandlerExecutor(mDeviceStateHandler), mFoldStateListener);
} catch (IllegalStateException e) {
+ mFoldStateListener = null;
Log.v(TAG, "Failed to register device state listener!");
Log.v(TAG, "Device state dependent characteristics updates will not be" +
"functional!");
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/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index 59f120d..4d87c95 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -61,6 +61,9 @@
@VisibleForTesting
final ActivityEmbeddingAnimationSpec mAnimationSpec;
+ @Nullable
+ private Animator mActiveAnimator;
+
ActivityEmbeddingAnimationRunner(@NonNull Context context,
@NonNull ActivityEmbeddingController controller) {
mController = controller;
@@ -75,8 +78,10 @@
// applied to make sure the surface is ready.
final List<Consumer<SurfaceControl.Transaction>> postStartTransactionCallbacks =
new ArrayList<>();
- final Animator animator = createAnimator(info, startTransaction, finishTransaction,
+ final Animator animator = createAnimator(info, startTransaction,
+ finishTransaction,
() -> mController.onAnimationFinished(transition), postStartTransactionCallbacks);
+ mActiveAnimator = animator;
// Start the animation.
if (!postStartTransactionCallbacks.isEmpty()) {
@@ -98,6 +103,17 @@
}
}
+ void cancelAnimationFromMerge() {
+ if (mActiveAnimator == null) {
+ Log.e(TAG,
+ "No active ActivityEmbedding animator running but mergeAnimation is "
+ + "trying to cancel one."
+ );
+ return;
+ }
+ mActiveAnimator.end();
+ }
+
/**
* Sets transition animation scale settings value.
* @param scale The setting value of transition animation scale.
@@ -153,6 +169,7 @@
adapter.onAnimationEnd(t);
}
t.apply();
+ mActiveAnimator = null;
animationFinishCallback.run();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
index bfbddbb..fbdbd3e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
@@ -118,6 +118,13 @@
return true;
}
+ @Override
+ public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ mAnimationRunner.cancelAnimationFromMerge();
+ }
+
private boolean handleNonEmbeddedChanges(List<TransitionInfo.Change> changes) {
final Rect nonClosingEmbeddedArea = new Rect();
for (int i = changes.size() - 1; i >= 0; i--) {
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/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
index a625346..4fca8b4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
@@ -65,12 +65,14 @@
final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
.addChange(createChange(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY))
.build();
- doReturn(mAnimator).when(mAnimRunner).createAnimator(any(), any(), any(), any(), any());
+ doReturn(mAnimator).when(mAnimRunner).createAnimator(any(), any(), any(), any(),
+ any());
mAnimRunner.startAnimation(mTransition, info, mStartTransaction, mFinishTransaction);
final ArgumentCaptor<Runnable> finishCallback = ArgumentCaptor.forClass(Runnable.class);
- verify(mAnimRunner).createAnimator(eq(info), eq(mStartTransaction), eq(mFinishTransaction),
+ verify(mAnimRunner).createAnimator(eq(info), eq(mStartTransaction),
+ eq(mFinishTransaction),
finishCallback.capture(), any());
verify(mStartTransaction).apply();
verify(mAnimator).start();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java
index 4f4f356..ab1ccd4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java
@@ -47,6 +47,7 @@
@Mock
ShellInit mShellInit;
+
@Mock
Transitions mTransitions;
@Mock
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
index b8f615a..ba34f1f7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
@@ -29,9 +29,13 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import android.animation.Animator;
+import android.animation.ValueAnimator;
import android.graphics.Rect;
+import android.view.SurfaceControl;
import android.window.TransitionInfo;
+import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -58,7 +62,8 @@
@Before
public void setup() {
super.setUp();
- doReturn(mAnimator).when(mAnimRunner).createAnimator(any(), any(), any(), any(), any());
+ doReturn(mAnimator).when(mAnimRunner).createAnimator(any(), any(), any(), any(),
+ any());
}
@Test
@@ -182,6 +187,44 @@
verifyNoMoreInteractions(mFinishTransaction);
}
+ @UiThreadTest
+ @Test
+ public void testMergeAnimation() {
+ final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
+ .addChange(createEmbeddedChange(
+ EMBEDDED_LEFT_BOUNDS, EMBEDDED_LEFT_BOUNDS, TASK_BOUNDS))
+ .build();
+
+ final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+ animator.addListener(new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mController.onAnimationFinished(mTransition);
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ });
+ doReturn(animator).when(mAnimRunner).createAnimator(any(), any(), any(), any(), any());
+ mController.startAnimation(mTransition, info, mStartTransaction,
+ mFinishTransaction, mFinishCallback);
+ verify(mFinishCallback, never()).onTransitionFinished(any(), any());
+ mController.mergeAnimation(mTransition, info, new SurfaceControl.Transaction(),
+ mTransition,
+ (wct, cb) -> {
+ });
+ verify(mFinishCallback).onTransitionFinished(any(), any());
+ }
+
@Test
public void testOnAnimationFinished() {
// Should not call finish when there is no transition.
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/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 6f67d68..1b04f18 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -372,13 +372,12 @@
void LnbClientCallbackImpl::onEvent(const LnbEventType lnbEventType) {
ALOGV("LnbClientCallbackImpl::onEvent, type=%d", lnbEventType);
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jobject lnb(env->NewLocalRef(mLnbObj));
- if (!env->IsSameObject(lnb, nullptr)) {
+ ScopedLocalRef lnb(env, env->NewLocalRef(mLnbObj));
+ if (!env->IsSameObject(lnb.get(), nullptr)) {
env->CallVoidMethod(
- lnb,
+ lnb.get(),
gFields.onLnbEventID,
(jint)lnbEventType);
- env->DeleteLocalRef(lnb);
} else {
ALOGE("LnbClientCallbackImpl::onEvent:"
"Lnb object has been freed. Ignoring callback.");
@@ -388,17 +387,15 @@
void LnbClientCallbackImpl::onDiseqcMessage(const vector<uint8_t> &diseqcMessage) {
ALOGV("LnbClientCallbackImpl::onDiseqcMessage");
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jobject lnb(env->NewLocalRef(mLnbObj));
- if (!env->IsSameObject(lnb, nullptr)) {
- jbyteArray array = env->NewByteArray(diseqcMessage.size());
- env->SetByteArrayRegion(array, 0, diseqcMessage.size(),
+ ScopedLocalRef lnb(env, env->NewLocalRef(mLnbObj));
+ if (!env->IsSameObject(lnb.get(), nullptr)) {
+ ScopedLocalRef array(env, env->NewByteArray(diseqcMessage.size()));
+ env->SetByteArrayRegion(array.get(), 0, diseqcMessage.size(),
reinterpret_cast<const jbyte *>(&diseqcMessage[0]));
env->CallVoidMethod(
- lnb,
+ lnb.get(),
gFields.onLnbDiseqcMessageID,
- array);
- env->DeleteLocalRef(lnb);
- env->DeleteLocalRef(array);
+ array.get());
} else {
ALOGE("LnbClientCallbackImpl::onDiseqcMessage:"
"Lnb object has been freed. Ignoring callback.");
@@ -422,10 +419,9 @@
void DvrClientCallbackImpl::onRecordStatus(RecordStatus status) {
ALOGV("DvrClientCallbackImpl::onRecordStatus");
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jobject dvr(env->NewLocalRef(mDvrObj));
- if (!env->IsSameObject(dvr, nullptr)) {
- env->CallVoidMethod(dvr, gFields.onDvrRecordStatusID, (jint)status);
- env->DeleteLocalRef(dvr);
+ ScopedLocalRef dvr(env, env->NewLocalRef(mDvrObj));
+ if (!env->IsSameObject(dvr.get(), nullptr)) {
+ env->CallVoidMethod(dvr.get(), gFields.onDvrRecordStatusID, (jint)status);
} else {
ALOGE("DvrClientCallbackImpl::onRecordStatus:"
"Dvr object has been freed. Ignoring callback.");
@@ -435,10 +431,9 @@
void DvrClientCallbackImpl::onPlaybackStatus(PlaybackStatus status) {
ALOGV("DvrClientCallbackImpl::onPlaybackStatus");
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jobject dvr(env->NewLocalRef(mDvrObj));
- if (!env->IsSameObject(dvr, nullptr)) {
- env->CallVoidMethod(dvr, gFields.onDvrPlaybackStatusID, (jint)status);
- env->DeleteLocalRef(dvr);
+ ScopedLocalRef dvr(env, env->NewLocalRef(mDvrObj));
+ if (!env->IsSameObject(dvr.get(), nullptr)) {
+ env->CallVoidMethod(dvr.get(), gFields.onDvrPlaybackStatusID, (jint)status);
} else {
ALOGE("DvrClientCallbackImpl::onPlaybackStatus:"
"Dvr object has been freed. Ignoring callback.");
@@ -614,7 +609,7 @@
}
/////////////// FilterClientCallbackImpl ///////////////////////
-void FilterClientCallbackImpl::getSectionEvent(jobjectArray &arr, const int size,
+void FilterClientCallbackImpl::getSectionEvent(const jobjectArray& arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -624,20 +619,20 @@
jint sectionNum = sectionEvent.sectionNum;
jlong dataLength = sectionEvent.dataLength;
- jobject obj = env->NewObject(mSectionEventClass, mSectionEventInitID, tableId, version,
- sectionNum, dataLength);
- env->SetObjectArrayElement(arr, size, obj);
- env->DeleteLocalRef(obj);
+ ScopedLocalRef obj(env, env->NewObject(mSectionEventClass, mSectionEventInitID, tableId,
+ version, sectionNum, dataLength));
+ env->SetObjectArrayElement(arr, size, obj.get());
}
-void FilterClientCallbackImpl::getMediaEvent(jobjectArray &arr, const int size,
+void FilterClientCallbackImpl::getMediaEvent(const jobjectArray& arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
const DemuxFilterMediaEvent &mediaEvent = event.get<DemuxFilterEvent::Tag::media>();
- jobject audioDescriptor = nullptr;
+ ScopedLocalRef<jobject> audioDescriptor(env);
gAudioPresentationFields.init(env);
- jobject presentationsJObj = JAudioPresentationInfo::asJobject(env, gAudioPresentationFields);
+ ScopedLocalRef presentationsJObj(env, JAudioPresentationInfo::asJobject(
+ env, gAudioPresentationFields));
switch (mediaEvent.extraMetaData.getTag()) {
case DemuxFilterMediaEventExtraMetaData::Tag::audio: {
@@ -650,9 +645,9 @@
jbyte adGainFront = ad.adGainFront;
jbyte adGainSurround = ad.adGainSurround;
- audioDescriptor = env->NewObject(mAudioDescriptorClass, mAudioDescriptorInitID, adFade,
- adPan, versionTextTag, adGainCenter, adGainFront,
- adGainSurround);
+ audioDescriptor.reset(env->NewObject(mAudioDescriptorClass, mAudioDescriptorInitID,
+ adFade, adPan, versionTextTag, adGainCenter,
+ adGainFront, adGainSurround));
break;
}
case DemuxFilterMediaEventExtraMetaData::Tag::audioPresentations: {
@@ -660,7 +655,7 @@
env, gAudioPresentationFields,
mediaEvent.extraMetaData
.get<DemuxFilterMediaEventExtraMetaData::Tag::audioPresentations>(),
- presentationsJObj);
+ presentationsJObj.get());
break;
}
default: {
@@ -693,31 +688,27 @@
sc = mediaEvent.scIndexMask.get<DemuxFilterScIndexMask::Tag::scVvc>();
}
- jobject obj = env->NewObject(mMediaEventClass, mMediaEventInitID, streamId, isPtsPresent, pts,
- isDtsPresent, dts, dataLength, offset, nullptr, isSecureMemory,
- avDataId, mpuSequenceNumber, isPesPrivateData, sc,
- audioDescriptor, presentationsJObj);
+ ScopedLocalRef obj(env, env->NewObject(mMediaEventClass, mMediaEventInitID, streamId,
+ isPtsPresent, pts, isDtsPresent, dts, dataLength,
+ offset, nullptr, isSecureMemory, avDataId,
+ mpuSequenceNumber, isPesPrivateData, sc,
+ audioDescriptor.get(), presentationsJObj.get()));
uint64_t avSharedMemSize = mFilterClient->getAvSharedHandleInfo().size;
if (mediaEvent.avMemory.fds.size() > 0 || mediaEvent.avDataId != 0 ||
(dataLength > 0 && (dataLength + offset) < avSharedMemSize)) {
sp<MediaEvent> mediaEventSp =
new MediaEvent(mFilterClient, dupFromAidl(mediaEvent.avMemory),
- mediaEvent.avDataId, dataLength + offset, obj);
+ mediaEvent.avDataId, dataLength + offset, obj.get());
mediaEventSp->mAvHandleRefCnt++;
- env->SetLongField(obj, mMediaEventFieldContextID, (jlong)mediaEventSp.get());
- mediaEventSp->incStrong(obj);
+ env->SetLongField(obj.get(), mMediaEventFieldContextID, (jlong)mediaEventSp.get());
+ mediaEventSp->incStrong(obj.get());
}
- env->SetObjectArrayElement(arr, size, obj);
- if(audioDescriptor != nullptr) {
- env->DeleteLocalRef(audioDescriptor);
- }
- env->DeleteLocalRef(obj);
- env->DeleteLocalRef(presentationsJObj);
+ env->SetObjectArrayElement(arr, size, obj.get());
}
-void FilterClientCallbackImpl::getPesEvent(jobjectArray &arr, const int size,
+void FilterClientCallbackImpl::getPesEvent(const jobjectArray& arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -726,13 +717,12 @@
jint dataLength = pesEvent.dataLength;
jint mpuSequenceNumber = pesEvent.mpuSequenceNumber;
- jobject obj = env->NewObject(mPesEventClass, mPesEventInitID, streamId, dataLength,
- mpuSequenceNumber);
- env->SetObjectArrayElement(arr, size, obj);
- env->DeleteLocalRef(obj);
+ ScopedLocalRef obj(env, env->NewObject(mPesEventClass, mPesEventInitID, streamId, dataLength,
+ mpuSequenceNumber));
+ env->SetObjectArrayElement(arr, size, obj.get());
}
-void FilterClientCallbackImpl::getTsRecordEvent(jobjectArray &arr, const int size,
+void FilterClientCallbackImpl::getTsRecordEvent(const jobjectArray& arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -764,13 +754,12 @@
jlong pts = tsRecordEvent.pts;
jint firstMbInSlice = tsRecordEvent.firstMbInSlice;
- jobject obj = env->NewObject(mTsRecordEventClass, mTsRecordEventInitID, jpid, ts, sc,
- byteNumber, pts, firstMbInSlice);
- env->SetObjectArrayElement(arr, size, obj);
- env->DeleteLocalRef(obj);
+ ScopedLocalRef obj(env, env->NewObject(mTsRecordEventClass, mTsRecordEventInitID, jpid, ts, sc,
+ byteNumber, pts, firstMbInSlice));
+ env->SetObjectArrayElement(arr, size, obj.get());
}
-void FilterClientCallbackImpl::getMmtpRecordEvent(jobjectArray &arr, const int size,
+void FilterClientCallbackImpl::getMmtpRecordEvent(const jobjectArray& arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -783,13 +772,13 @@
jint firstMbInSlice = mmtpRecordEvent.firstMbInSlice;
jlong tsIndexMask = mmtpRecordEvent.tsIndexMask;
- jobject obj = env->NewObject(mMmtpRecordEventClass, mMmtpRecordEventInitID, scHevcIndexMask,
- byteNumber, mpuSequenceNumber, pts, firstMbInSlice, tsIndexMask);
- env->SetObjectArrayElement(arr, size, obj);
- env->DeleteLocalRef(obj);
+ ScopedLocalRef obj(env, env->NewObject(mMmtpRecordEventClass, mMmtpRecordEventInitID,
+ scHevcIndexMask, byteNumber, mpuSequenceNumber, pts,
+ firstMbInSlice, tsIndexMask));
+ env->SetObjectArrayElement(arr, size, obj.get());
}
-void FilterClientCallbackImpl::getDownloadEvent(jobjectArray &arr, const int size,
+void FilterClientCallbackImpl::getDownloadEvent(const jobjectArray& arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -801,25 +790,25 @@
jint lastItemFragmentIndex = downloadEvent.lastItemFragmentIndex;
jint dataLength = downloadEvent.dataLength;
- jobject obj = env->NewObject(mDownloadEventClass, mDownloadEventInitID, itemId, downloadId,
- mpuSequenceNumber, itemFragmentIndex, lastItemFragmentIndex,
- dataLength);
- env->SetObjectArrayElement(arr, size, obj);
- env->DeleteLocalRef(obj);
+ ScopedLocalRef obj(env, env->NewObject(mDownloadEventClass, mDownloadEventInitID, itemId,
+ downloadId, mpuSequenceNumber, itemFragmentIndex,
+ lastItemFragmentIndex, dataLength));
+ env->SetObjectArrayElement(arr, size, obj.get());
}
-void FilterClientCallbackImpl::getIpPayloadEvent(jobjectArray &arr, const int size,
+void FilterClientCallbackImpl::getIpPayloadEvent(const jobjectArray& arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
- const DemuxFilterIpPayloadEvent &ipPayloadEvent = event.get<DemuxFilterEvent::Tag::ipPayload>();
+ const DemuxFilterIpPayloadEvent &ipPayloadEvent =
+ event.get<DemuxFilterEvent::Tag::ipPayload>();
jint dataLength = ipPayloadEvent.dataLength;
- jobject obj = env->NewObject(mIpPayloadEventClass, mIpPayloadEventInitID, dataLength);
- env->SetObjectArrayElement(arr, size, obj);
- env->DeleteLocalRef(obj);
+ ScopedLocalRef obj(env, env->NewObject(mIpPayloadEventClass, mIpPayloadEventInitID,
+ dataLength));
+ env->SetObjectArrayElement(arr, size, obj.get());
}
-void FilterClientCallbackImpl::getTemiEvent(jobjectArray &arr, const int size,
+void FilterClientCallbackImpl::getTemiEvent(const jobjectArray& arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
@@ -828,110 +817,108 @@
jbyte descrTag = temiEvent.descrTag;
std::vector<uint8_t> descrData = temiEvent.descrData;
- jbyteArray array = env->NewByteArray(descrData.size());
- env->SetByteArrayRegion(array, 0, descrData.size(), reinterpret_cast<jbyte *>(&descrData[0]));
+ ScopedLocalRef array(env, env->NewByteArray(descrData.size()));
+ env->SetByteArrayRegion(array.get(), 0, descrData.size(),
+ reinterpret_cast<jbyte *>(&descrData[0]));
- jobject obj = env->NewObject(mTemiEventClass, mTemiEventInitID, pts, descrTag, array);
- env->SetObjectArrayElement(arr, size, obj);
- env->DeleteLocalRef(array);
- env->DeleteLocalRef(obj);
+ ScopedLocalRef obj(env, env->NewObject(mTemiEventClass, mTemiEventInitID, pts, descrTag,
+ array.get()));
+ env->SetObjectArrayElement(arr, size, obj.get());
}
-void FilterClientCallbackImpl::getScramblingStatusEvent(jobjectArray &arr, const int size,
+void FilterClientCallbackImpl::getScramblingStatusEvent(const jobjectArray& arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
const DemuxFilterMonitorEvent &scramblingStatus =
event.get<DemuxFilterEvent::Tag::monitorEvent>()
.get<DemuxFilterMonitorEvent::Tag::scramblingStatus>();
- jobject obj = env->NewObject(mScramblingStatusEventClass, mScramblingStatusEventInitID,
- scramblingStatus);
- env->SetObjectArrayElement(arr, size, obj);
- env->DeleteLocalRef(obj);
+ ScopedLocalRef obj(env, env->NewObject(mScramblingStatusEventClass,
+ mScramblingStatusEventInitID,
+ scramblingStatus));
+ env->SetObjectArrayElement(arr, size, obj.get());
}
-void FilterClientCallbackImpl::getIpCidChangeEvent(jobjectArray &arr, const int size,
+void FilterClientCallbackImpl::getIpCidChangeEvent(const jobjectArray& arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
const DemuxFilterMonitorEvent &cid = event.get<DemuxFilterEvent::Tag::monitorEvent>()
.get<DemuxFilterMonitorEvent::Tag::cid>();
- jobject obj = env->NewObject(mIpCidChangeEventClass, mIpCidChangeEventInitID, cid);
- env->SetObjectArrayElement(arr, size, obj);
- env->DeleteLocalRef(obj);
+ ScopedLocalRef obj(env, env->NewObject(mIpCidChangeEventClass, mIpCidChangeEventInitID, cid));
+ env->SetObjectArrayElement(arr, size, obj.get());
}
-void FilterClientCallbackImpl::getRestartEvent(jobjectArray &arr, const int size,
+void FilterClientCallbackImpl::getRestartEvent(const jobjectArray& arr, const int size,
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
const int32_t &startId = event.get<DemuxFilterEvent::Tag::startId>();
- jobject obj = env->NewObject(mRestartEventClass, mRestartEventInitID, startId);
- env->SetObjectArrayElement(arr, size, obj);
- env->DeleteLocalRef(obj);
+ ScopedLocalRef obj(env, env->NewObject(mRestartEventClass, mRestartEventInitID, startId));
+ env->SetObjectArrayElement(arr, size, obj.get());
}
void FilterClientCallbackImpl::onFilterEvent(const vector<DemuxFilterEvent> &events) {
ALOGV("FilterClientCallbackImpl::onFilterEvent");
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jobjectArray array;
+ ScopedLocalRef<jobjectArray> array(env);
if (!events.empty()) {
- array = env->NewObjectArray(events.size(), mEventClass, nullptr);
+ array.reset(env->NewObjectArray(events.size(), mEventClass, nullptr));
}
for (int i = 0, arraySize = 0; i < events.size(); i++) {
const DemuxFilterEvent &event = events[i];
switch (event.getTag()) {
case DemuxFilterEvent::Tag::media: {
- getMediaEvent(array, arraySize, event);
+ getMediaEvent(array.get(), arraySize, event);
arraySize++;
break;
}
case DemuxFilterEvent::Tag::section: {
- getSectionEvent(array, arraySize, event);
+ getSectionEvent(array.get(), arraySize, event);
arraySize++;
break;
}
case DemuxFilterEvent::Tag::pes: {
- getPesEvent(array, arraySize, event);
+ getPesEvent(array.get(), arraySize, event);
arraySize++;
break;
}
case DemuxFilterEvent::Tag::tsRecord: {
- getTsRecordEvent(array, arraySize, event);
+ getTsRecordEvent(array.get(), arraySize, event);
arraySize++;
break;
}
case DemuxFilterEvent::Tag::mmtpRecord: {
- getMmtpRecordEvent(array, arraySize, event);
+ getMmtpRecordEvent(array.get(), arraySize, event);
arraySize++;
break;
}
case DemuxFilterEvent::Tag::download: {
- getDownloadEvent(array, arraySize, event);
+ getDownloadEvent(array.get(), arraySize, event);
arraySize++;
break;
}
case DemuxFilterEvent::Tag::ipPayload: {
- getIpPayloadEvent(array, arraySize, event);
+ getIpPayloadEvent(array.get(), arraySize, event);
arraySize++;
break;
}
case DemuxFilterEvent::Tag::temi: {
- getTemiEvent(array, arraySize, event);
+ getTemiEvent(array.get(), arraySize, event);
arraySize++;
break;
}
case DemuxFilterEvent::Tag::monitorEvent: {
switch (event.get<DemuxFilterEvent::Tag::monitorEvent>().getTag()) {
case DemuxFilterMonitorEvent::Tag::scramblingStatus: {
- getScramblingStatusEvent(array, arraySize, event);
+ getScramblingStatusEvent(array.get(), arraySize, event);
arraySize++;
break;
}
case DemuxFilterMonitorEvent::Tag::cid: {
- getIpCidChangeEvent(array, arraySize, event);
+ getIpCidChangeEvent(array.get(), arraySize, event);
arraySize++;
break;
}
@@ -943,7 +930,7 @@
break;
}
case DemuxFilterEvent::Tag::startId: {
- getRestartEvent(array, arraySize, event);
+ getRestartEvent(array.get(), arraySize, event);
arraySize++;
break;
}
@@ -953,32 +940,29 @@
}
}
}
- jobject filter(env->NewLocalRef(mFilterObj));
- if (!env->IsSameObject(filter, nullptr)) {
+ ScopedLocalRef filter(env, env->NewLocalRef(mFilterObj));
+ if (!env->IsSameObject(filter.get(), nullptr)) {
jmethodID methodID = gFields.onFilterEventID;
if (mSharedFilter) {
methodID = gFields.onSharedFilterEventID;
}
- env->CallVoidMethod(filter, methodID, array);
- env->DeleteLocalRef(filter);
+ env->CallVoidMethod(filter.get(), methodID, array.get());
} else {
ALOGE("FilterClientCallbackImpl::onFilterEvent:"
"Filter object has been freed. Ignoring callback.");
}
- env->DeleteLocalRef(array);
}
void FilterClientCallbackImpl::onFilterStatus(const DemuxFilterStatus status) {
ALOGV("FilterClientCallbackImpl::onFilterStatus");
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jobject filter(env->NewLocalRef(mFilterObj));
- if (!env->IsSameObject(filter, nullptr)) {
+ ScopedLocalRef filter(env, env->NewLocalRef(mFilterObj));
+ if (!env->IsSameObject(filter.get(), nullptr)) {
jmethodID methodID = gFields.onFilterStatusID;
if (mSharedFilter) {
methodID = gFields.onSharedFilterStatusID;
}
- env->CallVoidMethod(filter, methodID, (jint)static_cast<uint8_t>(status));
- env->DeleteLocalRef(filter);
+ env->CallVoidMethod(filter.get(), methodID, (jint)static_cast<uint8_t>(status));
} else {
ALOGE("FilterClientCallbackImpl::onFilterStatus:"
"Filter object has been freed. Ignoring callback.");
@@ -1115,13 +1099,12 @@
std::scoped_lock<std::mutex> lock(mMutex);
for (const auto& mapEntry : mListenersMap) {
ALOGV("JTuner:%p, jweak:%p", mapEntry.first, mapEntry.second);
- jobject frontend(env->NewLocalRef(mapEntry.second));
- if (!env->IsSameObject(frontend, nullptr)) {
+ ScopedLocalRef frontend(env, env->NewLocalRef(mapEntry.second));
+ if (!env->IsSameObject(frontend.get(), nullptr)) {
env->CallVoidMethod(
- frontend,
+ frontend.get(),
gFields.onFrontendEventID,
(jint)frontendEventType);
- env->DeleteLocalRef(frontend);
} else {
ALOGW("FrontendClientCallbackImpl::onEvent:"
"Frontend object has been freed. Ignoring callback.");
@@ -1133,20 +1116,18 @@
FrontendScanMessageType type, const FrontendScanMessage& message) {
ALOGV("FrontendClientCallbackImpl::onScanMessage, type=%d", type);
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
+ ScopedLocalRef clazz(env, env->FindClass("android/media/tv/tuner/Tuner"));
std::scoped_lock<std::mutex> lock(mMutex);
for (const auto& mapEntry : mListenersMap) {
- jobject frontend(env->NewLocalRef(mapEntry.second));
- if (env->IsSameObject(frontend, nullptr)) {
+ ScopedLocalRef frontend(env, env->NewLocalRef(mapEntry.second));
+ if (env->IsSameObject(frontend.get(), nullptr)) {
ALOGE("FrontendClientCallbackImpl::onScanMessage:"
"Tuner object has been freed. Ignoring callback.");
continue;
}
- executeOnScanMessage(env, clazz, frontend, type, message);
- env->DeleteLocalRef(frontend);
+ executeOnScanMessage(env, clazz.get(), frontend.get(), type, message);
}
- env->DeleteLocalRef(clazz);
}
void FrontendClientCallbackImpl::executeOnScanMessage(
@@ -1183,20 +1164,19 @@
}
case FrontendScanMessageType::FREQUENCY: {
std::vector<int64_t> v = message.get<FrontendScanMessage::Tag::frequencies>();
- jlongArray freqs = env->NewLongArray(v.size());
- env->SetLongArrayRegion(freqs, 0, v.size(), reinterpret_cast<jlong *>(&v[0]));
+ ScopedLocalRef freqs(env, env->NewLongArray(v.size()));
+ env->SetLongArrayRegion(freqs.get(), 0, v.size(), reinterpret_cast<jlong *>(&v[0]));
env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onFrequenciesReport", "([J)V"),
- freqs);
- env->DeleteLocalRef(freqs);
+ freqs.get());
break;
}
case FrontendScanMessageType::SYMBOL_RATE: {
std::vector<int32_t> v = message.get<FrontendScanMessage::Tag::symbolRates>();
- jintArray symbolRates = env->NewIntArray(v.size());
- env->SetIntArrayRegion(symbolRates, 0, v.size(), reinterpret_cast<jint *>(&v[0]));
+ ScopedLocalRef symbolRates(env, env->NewIntArray(v.size()));
+ env->SetIntArrayRegion(symbolRates.get(), 0, v.size(),
+ reinterpret_cast<jint *>(&v[0]));
env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onSymbolRates", "([I)V"),
- symbolRates);
- env->DeleteLocalRef(symbolRates);
+ symbolRates.get());
break;
}
case FrontendScanMessageType::HIERARCHY: {
@@ -1211,27 +1191,29 @@
}
case FrontendScanMessageType::PLP_IDS: {
std::vector<int32_t> jintV = message.get<FrontendScanMessage::Tag::plpIds>();
- jintArray plpIds = env->NewIntArray(jintV.size());
- env->SetIntArrayRegion(plpIds, 0, jintV.size(), reinterpret_cast<jint *>(&jintV[0]));
- env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onPlpIds", "([I)V"), plpIds);
- env->DeleteLocalRef(plpIds);
+ ScopedLocalRef plpIds(env, env->NewIntArray(jintV.size()));
+ env->SetIntArrayRegion(plpIds.get(), 0, jintV.size(),
+ reinterpret_cast<jint *>(&jintV[0]));
+ env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onPlpIds", "([I)V"),
+ plpIds.get());
break;
}
case FrontendScanMessageType::GROUP_IDS: {
std::vector<int32_t> jintV = message.get<FrontendScanMessage::groupIds>();
- jintArray groupIds = env->NewIntArray(jintV.size());
- env->SetIntArrayRegion(groupIds, 0, jintV.size(), reinterpret_cast<jint *>(&jintV[0]));
- env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onGroupIds", "([I)V"), groupIds);
- env->DeleteLocalRef(groupIds);
+ ScopedLocalRef groupIds(env, env->NewIntArray(jintV.size()));
+ env->SetIntArrayRegion(groupIds.get(), 0, jintV.size(),
+ reinterpret_cast<jint *>(&jintV[0]));
+ env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onGroupIds", "([I)V"),
+ groupIds.get());
break;
}
case FrontendScanMessageType::INPUT_STREAM_IDS: {
std::vector<int32_t> jintV = message.get<FrontendScanMessage::inputStreamIds>();
- jintArray streamIds = env->NewIntArray(jintV.size());
- env->SetIntArrayRegion(streamIds, 0, jintV.size(), reinterpret_cast<jint *>(&jintV[0]));
+ ScopedLocalRef streamIds(env, env->NewIntArray(jintV.size()));
+ env->SetIntArrayRegion(streamIds.get(), 0, jintV.size(),
+ reinterpret_cast<jint *>(&jintV[0]));
env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onInputStreamIds", "([I)V"),
- streamIds);
- env->DeleteLocalRef(streamIds);
+ streamIds.get());
break;
}
case FrontendScanMessageType::STANDARD: {
@@ -1254,26 +1236,25 @@
break;
}
case FrontendScanMessageType::ATSC3_PLP_INFO: {
- jclass plpClazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3PlpInfo");
- jmethodID init = env->GetMethodID(plpClazz, "<init>", "(IZ)V");
+ ScopedLocalRef plpClazz(env,
+ env->FindClass("android/media/tv/tuner/frontend/Atsc3PlpInfo"));
+ jmethodID init = env->GetMethodID(plpClazz.get(), "<init>", "(IZ)V");
std::vector<FrontendScanAtsc3PlpInfo> plpInfos =
message.get<FrontendScanMessage::atsc3PlpInfos>();
- jobjectArray array = env->NewObjectArray(plpInfos.size(), plpClazz, nullptr);
+ ScopedLocalRef array(env,
+ env->NewObjectArray(plpInfos.size(), plpClazz.get(), nullptr));
for (int i = 0; i < plpInfos.size(); i++) {
const FrontendScanAtsc3PlpInfo &info = plpInfos[i];
jint plpId = info.plpId;
jboolean lls = info.bLlsFlag;
- jobject obj = env->NewObject(plpClazz, init, plpId, lls);
- env->SetObjectArrayElement(array, i, obj);
- env->DeleteLocalRef(obj);
+ ScopedLocalRef obj(env, env->NewObject(plpClazz.get(), init, plpId, lls));
+ env->SetObjectArrayElement(array.get(), i, obj.get());
}
env->CallVoidMethod(frontend,
env->GetMethodID(clazz, "onAtsc3PlpInfos",
"([Landroid/media/tv/tuner/frontend/"
"Atsc3PlpInfo;)V"),
- array);
- env->DeleteLocalRef(array);
- env->DeleteLocalRef(plpClazz);
+ array.get());
break;
}
case FrontendScanMessageType::MODULATION: {
@@ -1341,11 +1322,12 @@
}
case FrontendScanMessageType::DVBT_CELL_IDS: {
std::vector<int32_t> jintV = message.get<FrontendScanMessage::dvbtCellIds>();
- jintArray cellIds = env->NewIntArray(jintV.size());
- env->SetIntArrayRegion(cellIds, 0, jintV.size(), reinterpret_cast<jint *>(&jintV[0]));
- env->CallVoidMethod(frontend, env->GetMethodID(clazz, "onDvbtCellIdsReported", "([I)V"),
- cellIds);
- env->DeleteLocalRef(cellIds);
+ ScopedLocalRef cellIds(env, env->NewIntArray(jintV.size()));
+ env->SetIntArrayRegion(cellIds.get(), 0, jintV.size(),
+ reinterpret_cast<jint *>(&jintV[0]));
+ env->CallVoidMethod(frontend,
+ env->GetMethodID(clazz, "onDvbtCellIdsReported", "([I)V"),
+ cellIds.get());
break;
}
default:
@@ -1434,7 +1416,8 @@
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass arrayListClazz = env->FindClass("java/util/ArrayList");
jmethodID arrayListAdd = env->GetMethodID(arrayListClazz, "add", "(Ljava/lang/Object;)Z");
- jobject obj = env->NewObject(arrayListClazz, env->GetMethodID(arrayListClazz, "<init>", "()V"));
+ jobject obj = env->NewObject(arrayListClazz,
+ env->GetMethodID(arrayListClazz, "<init>", "()V"));
jclass integerClazz = env->FindClass("java/lang/Integer");
jmethodID intInit = env->GetMethodID(integerClazz, "<init>", "(I)V");
@@ -1672,7 +1655,7 @@
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendInfo");
jmethodID infoInit =
env->GetMethodID(clazz, "<init>",
- "(IIJJIIJI[ILandroid/media/tv/tuner/frontend/FrontendCapabilities;)V");
+ "(IIJJIIJI[ILandroid/media/tv/tuner/frontend/FrontendCapabilities;)V");
jint type = (jint)feInfo->type;
jlong minFrequency = feInfo->minFrequency;
@@ -1812,9 +1795,8 @@
jmethodID init = env->GetMethodID(clazz, "<init>", "(II)V");
jobjectArray valObj = env->NewObjectArray(size, clazz, nullptr);
for (int i = 0; i < size; i++) {
- jobject readinessObj = env->NewObject(clazz, init, intTypes[i], readiness[i]);
- env->SetObjectArrayElement(valObj, i, readinessObj);
- env->DeleteLocalRef(readinessObj);
+ ScopedLocalRef readinessObj(env, env->NewObject(clazz, init, intTypes[i], readiness[i]));
+ env->SetObjectArrayElement(valObj, i, readinessObj.get());
}
return valObj;
}
@@ -2260,79 +2242,72 @@
switch (s.getTag()) {
case FrontendStatus::Tag::isDemodLocked: {
jfieldID field = env->GetFieldID(clazz, "mIsDemodLocked", "Ljava/lang/Boolean;");
- jobject newBooleanObj = env->NewObject(booleanClazz, initBoolean,
- s.get<FrontendStatus::Tag::isDemodLocked>());
- env->SetObjectField(statusObj, field, newBooleanObj);
- env->DeleteLocalRef(newBooleanObj);
+ ScopedLocalRef newBooleanObj(env,
+ env->NewObject(booleanClazz, initBoolean,
+ s.get<FrontendStatus::Tag::isDemodLocked>()));
+ env->SetObjectField(statusObj, field, newBooleanObj.get());
break;
}
case FrontendStatus::Tag::snr: {
jfieldID field = env->GetFieldID(clazz, "mSnr", "Ljava/lang/Integer;");
- jobject newIntegerObj =
- env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::snr>());
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ ScopedLocalRef newIntegerObj(env,
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::snr>()));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::ber: {
jfieldID field = env->GetFieldID(clazz, "mBer", "Ljava/lang/Integer;");
- jobject newIntegerObj =
- env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::ber>());
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ ScopedLocalRef newIntegerObj(env,
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::ber>()));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::per: {
jfieldID field = env->GetFieldID(clazz, "mPer", "Ljava/lang/Integer;");
- jobject newIntegerObj =
- env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::per>());
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ ScopedLocalRef newIntegerObj(env,
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::per>()));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::preBer: {
jfieldID field = env->GetFieldID(clazz, "mPerBer", "Ljava/lang/Integer;");
- jobject newIntegerObj =
- env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::preBer>());
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ ScopedLocalRef newIntegerObj(env,
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::preBer>()));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::signalQuality: {
jfieldID field = env->GetFieldID(clazz, "mSignalQuality", "Ljava/lang/Integer;");
- jobject newIntegerObj = env->NewObject(intClazz, initInt,
- s.get<FrontendStatus::Tag::signalQuality>());
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ ScopedLocalRef newIntegerObj(env,
+ env->NewObject(intClazz, initInt,
+ s.get<FrontendStatus::Tag::signalQuality>()));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::signalStrength: {
jfieldID field = env->GetFieldID(clazz, "mSignalStrength", "Ljava/lang/Integer;");
- jobject newIntegerObj =
+ ScopedLocalRef newIntegerObj(env,
env->NewObject(intClazz, initInt,
- s.get<FrontendStatus::Tag::signalStrength>());
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ s.get<FrontendStatus::Tag::signalStrength>()));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::symbolRate: {
jfieldID field = env->GetFieldID(clazz, "mSymbolRate", "Ljava/lang/Integer;");
- jobject newIntegerObj =
- env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::symbolRate>());
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ ScopedLocalRef newIntegerObj(env,
+ env->NewObject(intClazz, initInt,
+ s.get<FrontendStatus::Tag::symbolRate>()));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::innerFec: {
jfieldID field = env->GetFieldID(clazz, "mInnerFec", "Ljava/lang/Long;");
- jclass longClazz = env->FindClass("java/lang/Long");
- jmethodID initLong = env->GetMethodID(longClazz, "<init>", "(J)V");
- jobject newLongObj =
- env->NewObject(longClazz, initLong,
- static_cast<long>(s.get<FrontendStatus::Tag::innerFec>()));
- env->SetObjectField(statusObj, field, newLongObj);
- env->DeleteLocalRef(newLongObj);
- env->DeleteLocalRef(longClazz);
+ ScopedLocalRef longClazz(env, env->FindClass("java/lang/Long"));
+ jmethodID initLong = env->GetMethodID(longClazz.get(), "<init>", "(J)V");
+ ScopedLocalRef newLongObj(env,
+ env->NewObject(longClazz.get(), initLong,
+ static_cast<long>(s.get<FrontendStatus::Tag::innerFec>())));
+ env->SetObjectField(statusObj, field, newLongObj.get());
break;
}
case FrontendStatus::Tag::modulationStatus: {
@@ -2373,139 +2348,128 @@
}
}
if (valid) {
- jobject newIntegerObj = env->NewObject(intClazz, initInt, intModulation);
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ ScopedLocalRef newIntegerObj(env,
+ env->NewObject(intClazz, initInt, intModulation));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
}
break;
}
case FrontendStatus::Tag::inversion: {
jfieldID field = env->GetFieldID(clazz, "mInversion", "Ljava/lang/Integer;");
- jobject newIntegerObj =
+ ScopedLocalRef newIntegerObj(env,
env->NewObject(intClazz, initInt,
- static_cast<jint>(s.get<FrontendStatus::Tag::inversion>()));
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ static_cast<jint>(s.get<FrontendStatus::Tag::inversion>())));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::lnbVoltage: {
jfieldID field = env->GetFieldID(clazz, "mLnbVoltage", "Ljava/lang/Integer;");
- jobject newIntegerObj =
+ ScopedLocalRef newIntegerObj(env,
env->NewObject(intClazz, initInt,
- static_cast<jint>(s.get<FrontendStatus::Tag::lnbVoltage>()));
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ static_cast<jint>(s.get<FrontendStatus::Tag::lnbVoltage>())));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::plpId: {
jfieldID field = env->GetFieldID(clazz, "mPlpId", "Ljava/lang/Integer;");
- jobject newIntegerObj =
- env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::plpId>());
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ ScopedLocalRef newIntegerObj(env,
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::plpId>()));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::isEWBS: {
jfieldID field = env->GetFieldID(clazz, "mIsEwbs", "Ljava/lang/Boolean;");
- jobject newBooleanObj = env->NewObject(booleanClazz, initBoolean,
- s.get<FrontendStatus::Tag::isEWBS>());
- env->SetObjectField(statusObj, field, newBooleanObj);
- env->DeleteLocalRef(newBooleanObj);
+ ScopedLocalRef newBooleanObj(env, env->NewObject(booleanClazz, initBoolean,
+ s.get<FrontendStatus::Tag::isEWBS>()));
+ env->SetObjectField(statusObj, field, newBooleanObj.get());
break;
}
case FrontendStatus::Tag::agc: {
jfieldID field = env->GetFieldID(clazz, "mAgc", "Ljava/lang/Integer;");
- jobject newIntegerObj =
- env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::agc>());
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ ScopedLocalRef newIntegerObj(env,
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::agc>()));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::isLnaOn: {
jfieldID field = env->GetFieldID(clazz, "mIsLnaOn", "Ljava/lang/Boolean;");
- jobject newBooleanObj = env->NewObject(booleanClazz, initBoolean,
- s.get<FrontendStatus::Tag::isLnaOn>());
- env->SetObjectField(statusObj, field, newBooleanObj);
- env->DeleteLocalRef(newBooleanObj);
+ ScopedLocalRef newBooleanObj(env, env->NewObject(booleanClazz, initBoolean,
+ s.get<FrontendStatus::Tag::isLnaOn>()));
+ env->SetObjectField(statusObj, field, newBooleanObj.get());
break;
}
case FrontendStatus::Tag::isLayerError: {
jfieldID field = env->GetFieldID(clazz, "mIsLayerErrors", "[Z");
vector<bool> layerErr = s.get<FrontendStatus::Tag::isLayerError>();
- jbooleanArray valObj = env->NewBooleanArray(layerErr.size());
+ ScopedLocalRef valObj(env, env->NewBooleanArray(layerErr.size()));
for (size_t i = 0; i < layerErr.size(); i++) {
jboolean x = layerErr[i];
- env->SetBooleanArrayRegion(valObj, i, 1, &x);
+ env->SetBooleanArrayRegion(valObj.get(), i, 1, &x);
}
- env->SetObjectField(statusObj, field, valObj);
- env->DeleteLocalRef(valObj);
+ env->SetObjectField(statusObj, field, valObj.get());
break;
}
case FrontendStatus::Tag::mer: {
jfieldID field = env->GetFieldID(clazz, "mMer", "Ljava/lang/Integer;");
- jobject newIntegerObj =
- env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::mer>());
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ ScopedLocalRef newIntegerObj(env,
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::mer>()));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::freqOffset: {
jfieldID field = env->GetFieldID(clazz, "mFreqOffset", "Ljava/lang/Long;");
- jobject newLongObj = env->NewObject(longClazz, initLong,
- s.get<FrontendStatus::Tag::freqOffset>());
- env->SetObjectField(statusObj, field, newLongObj);
- env->DeleteLocalRef(newLongObj);
+ ScopedLocalRef newLongObj(env, env->NewObject(longClazz, initLong,
+ s.get<FrontendStatus::Tag::freqOffset>()));
+ env->SetObjectField(statusObj, field, newLongObj.get());
break;
}
case FrontendStatus::Tag::hierarchy: {
jfieldID field = env->GetFieldID(clazz, "mHierarchy", "Ljava/lang/Integer;");
- jobject newIntegerObj =
+ ScopedLocalRef newIntegerObj(env,
env->NewObject(intClazz, initInt,
- static_cast<jint>(s.get<FrontendStatus::Tag::hierarchy>()));
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ static_cast<jint>(s.get<FrontendStatus::Tag::hierarchy>())));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::isRfLocked: {
jfieldID field = env->GetFieldID(clazz, "mIsRfLocked", "Ljava/lang/Boolean;");
- jobject newBooleanObj = env->NewObject(booleanClazz, initBoolean,
- s.get<FrontendStatus::Tag::isRfLocked>());
- env->SetObjectField(statusObj, field, newBooleanObj);
- env->DeleteLocalRef(newBooleanObj);
+ ScopedLocalRef newBooleanObj(env, env->NewObject(booleanClazz, initBoolean,
+ s.get<FrontendStatus::Tag::isRfLocked>()));
+ env->SetObjectField(statusObj, field, newBooleanObj.get());
break;
}
case FrontendStatus::Tag::plpInfo: {
jfieldID field = env->GetFieldID(clazz, "mPlpInfo",
"[Landroid/media/tv/tuner/frontend/FrontendStatus$Atsc3PlpTuningInfo;");
- jclass plpClazz = env->FindClass(
- "android/media/tv/tuner/frontend/FrontendStatus$Atsc3PlpTuningInfo");
- jmethodID initPlp = env->GetMethodID(plpClazz, "<init>", "(IZI)V");
+ ScopedLocalRef plpClazz(env, env->FindClass(
+ "android/media/tv/tuner/frontend/FrontendStatus$Atsc3PlpTuningInfo"));
+ jmethodID initPlp = env->GetMethodID(plpClazz.get(), "<init>", "(IZI)V");
- vector<FrontendStatusAtsc3PlpInfo> plpInfos = s.get<FrontendStatus::Tag::plpInfo>();
- jobjectArray valObj = env->NewObjectArray(plpInfos.size(), plpClazz, nullptr);
+ vector<FrontendStatusAtsc3PlpInfo> plpInfos =
+ s.get<FrontendStatus::Tag::plpInfo>();
+ ScopedLocalRef valObj(env, env->NewObjectArray(plpInfos.size(), plpClazz.get(),
+ nullptr));
for (int i = 0; i < plpInfos.size(); i++) {
const FrontendStatusAtsc3PlpInfo &info = plpInfos[i];
jint plpId = info.plpId;
jboolean isLocked = info.isLocked;
jint uec = info.uec;
- jobject plpObj = env->NewObject(plpClazz, initPlp, plpId, isLocked, uec);
- env->SetObjectArrayElement(valObj, i, plpObj);
- env->DeleteLocalRef(plpObj);
+ ScopedLocalRef plpObj(env, env->NewObject(plpClazz.get(), initPlp, plpId,
+ isLocked, uec));
+ env->SetObjectArrayElement(valObj.get(), i, plpObj.get());
}
- env->SetObjectField(statusObj, field, valObj);
- env->DeleteLocalRef(valObj);
- env->DeleteLocalRef(plpClazz);
+ env->SetObjectField(statusObj, field, valObj.get());
break;
}
case FrontendStatus::Tag::modulations: {
jfieldID field = env->GetFieldID(clazz, "mModulationsExt", "[I");
std::vector<FrontendModulation> v = s.get<FrontendStatus::Tag::modulations>();
- jintArray valObj = env->NewIntArray(v.size());
+ ScopedLocalRef valObj(env, env->NewIntArray(v.size()));
bool valid = false;
jint m[1];
for (int i = 0; i < v.size(); i++) {
@@ -2514,63 +2478,63 @@
case FrontendModulation::Tag::dvbc: {
m[0] = static_cast<jint>(
modulation.get<FrontendModulation::Tag::dvbc>());
- env->SetIntArrayRegion(valObj, i, 1, m);
+ env->SetIntArrayRegion(valObj.get(), i, 1, m);
valid = true;
break;
}
case FrontendModulation::Tag::dvbs: {
m[0] = static_cast<jint>(
modulation.get<FrontendModulation::Tag::dvbs>());
- env->SetIntArrayRegion(valObj, i, 1, m);
+ env->SetIntArrayRegion(valObj.get(), i, 1, m);
valid = true;
break;
}
case FrontendModulation::Tag::dvbt: {
m[0] = static_cast<jint>(
modulation.get<FrontendModulation::Tag::dvbt>());
- env->SetIntArrayRegion(valObj, i, 1, m);
+ env->SetIntArrayRegion(valObj.get(), i, 1, m);
valid = true;
break;
}
case FrontendModulation::Tag::isdbs: {
m[0] = static_cast<jint>(
modulation.get<FrontendModulation::Tag::isdbs>());
- env->SetIntArrayRegion(valObj, i, 1, m);
+ env->SetIntArrayRegion(valObj.get(), i, 1, m);
valid = true;
break;
}
case FrontendModulation::Tag::isdbs3: {
m[0] = static_cast<jint>(
modulation.get<FrontendModulation::Tag::isdbs3>());
- env->SetIntArrayRegion(valObj, i, 1, m);
+ env->SetIntArrayRegion(valObj.get(), i, 1, m);
valid = true;
break;
}
case FrontendModulation::Tag::isdbt: {
m[0] = static_cast<jint>(
modulation.get<FrontendModulation::Tag::isdbt>());
- env->SetIntArrayRegion(valObj, i, 1, m);
+ env->SetIntArrayRegion(valObj.get(), i, 1, m);
valid = true;
break;
}
case FrontendModulation::Tag::atsc: {
m[0] = static_cast<jint>(
modulation.get<FrontendModulation::Tag::atsc>());
- env->SetIntArrayRegion(valObj, i, 1, m);
+ env->SetIntArrayRegion(valObj.get(), i, 1, m);
valid = true;
break;
}
case FrontendModulation::Tag::atsc3: {
m[0] = static_cast<jint>(
modulation.get<FrontendModulation::Tag::atsc3>());
- env->SetIntArrayRegion(valObj, i, 1, m);
+ env->SetIntArrayRegion(valObj.get(), i, 1, m);
valid = true;
break;
}
case FrontendModulation::Tag::dtmb: {
m[0] = static_cast<jint>(
modulation.get<FrontendModulation::Tag::dtmb>());
- env->SetIntArrayRegion(valObj, i, 1, m);
+ env->SetIntArrayRegion(valObj.get(), i, 1, m);
valid = true;
break;
}
@@ -2579,31 +2543,28 @@
}
}
if (valid) {
- env->SetObjectField(statusObj, field, valObj);
+ env->SetObjectField(statusObj, field, valObj.get());
}
- env->DeleteLocalRef(valObj);
break;
}
case FrontendStatus::Tag::bers: {
jfieldID field = env->GetFieldID(clazz, "mBers", "[I");
std::vector<int32_t> v = s.get<FrontendStatus::Tag::bers>();
- jintArray valObj = env->NewIntArray(v.size());
- env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint *>(&v[0]));
+ ScopedLocalRef valObj(env, env->NewIntArray(v.size()));
+ env->SetIntArrayRegion(valObj.get(), 0, v.size(), reinterpret_cast<jint *>(&v[0]));
- env->SetObjectField(statusObj, field, valObj);
- env->DeleteLocalRef(valObj);
+ env->SetObjectField(statusObj, field, valObj.get());
break;
}
case FrontendStatus::Tag::codeRates: {
jfieldID field = env->GetFieldID(clazz, "mCodeRates", "[I");
std::vector<FrontendInnerFec> v = s.get<FrontendStatus::Tag::codeRates>();
- jintArray valObj = env->NewIntArray(v.size());
- env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint *>(&v[0]));
+ ScopedLocalRef valObj(env, env->NewIntArray(v.size()));
+ env->SetIntArrayRegion(valObj.get(), 0, v.size(), reinterpret_cast<jint *>(&v[0]));
- env->SetObjectField(statusObj, field, valObj);
- env->DeleteLocalRef(valObj);
+ env->SetObjectField(statusObj, field, valObj.get());
break;
}
case FrontendStatus::Tag::bandwidth: {
@@ -2642,9 +2603,9 @@
break;
}
if (valid) {
- jobject newIntegerObj = env->NewObject(intClazz, initInt, intBandwidth);
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt,
+ intBandwidth));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
}
break;
}
@@ -2655,8 +2616,8 @@
bool valid = true;
switch (interval.getTag()) {
case FrontendGuardInterval::Tag::dvbt: {
- intInterval =
- static_cast<jint>(interval.get<FrontendGuardInterval::Tag::dvbt>());
+ intInterval = static_cast<jint>(
+ interval.get<FrontendGuardInterval::Tag::dvbt>());
break;
}
case FrontendGuardInterval::Tag::isdbt: {
@@ -2665,8 +2626,8 @@
break;
}
case FrontendGuardInterval::Tag::dtmb: {
- intInterval =
- static_cast<jint>(interval.get<FrontendGuardInterval::Tag::dtmb>());
+ intInterval = static_cast<jint>(
+ interval.get<FrontendGuardInterval::Tag::dtmb>());
break;
}
default:
@@ -2674,14 +2635,15 @@
break;
}
if (valid) {
- jobject newIntegerObj = env->NewObject(intClazz, initInt, intInterval);
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt,
+ intInterval));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
}
break;
}
case FrontendStatus::Tag::transmissionMode: {
- jfieldID field = env->GetFieldID(clazz, "mTransmissionMode", "Ljava/lang/Integer;");
+ jfieldID field = env->GetFieldID(clazz, "mTransmissionMode",
+ "Ljava/lang/Integer;");
const FrontendTransmissionMode &transmissionMode =
s.get<FrontendStatus::Tag::transmissionMode>();
jint intTransmissionMode;
@@ -2707,32 +2669,30 @@
break;
}
if (valid) {
- jobject newIntegerObj = env->NewObject(intClazz, initInt, intTransmissionMode);
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt,
+ intTransmissionMode));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
}
break;
}
case FrontendStatus::Tag::uec: {
jfieldID field = env->GetFieldID(clazz, "mUec", "Ljava/lang/Integer;");
- jobject newIntegerObj =
- env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::uec>());
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ ScopedLocalRef newIntegerObj(env,
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::uec>()));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::systemId: {
jfieldID field = env->GetFieldID(clazz, "mSystemId", "Ljava/lang/Integer;");
- jobject newIntegerObj =
- env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::systemId>());
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ ScopedLocalRef newIntegerObj(env,
+ env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::systemId>()));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::interleaving: {
jfieldID field = env->GetFieldID(clazz, "mInterleaving", "[I");
std::vector<FrontendInterleaveMode> v = s.get<FrontendStatus::Tag::interleaving>();
- jintArray valObj = env->NewIntArray(v.size());
+ ScopedLocalRef valObj(env, env->NewIntArray(v.size()));
bool valid = false;
jint in[1];
for (int i = 0; i < v.size(); i++) {
@@ -2741,28 +2701,28 @@
case FrontendInterleaveMode::Tag::atsc3: {
in[0] = static_cast<jint>(
interleaving.get<FrontendInterleaveMode::Tag::atsc3>());
- env->SetIntArrayRegion(valObj, i, 1, in);
+ env->SetIntArrayRegion(valObj.get(), i, 1, in);
valid = true;
break;
}
case FrontendInterleaveMode::Tag::dvbc: {
in[0] = static_cast<jint>(
interleaving.get<FrontendInterleaveMode::Tag::dvbc>());
- env->SetIntArrayRegion(valObj, i, 1, in);
+ env->SetIntArrayRegion(valObj.get(), i, 1, in);
valid = true;
break;
}
case FrontendInterleaveMode::Tag::dtmb: {
in[0] = static_cast<jint>(
interleaving.get<FrontendInterleaveMode::Tag::dtmb>());
- env->SetIntArrayRegion(valObj, i, 1, in);
+ env->SetIntArrayRegion(valObj.get(), i, 1, in);
valid = true;
break;
}
case FrontendInterleaveMode::Tag::isdbt: {
in[0] = static_cast<jint>(
interleaving.get<FrontendInterleaveMode::Tag::isdbt>());
- env->SetIntArrayRegion(valObj, i, 1, in);
+ env->SetIntArrayRegion(valObj.get(), i, 1, in);
valid = true;
break;
}
@@ -2771,31 +2731,28 @@
}
}
if (valid) {
- env->SetObjectField(statusObj, field, valObj);
+ env->SetObjectField(statusObj, field, valObj.get());
}
- env->DeleteLocalRef(valObj);
break;
}
case FrontendStatus::Tag::isdbtSegment: {
jfieldID field = env->GetFieldID(clazz, "mIsdbtSegment", "[I");
std::vector<int32_t> v = s.get<FrontendStatus::Tag::isdbtSegment>();
- jintArray valObj = env->NewIntArray(v.size());
- env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint*>(&v[0]));
+ ScopedLocalRef valObj(env, env->NewIntArray(v.size()));
+ env->SetIntArrayRegion(valObj.get(), 0, v.size(), reinterpret_cast<jint*>(&v[0]));
- env->SetObjectField(statusObj, field, valObj);
- env->DeleteLocalRef(valObj);
+ env->SetObjectField(statusObj, field, valObj.get());
break;
}
case FrontendStatus::Tag::tsDataRate: {
jfieldID field = env->GetFieldID(clazz, "mTsDataRate", "[I");
std::vector<int32_t> v = s.get<FrontendStatus::Tag::tsDataRate>();
- jintArray valObj = env->NewIntArray(v.size());
- env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint *>(&v[0]));
+ ScopedLocalRef valObj(env, env->NewIntArray(v.size()));
+ env->SetIntArrayRegion(valObj.get(), 0, v.size(), reinterpret_cast<jint *>(&v[0]));
- env->SetObjectField(statusObj, field, valObj);
- env->DeleteLocalRef(valObj);
+ env->SetObjectField(statusObj, field, valObj.get());
break;
}
case FrontendStatus::Tag::rollOff: {
@@ -2813,7 +2770,8 @@
break;
}
case FrontendRollOff::Tag::isdbs3: {
- intRollOff = static_cast<jint>(rollOff.get<FrontendRollOff::Tag::isdbs3>());
+ intRollOff = static_cast<jint>(
+ rollOff.get<FrontendRollOff::Tag::isdbs3>());
break;
}
default:
@@ -2821,141 +2779,135 @@
break;
}
if (valid) {
- jobject newIntegerObj = env->NewObject(intClazz, initInt, intRollOff);
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ ScopedLocalRef newIntegerObj(env, env->NewObject(intClazz, initInt,
+ intRollOff));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
}
break;
}
case FrontendStatus::Tag::isMiso: {
jfieldID field = env->GetFieldID(clazz, "mIsMisoEnabled", "Ljava/lang/Boolean;");
- jobject newBooleanObj = env->NewObject(booleanClazz, initBoolean,
- s.get<FrontendStatus::Tag::isMiso>());
- env->SetObjectField(statusObj, field, newBooleanObj);
- env->DeleteLocalRef(newBooleanObj);
+ ScopedLocalRef newBooleanObj(env, env->NewObject(booleanClazz, initBoolean,
+ s.get<FrontendStatus::Tag::isMiso>()));
+ env->SetObjectField(statusObj, field, newBooleanObj.get());
break;
}
case FrontendStatus::Tag::isLinear: {
jfieldID field = env->GetFieldID(clazz, "mIsLinear", "Ljava/lang/Boolean;");
- jobject newBooleanObj = env->NewObject(booleanClazz, initBoolean,
- s.get<FrontendStatus::Tag::isLinear>());
- env->SetObjectField(statusObj, field, newBooleanObj);
- env->DeleteLocalRef(newBooleanObj);
+ ScopedLocalRef newBooleanObj(env, env->NewObject(booleanClazz, initBoolean,
+ s.get<FrontendStatus::Tag::isLinear>()));
+ env->SetObjectField(statusObj, field, newBooleanObj.get());
break;
}
case FrontendStatus::Tag::isShortFrames: {
jfieldID field = env->GetFieldID(clazz, "mIsShortFrames", "Ljava/lang/Boolean;");
- jobject newBooleanObj = env->NewObject(booleanClazz, initBoolean,
- s.get<FrontendStatus::Tag::isShortFrames>());
- env->SetObjectField(statusObj, field, newBooleanObj);
- env->DeleteLocalRef(newBooleanObj);
+ ScopedLocalRef newBooleanObj(env,
+ env->NewObject(booleanClazz, initBoolean,
+ s.get<FrontendStatus::Tag::isShortFrames>()));
+ env->SetObjectField(statusObj, field, newBooleanObj.get());
break;
}
case FrontendStatus::Tag::isdbtMode: {
jfieldID field = env->GetFieldID(clazz, "mIsdbtMode", "Ljava/lang/Integer;");
- jobject newIntegerObj =
- env->NewObject(intClazz, initInt, s.get<FrontendStatus::Tag::isdbtMode>());
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ ScopedLocalRef newIntegerObj(env,
+ env->NewObject(intClazz, initInt,
+ s.get<FrontendStatus::Tag::isdbtMode>()));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::partialReceptionFlag: {
jfieldID field =
- env->GetFieldID(clazz, "mIsdbtPartialReceptionFlag", "Ljava/lang/Integer;");
- jobject newIntegerObj =
+ env->GetFieldID(clazz, "mIsdbtPartialReceptionFlag",
+ "Ljava/lang/Integer;");
+ ScopedLocalRef newIntegerObj(env,
env->NewObject(intClazz, initInt,
- s.get<FrontendStatus::Tag::partialReceptionFlag>());
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ s.get<FrontendStatus::Tag::partialReceptionFlag>()));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::streamIdList: {
jfieldID field = env->GetFieldID(clazz, "mStreamIds", "[I");
std::vector<int32_t> ids = s.get<FrontendStatus::Tag::streamIdList>();
- jintArray valObj = env->NewIntArray(v.size());
- env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint *>(&ids[0]));
+ ScopedLocalRef valObj(env, env->NewIntArray(v.size()));
+ env->SetIntArrayRegion(valObj.get(), 0, v.size(),
+ reinterpret_cast<jint *>(&ids[0]));
- env->SetObjectField(statusObj, field, valObj);
- env->DeleteLocalRef(valObj);
+ env->SetObjectField(statusObj, field, valObj.get());
break;
}
case FrontendStatus::Tag::dvbtCellIds: {
jfieldID field = env->GetFieldID(clazz, "mDvbtCellIds", "[I");
std::vector<int32_t> ids = s.get<FrontendStatus::Tag::dvbtCellIds>();
- jintArray valObj = env->NewIntArray(v.size());
- env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint *>(&ids[0]));
+ ScopedLocalRef valObj(env, env->NewIntArray(v.size()));
+ env->SetIntArrayRegion(valObj.get(), 0, v.size(),
+ reinterpret_cast<jint *>(&ids[0]));
- env->SetObjectField(statusObj, field, valObj);
- env->DeleteLocalRef(valObj);
+ env->SetObjectField(statusObj, field, valObj.get());
break;
}
case FrontendStatus::Tag::allPlpInfo: {
jfieldID field = env->GetFieldID(clazz, "mAllPlpInfo",
- "[Landroid/media/tv/tuner/frontend/Atsc3PlpInfo;");
- jclass plpClazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3PlpInfo");
- jmethodID initPlp = env->GetMethodID(plpClazz, "<init>", "(IZ)V");
+ "[Landroid/media/tv/tuner/frontend/Atsc3PlpInfo;");
+ ScopedLocalRef plpClazz(env,
+ env->FindClass("android/media/tv/tuner/frontend/Atsc3PlpInfo"));
+ jmethodID initPlp = env->GetMethodID(plpClazz.get(), "<init>", "(IZ)V");
vector<FrontendScanAtsc3PlpInfo> plpInfos =
s.get<FrontendStatus::Tag::allPlpInfo>();
- jobjectArray valObj = env->NewObjectArray(plpInfos.size(), plpClazz, nullptr);
+ ScopedLocalRef valObj(env, env->NewObjectArray(plpInfos.size(), plpClazz.get(),
+ nullptr));
for (int i = 0; i < plpInfos.size(); i++) {
- jobject plpObj = env->NewObject(plpClazz, initPlp, plpInfos[i].plpId,
- plpInfos[i].bLlsFlag);
- env->SetObjectArrayElement(valObj, i, plpObj);
- env->DeleteLocalRef(plpObj);
+ ScopedLocalRef plpObj(env, env->NewObject(plpClazz.get(), initPlp,
+ plpInfos[i].plpId,
+ plpInfos[i].bLlsFlag));
+ env->SetObjectArrayElement(valObj.get(), i, plpObj.get());
}
- env->SetObjectField(statusObj, field, valObj);
- env->DeleteLocalRef(valObj);
- env->DeleteLocalRef(plpClazz);
+ env->SetObjectField(statusObj, field, valObj.get());
break;
}
case FrontendStatus::Tag::iptvContentUrl: {
jfieldID field = env->GetFieldID(clazz, "mIptvContentUrl", "Ljava/lang/String;");
std::string iptvContentUrl = s.get<FrontendStatus::Tag::iptvContentUrl>();
- jstring iptvContentUrlUtf8 = env->NewStringUTF(iptvContentUrl.c_str());
- env->SetObjectField(statusObj, field, iptvContentUrlUtf8);
- env->DeleteLocalRef(iptvContentUrlUtf8);
+ ScopedLocalRef iptvContentUrlUtf8(env, env->NewStringUTF(iptvContentUrl.c_str()));
+ env->SetObjectField(statusObj, field, iptvContentUrlUtf8.get());
break;
}
case FrontendStatus::Tag::iptvPacketsLost: {
jfieldID field = env->GetFieldID(clazz, "mIptvPacketsLost", "Ljava/lang/Long;");
- jobject newLongObj =
+ ScopedLocalRef newLongObj(env,
env->NewObject(longClazz, initLong,
- s.get<FrontendStatus::Tag::iptvPacketsLost>());
- env->SetObjectField(statusObj, field, newLongObj);
- env->DeleteLocalRef(newLongObj);
+ s.get<FrontendStatus::Tag::iptvPacketsLost>()));
+ env->SetObjectField(statusObj, field, newLongObj.get());
break;
}
case FrontendStatus::Tag::iptvPacketsReceived: {
- jfieldID field = env->GetFieldID(clazz, "mIptvPacketsReceived", "Ljava/lang/Long;");
- jobject newLongObj =
+ jfieldID field = env->GetFieldID(clazz, "mIptvPacketsReceived",
+ "Ljava/lang/Long;");
+ ScopedLocalRef newLongObj(env,
env->NewObject(longClazz, initLong,
- s.get<FrontendStatus::Tag::iptvPacketsReceived>());
- env->SetObjectField(statusObj, field, newLongObj);
- env->DeleteLocalRef(newLongObj);
+ s.get<FrontendStatus::Tag::iptvPacketsReceived>()));
+ env->SetObjectField(statusObj, field, newLongObj.get());
break;
}
case FrontendStatus::Tag::iptvWorstJitterMs: {
jfieldID field = env->GetFieldID(clazz, "mIptvWorstJitterMs",
"Ljava/lang/Integer;");
- jobject newIntegerObj =
+ ScopedLocalRef newIntegerObj(env,
env->NewObject(intClazz, initInt,
- s.get<FrontendStatus::Tag::iptvWorstJitterMs>());
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ s.get<FrontendStatus::Tag::iptvWorstJitterMs>()));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
case FrontendStatus::Tag::iptvAverageJitterMs: {
jfieldID field = env->GetFieldID(clazz, "mIptvAverageJitterMs",
"Ljava/lang/Integer;");
- jobject newIntegerObj =
+ ScopedLocalRef newIntegerObj(env,
env->NewObject(intClazz, initInt,
- s.get<FrontendStatus::Tag::iptvAverageJitterMs>());
- env->SetObjectField(statusObj, field, newIntegerObj);
- env->DeleteLocalRef(newIntegerObj);
+ s.get<FrontendStatus::Tag::iptvAverageJitterMs>()));
+ env->SetObjectField(statusObj, field, newIntegerObj.get());
break;
}
}
@@ -3089,21 +3041,22 @@
vector<FrontendAtsc3PlpSettings> plps = vector<FrontendAtsc3PlpSettings>(len);
// parse PLP settings
for (int i = 0; i < len; i++) {
- jobject plp = env->GetObjectArrayElement(plpSettings, i);
- int32_t plpId = env->GetIntField(plp, env->GetFieldID(plpClazz, "mPlpId", "I"));
+ ScopedLocalRef plp(env, env->GetObjectArrayElement(plpSettings, i));
+ int32_t plpId = env->GetIntField(plp.get(), env->GetFieldID(plpClazz, "mPlpId", "I"));
FrontendAtsc3Modulation modulation =
static_cast<FrontendAtsc3Modulation>(
- env->GetIntField(plp, env->GetFieldID(plpClazz, "mModulation", "I")));
+ env->GetIntField(plp.get(), env->GetFieldID(plpClazz, "mModulation",
+ "I")));
FrontendAtsc3TimeInterleaveMode interleaveMode =
static_cast<FrontendAtsc3TimeInterleaveMode>(
env->GetIntField(
- plp, env->GetFieldID(plpClazz, "mInterleaveMode", "I")));
+ plp.get(), env->GetFieldID(plpClazz, "mInterleaveMode", "I")));
FrontendAtsc3CodeRate codeRate =
static_cast<FrontendAtsc3CodeRate>(
- env->GetIntField(plp, env->GetFieldID(plpClazz, "mCodeRate", "I")));
+ env->GetIntField(plp.get(), env->GetFieldID(plpClazz, "mCodeRate", "I")));
FrontendAtsc3Fec fec =
static_cast<FrontendAtsc3Fec>(
- env->GetIntField(plp, env->GetFieldID(plpClazz, "mFec", "I")));
+ env->GetIntField(plp.get(), env->GetFieldID(plpClazz, "mFec", "I")));
FrontendAtsc3PlpSettings frontendAtsc3PlpSettings {
.plpId = plpId,
.modulation = modulation,
@@ -3112,7 +3065,6 @@
.fec = fec,
};
plps[i] = frontendAtsc3PlpSettings;
- env->DeleteLocalRef(plp);
}
return plps;
}
@@ -3457,18 +3409,17 @@
"android/media/tv/tuner/frontend/IsdbtFrontendSettings$IsdbtLayerSettings");
frontendIsdbtSettings.layerSettings.resize(len);
for (int i = 0; i < len; i++) {
- jobject layer = env->GetObjectArrayElement(layerSettings, i);
+ ScopedLocalRef layer(env, env->GetObjectArrayElement(layerSettings, i));
frontendIsdbtSettings.layerSettings[i].modulation = static_cast<FrontendIsdbtModulation>(
- env->GetIntField(layer, env->GetFieldID(layerClazz, "mModulation", "I")));
+ env->GetIntField(layer.get(), env->GetFieldID(layerClazz, "mModulation", "I")));
frontendIsdbtSettings.layerSettings[i].timeInterleave =
static_cast<FrontendIsdbtTimeInterleaveMode>(
- env->GetIntField(layer,
+ env->GetIntField(layer.get(),
env->GetFieldID(layerClazz, "mTimeInterleaveMode", "I")));
frontendIsdbtSettings.layerSettings[i].coderate = static_cast<FrontendIsdbtCoderate>(
- env->GetIntField(layer, env->GetFieldID(layerClazz, "mCodeRate", "I")));
+ env->GetIntField(layer.get(), env->GetFieldID(layerClazz, "mCodeRate", "I")));
frontendIsdbtSettings.layerSettings[i].numOfSegment =
- env->GetIntField(layer, env->GetFieldID(layerClazz, "mNumOfSegments", "I"));
- env->DeleteLocalRef(layer);
+ env->GetIntField(layer.get(), env->GetFieldID(layerClazz, "mNumOfSegments", "I"));
}
frontendSettings.set<FrontendSettings::Tag::isdbt>(frontendIsdbtSettings);
@@ -3498,7 +3449,8 @@
env->GetIntField(settings, env->GetFieldID(clazz, "mGuardInterval", "I")));
FrontendDtmbTimeInterleaveMode interleaveMode =
static_cast<FrontendDtmbTimeInterleaveMode>(
- env->GetIntField(settings, env->GetFieldID(clazz, "mTimeInterleaveMode", "I")));
+ env->GetIntField(settings, env->GetFieldID(clazz, "mTimeInterleaveMode",
+ "I")));
FrontendDtmbSettings frontendDtmbSettings{
.frequency = freq,
@@ -3515,7 +3467,8 @@
return frontendSettings;
}
-static DemuxIpAddress getDemuxIpAddress(JNIEnv *env, const jobject& config, const char* className) {
+static DemuxIpAddress getDemuxIpAddress(JNIEnv *env, const jobject& config,
+ const char* className) {
jclass clazz = env->FindClass(className);
jbyteArray jsrcIpAddress = static_cast<jbyteArray>(
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 6b1b6b1..01c998d 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -163,17 +163,19 @@
jmethodID mRestartEventInitID;
jfieldID mMediaEventFieldContextID;
bool mSharedFilter;
- void getSectionEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
- void getMediaEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
- void getPesEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
- void getTsRecordEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
- void getMmtpRecordEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
- void getDownloadEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
- void getIpPayloadEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
- void getTemiEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
- void getScramblingStatusEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
- void getIpCidChangeEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
- void getRestartEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getSectionEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getMediaEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getPesEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getTsRecordEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getMmtpRecordEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getDownloadEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getIpPayloadEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getTemiEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent& event);
+ void getScramblingStatusEvent(const jobjectArray& arr, const int size,
+ const DemuxFilterEvent& event);
+ void getIpCidChangeEvent(const jobjectArray& arr, const int size,
+ const DemuxFilterEvent& event);
+ void getRestartEvent(const jobjectArray& arr, const int size, const DemuxFilterEvent& event);
};
struct JTuner;
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
index 964e4b2..00ccea1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtilsInternal.java
@@ -36,6 +36,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.UserManager.EnforcingUser;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
@@ -118,12 +119,13 @@
return enforcedAdmin;
}
- final int restrictionSource = enforcingUsers.get(0).getUserRestrictionSource();
+ final EnforcingUser enforcingUser = enforcingUsers.get(0);
+ final int restrictionSource = enforcingUser.getUserRestrictionSource();
if (restrictionSource == UserManager.RESTRICTION_SOURCE_SYSTEM) {
return null;
}
- final EnforcedAdmin admin = getProfileOrDeviceOwner(context, userHandle);
+ final EnforcedAdmin admin = getProfileOrDeviceOwner(context, enforcingUser.getUserHandle());
if (admin != null) {
return admin;
}
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-keyguard/layout/keyguard_emergency_carrier_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
index 8bb7877..371670c 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_emergency_carrier_area.xml
@@ -39,7 +39,8 @@
android:ellipsize="marquee"
android:visibility="gone"
android:gravity="center"
- androidprv:allCaps="@bool/kg_use_all_caps" />
+ androidprv:allCaps="@bool/kg_use_all_caps"
+ androidprv:debugLocation="Emergency" />
<com.android.keyguard.EmergencyButton
android:id="@+id/emergency_call_button"
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/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml
index 8b85940..64c4eff 100644
--- a/packages/SystemUI/res/layout/keyguard_status_bar.xml
+++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml
@@ -78,6 +78,7 @@
android:textColor="?attr/wallpaperTextColorSecondary"
android:singleLine="true"
systemui:showMissingSim="true"
- systemui:showAirplaneMode="true" />
+ systemui:showAirplaneMode="true"
+ systemui:debugLocation="Keyguard" />
</com.android.systemui.statusbar.phone.KeyguardStatusBarView>
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/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 8d8fdf0..bd86e51 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -146,6 +146,7 @@
<attr name="allCaps" format="boolean" />
<attr name="showMissingSim" format="boolean" />
<attr name="showAirplaneMode" format="boolean" />
+ <attr name="debugLocation" format="string" />
</declare-styleable>
<declare-styleable name="IlluminationDrawable">
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/CarrierText.java b/packages/SystemUI/src/com/android/keyguard/CarrierText.java
index e4f6e131..87a9b0f 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierText.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierText.java
@@ -33,6 +33,8 @@
private final boolean mShowAirplaneMode;
+ private final String mDebugLocation;
+
public CarrierText(Context context) {
this(context, null);
}
@@ -46,6 +48,7 @@
useAllCaps = a.getBoolean(R.styleable.CarrierText_allCaps, false);
mShowAirplaneMode = a.getBoolean(R.styleable.CarrierText_showAirplaneMode, false);
mShowMissingSim = a.getBoolean(R.styleable.CarrierText_showMissingSim, false);
+ mDebugLocation = a.getString(R.styleable.CarrierText_debugLocation);
} finally {
a.recycle();
}
@@ -70,6 +73,10 @@
return mShowMissingSim;
}
+ public String getDebugLocation() {
+ return mDebugLocation;
+ }
+
private static class CarrierTextTransformationMethod extends SingleLineTransformationMethod {
private final Locale mLocale;
private final boolean mAllCaps;
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
index 997c527..33f9ecd 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java
@@ -53,6 +53,7 @@
mCarrierTextManager = carrierTextManagerBuilder
.setShowAirplaneMode(mView.getShowAirplaneMode())
.setShowMissingSim(mView.getShowMissingSim())
+ .setDebugLocationString(mView.getDebugLocation())
.build();
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
index 1f75e81..a724514 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
@@ -651,6 +651,7 @@
private final CarrierTextManagerLogger mLogger;
private boolean mShowAirplaneMode;
private boolean mShowMissingSim;
+ private String mDebugLocation;
@Inject
public Builder(
@@ -689,14 +690,25 @@
return this;
}
+ /**
+ * To help disambiguate logs, set a location to be used in the LogBuffer calls, e.g.:
+ * "keyguard" or "keyguard emergency status bar"
+ */
+ public Builder setDebugLocationString(String debugLocationString) {
+ mDebugLocation = debugLocationString;
+ return this;
+ }
+
/** Create a CarrierTextManager. */
public CarrierTextManager build() {
+ mLogger.setLocation(mDebugLocation);
return new CarrierTextManager(
mContext, mSeparator, mShowAirplaneMode, mShowMissingSim, mWifiRepository,
mTelephonyManager, mTelephonyListenerManager, mWakefulnessLifecycle,
mMainExecutor, mBgExecutor, mKeyguardUpdateMonitor, mLogger);
}
}
+
/**
* Data structure for passing information to CarrierTextController subscribers
*/
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/keyguard/logging/CarrierTextManagerLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt
index 4001a4e..1978715 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt
@@ -18,16 +18,20 @@
import androidx.annotation.IntDef
import com.android.keyguard.CarrierTextManager.CarrierTextCallbackInfo
-import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
import com.android.systemui.log.dagger.CarrierTextManagerLog
import javax.inject.Inject
/** Logger adapter for [CarrierTextManager] to add detailed messages in a [LogBuffer] */
-@SysUISingleton
class CarrierTextManagerLogger @Inject constructor(@CarrierTextManagerLog val buffer: LogBuffer) {
/**
+ * To help disambiguate carrier text manager instances, set a location string here which will
+ * propagate to [logUpdate] and [logUpdateCarrierTextForReason]
+ */
+ var location: String? = null
+
+ /**
* This method and the methods below trace the execution of CarrierTextManager.updateCarrierText
*/
fun logUpdate(numSubs: Int) {
@@ -35,7 +39,7 @@
TAG,
LogLevel.VERBOSE,
{ int1 = numSubs },
- { "updateCarrierText: numSubs=$int1" },
+ { "updateCarrierText: location=${location ?: "(unknown)"} numSubs=$int1" },
)
}
@@ -99,7 +103,10 @@
TAG,
LogLevel.DEBUG,
{ int1 = reason },
- { "refreshing carrier info for reason: ${reason.reasonMessage()}" }
+ {
+ "refreshing carrier info for reason: ${reason.reasonMessage()}" +
+ " location=${location ?: "(unknown)"}"
+ }
)
}
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/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index e837786..0fdd7ca 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -2112,6 +2112,9 @@
? QUICK_SETTINGS : (
mKeyguardStateController.canDismissLockScreen() ? UNLOCK : BOUNCER_UNLOCK);
if (!isFalseTouch(x, y, interactionType)) {
+ mShadeLog.logFlingExpands(vel, vectorVel, interactionType,
+ this.mFlingAnimationUtils.getMinVelocityPxPerSecond(),
+ mExpandedFraction > 0.5f, mAllowExpandForSmallExpansion);
if (Math.abs(vectorVel) < this.mFlingAnimationUtils.getMinVelocityPxPerSecond()) {
expands = shouldExpandWhenNotFlinging();
} else {
@@ -2914,6 +2917,10 @@
&& mBarState == StatusBarState.SHADE;
}
+ private boolean isPanelVisibleBecauseScrimIsAnimatingOff() {
+ return mUnlockedScreenOffAnimationController.isAnimationPlaying();
+ }
+
@Override
public boolean shouldHideStatusBarIconsWhenExpanded() {
if (mIsLaunchAnimationRunning) {
@@ -3498,7 +3505,7 @@
* gesture), we always play haptic.
*/
private void maybeVibrateOnOpening(boolean openingWithTouch) {
- if (mVibrateOnOpening) {
+ if (mVibrateOnOpening && mBarState != KEYGUARD && mBarState != SHADE_LOCKED) {
if (!openingWithTouch || !mHasVibratedOnOpen) {
mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
mHasVibratedOnOpen = true;
@@ -3973,6 +3980,7 @@
|| isPanelVisibleBecauseOfHeadsUp()
|| mTracking
|| mHeightAnimator != null
+ || isPanelVisibleBecauseScrimIsAnimatingOff()
&& !mIsSpringBackAnimation;
}
@@ -4939,7 +4947,7 @@
mShadeLog.logHasVibrated(mHasVibratedOnOpen, mExpandedFraction);
}
addMovement(event);
- if (!isFullyCollapsed() && !isOnKeyguard()) {
+ if (!isFullyCollapsed()) {
maybeVibrateOnOpening(true /* openingWithTouch */);
}
float h = y - mInitialExpandY;
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/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
index 25073c1b..2b772e3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
@@ -253,6 +253,31 @@
)
}
+ fun logFlingExpands(
+ vel: Float,
+ vectorVel: Float,
+ interactionType: Int,
+ minVelocityPxPerSecond: Float,
+ expansionOverHalf: Boolean,
+ allowExpandForSmallExpansion: Boolean
+ ) {
+ buffer.log(
+ TAG,
+ LogLevel.VERBOSE,
+ {
+ int1 = interactionType
+ long1 = vel.toLong()
+ long2 = vectorVel.toLong()
+ double1 = minVelocityPxPerSecond.toDouble()
+ bool1 = expansionOverHalf
+ bool2 = allowExpandForSmallExpansion
+ },
+ { "NPVC flingExpands called with vel: $long1, vectorVel: $long2, " +
+ "interactionType: $int1, minVelocityPxPerSecond: $double1 " +
+ "expansionOverHalf: $bool1, allowExpandForSmallExpansion: $bool2" }
+ )
+ }
+
fun flingQs(flingType: Int, isClick: Boolean) {
buffer.log(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
index 0ebcfa2..fc1e87a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
@@ -141,6 +141,7 @@
mCarrierTextManager = carrierTextManagerBuilder
.setShowAirplaneMode(false)
.setShowMissingSim(false)
+ .setDebugLocationString("Shade")
.build();
mCarrierConfigTracker = carrierConfigTracker;
mSlotIndexResolver = slotIndexResolver;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index ae7c216..b0f3f59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -93,6 +93,12 @@
private boolean mAppearing;
private float mPulseHeight = MAX_PULSE_HEIGHT;
+ /**
+ * The ExpandableNotificationRow that is pulsing, or the one that was pulsing
+ * when the device started to transition from AOD to LockScreen.
+ */
+ private ExpandableNotificationRow mPulsingRow;
+
/** Fraction of lockscreen to shade animation (on lockscreen swipe down). */
private float mFractionToShade;
@@ -564,6 +570,19 @@
return mPulsing && entry.isAlerting();
}
+ public void setPulsingRow(ExpandableNotificationRow row) {
+ mPulsingRow = row;
+ }
+
+ /**
+ * @param row The row to check
+ * @return true if row is the pulsing row when the device started to transition from AOD to lock
+ * screen
+ */
+ public boolean isPulsingRow(ExpandableView row) {
+ return mPulsingRow == row;
+ }
+
public boolean isPanelTracking() {
return mPanelTracking;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 92d767a..6f1c378 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -548,7 +548,7 @@
ExpandableViewState viewState = view.getViewState();
viewState.location = ExpandableViewState.LOCATION_UNKNOWN;
- final float expansionFraction = getExpansionFractionWithoutShelf(
+ float expansionFraction = getExpansionFractionWithoutShelf(
algorithmState, ambientState);
// Add gap between sections.
@@ -619,6 +619,11 @@
updateViewWithShelf(view, viewState, shelfStart);
}
}
+ // Avoid pulsing notification flicker during AOD to LS
+ // A pulsing notification is already expanded, no need to expand it again with animation
+ if (ambientState.isPulsingRow(view)) {
+ expansionFraction = 1.0f;
+ }
// Clip height of view right before shelf.
viewState.height = (int) (getMaxAllowedChildHeight(view) * expansionFraction);
}
@@ -700,9 +705,11 @@
&& !(child instanceof FooterView);
}
- private void updatePulsingStates(StackScrollAlgorithmState algorithmState,
+ @VisibleForTesting
+ void updatePulsingStates(StackScrollAlgorithmState algorithmState,
AmbientState ambientState) {
int childCount = algorithmState.visibleChildren.size();
+ ExpandableNotificationRow pulsingRow = null;
for (int i = 0; i < childCount; i++) {
View child = algorithmState.visibleChildren.get(i);
if (!(child instanceof ExpandableNotificationRow)) {
@@ -714,6 +721,19 @@
}
ExpandableViewState viewState = row.getViewState();
viewState.hidden = false;
+ pulsingRow = row;
+ }
+
+ // Set AmbientState#pulsingRow to the current pulsing row when on AOD.
+ // Set AmbientState#pulsingRow=null when on lockscreen, since AmbientState#pulsingRow
+ // is only used for skipping the unfurl animation for (the notification that was already
+ // showing at full height on AOD) during the AOD=>lockscreen transition, where
+ // dozeAmount=[1f, 0f). We also need to reset the pulsingRow once it is no longer used
+ // because it will interfere with future unfurling animations - for example, during the
+ // LS=>AOD animation, the pulsingRow may stay at full height when it should squish with the
+ // rest of the stack.
+ if (ambientState.getDozeAmount() == 0.0f || ambientState.getDozeAmount() == 1.0f) {
+ ambientState.setPulsingRow(pulsingRow);
}
}
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/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index c5495e1..a5a9de5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -1106,7 +1106,7 @@
}
@Test
- public void shadeExpanded_inShadeState() {
+ public void shadeFullyExpanded_inShadeState() {
mStatusBarStateController.setState(SHADE);
mNotificationPanelViewController.setExpandedHeight(0);
@@ -1118,7 +1118,7 @@
}
@Test
- public void shadeExpanded_onKeyguard() {
+ public void shadeFullyExpanded_onKeyguard() {
mStatusBarStateController.setState(KEYGUARD);
int transitionDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
@@ -1127,8 +1127,39 @@
}
@Test
- public void shadeExpanded_onShadeLocked() {
+ public void shadeFullyExpanded_onShadeLocked() {
mStatusBarStateController.setState(SHADE_LOCKED);
assertThat(mNotificationPanelViewController.isShadeFullyExpanded()).isTrue();
}
+
+ @Test
+ public void shadeExpanded_whenHasHeight() {
+ int transitionDistance = mNotificationPanelViewController.getMaxPanelTransitionDistance();
+ mNotificationPanelViewController.setExpandedHeight(transitionDistance);
+ assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
+ }
+
+ @Test
+ public void shadeExpanded_whenInstantExpanding() {
+ mNotificationPanelViewController.expand(true);
+ assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
+ }
+
+ @Test
+ public void shadeExpanded_whenHunIsPresent() {
+ when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
+ assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
+ }
+
+ @Test
+ public void shadeExpanded_whenWaitingForExpandGesture() {
+ mNotificationPanelViewController.startWaitingForExpandGesture();
+ assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
+ }
+
+ @Test
+ public void shadeExpanded_whenUnlockedOffscreenAnimationRunning() {
+ when(mUnlockedScreenOffAnimationController.isAnimationPlaying()).thenReturn(true);
+ assertThat(mNotificationPanelViewController.isExpanded()).isTrue();
+ }
}
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/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
index 2ef3d60..57ae621 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
@@ -23,6 +23,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
@@ -109,6 +110,8 @@
.thenReturn(mCarrierTextControllerBuilder);
when(mCarrierTextControllerBuilder.setShowMissingSim(anyBoolean()))
.thenReturn(mCarrierTextControllerBuilder);
+ when(mCarrierTextControllerBuilder.setDebugLocationString(anyString()))
+ .thenReturn(mCarrierTextControllerBuilder);
when(mCarrierTextControllerBuilder.build()).thenReturn(mCarrierTextManager);
doAnswer(invocation -> mCallback = invocation.getArgument(0))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 7f20f1e..e12d179 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -716,6 +716,94 @@
.isLessThan(px(R.dimen.heads_up_pinned_elevation))
}
+ @Test
+ fun aodToLockScreen_hasPulsingNotification_pulsingNotificationRowDoesNotChange() {
+ // Given: Before AOD to LockScreen, there was a pulsing notification
+ val pulsingNotificationView = createPulsingViewMock()
+ val algorithmState = StackScrollAlgorithm.StackScrollAlgorithmState()
+ algorithmState.visibleChildren.add(pulsingNotificationView)
+ ambientState.setPulsingRow(pulsingNotificationView)
+
+ // When: during AOD to LockScreen, any dozeAmount between (0, 1.0) is equivalent as a middle
+ // stage; here we use 0.5 for testing.
+ // stackScrollAlgorithm.updatePulsingStates is called
+ ambientState.dozeAmount = 0.5f
+ stackScrollAlgorithm.updatePulsingStates(algorithmState, ambientState)
+
+ // Then: ambientState.pulsingRow should still be pulsingNotificationView
+ assertTrue(ambientState.isPulsingRow(pulsingNotificationView))
+ }
+
+ @Test
+ fun deviceOnAod_hasPulsingNotification_recordPulsingNotificationRow() {
+ // Given: Device is on AOD, there is a pulsing notification
+ // ambientState.pulsingRow is null before stackScrollAlgorithm.updatePulsingStates
+ ambientState.dozeAmount = 1.0f
+ val pulsingNotificationView = createPulsingViewMock()
+ val algorithmState = StackScrollAlgorithm.StackScrollAlgorithmState()
+ algorithmState.visibleChildren.add(pulsingNotificationView)
+ ambientState.setPulsingRow(null)
+
+ // When: stackScrollAlgorithm.updatePulsingStates is called
+ stackScrollAlgorithm.updatePulsingStates(algorithmState, ambientState)
+
+ // Then: ambientState.pulsingRow should record the pulsingNotificationView
+ assertTrue(ambientState.isPulsingRow(pulsingNotificationView))
+ }
+
+ @Test
+ fun deviceOnLockScreen_hasPulsingNotificationBefore_clearPulsingNotificationRowRecord() {
+ // Given: Device finished AOD to LockScreen, there was a pulsing notification, and
+ // ambientState.pulsingRow was not null before AOD to LockScreen
+ // pulsingNotificationView.showingPulsing() returns false since the device is on LockScreen
+ ambientState.dozeAmount = 0.0f
+ val pulsingNotificationView = createPulsingViewMock()
+ whenever(pulsingNotificationView.showingPulsing()).thenReturn(false)
+ val algorithmState = StackScrollAlgorithm.StackScrollAlgorithmState()
+ algorithmState.visibleChildren.add(pulsingNotificationView)
+ ambientState.setPulsingRow(pulsingNotificationView)
+
+ // When: stackScrollAlgorithm.updatePulsingStates is called
+ stackScrollAlgorithm.updatePulsingStates(algorithmState, ambientState)
+
+ // Then: ambientState.pulsingRow should be null
+ assertTrue(ambientState.isPulsingRow(null))
+ }
+
+ @Test
+ fun aodToLockScreen_hasPulsingNotification_pulsingNotificationRowShowAtFullHeight() {
+ // Given: Before AOD to LockScreen, there was a pulsing notification
+ val pulsingNotificationView = createPulsingViewMock()
+ val algorithmState = StackScrollAlgorithm.StackScrollAlgorithmState()
+ algorithmState.visibleChildren.add(pulsingNotificationView)
+ ambientState.setPulsingRow(pulsingNotificationView)
+
+ // When: during AOD to LockScreen, any dozeAmount between (0, 1.0) is equivalent as a middle
+ // stage; here we use 0.5 for testing. The expansionFraction is also 0.5.
+ // stackScrollAlgorithm.resetViewStates is called.
+ ambientState.dozeAmount = 0.5f
+ setExpansionFractionWithoutShelfDuringAodToLockScreen(
+ ambientState,
+ algorithmState,
+ fraction = 0.5f
+ )
+ stackScrollAlgorithm.resetViewStates(ambientState, 0)
+
+ // Then: pulsingNotificationView should show at full height
+ assertEquals(
+ stackScrollAlgorithm.getMaxAllowedChildHeight(pulsingNotificationView),
+ pulsingNotificationView.viewState.height
+ )
+
+ // After: reset dozeAmount and expansionFraction
+ ambientState.dozeAmount = 0f
+ setExpansionFractionWithoutShelfDuringAodToLockScreen(
+ ambientState,
+ algorithmState,
+ fraction = 1f
+ )
+ }
+
private fun createHunViewMock(
isShadeOpen: Boolean,
fullyVisible: Boolean,
@@ -744,6 +832,29 @@
headsUpIsVisible = fullyVisible
}
+ private fun createPulsingViewMock(
+ ) =
+ mock<ExpandableNotificationRow>().apply {
+ whenever(this.viewState).thenReturn(ExpandableViewState())
+ whenever(this.showingPulsing()).thenReturn(true)
+ }
+
+ private fun setExpansionFractionWithoutShelfDuringAodToLockScreen(
+ ambientState: AmbientState,
+ algorithmState: StackScrollAlgorithm.StackScrollAlgorithmState,
+ fraction: Float
+ ) {
+ // showingShelf: false
+ algorithmState.firstViewInShelf = null
+ // scrimPadding: 0, because device is on lock screen
+ ambientState.setStatusBarState(StatusBarState.KEYGUARD)
+ ambientState.dozeAmount = 0.0f
+ // set stackEndHeight and stackHeight
+ // ExpansionFractionWithoutShelf == stackHeight / stackEndHeight
+ ambientState.stackEndHeight = 100f
+ ambientState.stackHeight = ambientState.stackEndHeight * fraction
+ }
+
private fun resetViewStates_expansionChanging_notificationAlphaUpdated(
expansionFraction: Float,
expectedAlpha: Float,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 544828a..bfa397f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -18894,12 +18894,14 @@
@Override
public void waitForBroadcastIdle() {
- waitForBroadcastIdle(LOG_WRITER_INFO);
+ waitForBroadcastIdle(LOG_WRITER_INFO, false);
}
- public void waitForBroadcastIdle(@NonNull PrintWriter pw) {
+ void waitForBroadcastIdle(@NonNull PrintWriter pw, boolean flushBroadcastLoopers) {
enforceCallingPermission(permission.DUMP, "waitForBroadcastIdle()");
- BroadcastLoopers.waitForIdle(pw);
+ if (flushBroadcastLoopers) {
+ BroadcastLoopers.waitForIdle(pw);
+ }
for (BroadcastQueue queue : mBroadcastQueues) {
queue.waitForIdle(pw);
}
@@ -18912,7 +18914,7 @@
waitForBroadcastBarrier(LOG_WRITER_INFO, false, false);
}
- public void waitForBroadcastBarrier(@NonNull PrintWriter pw,
+ void waitForBroadcastBarrier(@NonNull PrintWriter pw,
boolean flushBroadcastLoopers, boolean flushApplicationThreads) {
enforceCallingPermission(permission.DUMP, "waitForBroadcastBarrier()");
if (flushBroadcastLoopers) {
@@ -18930,7 +18932,7 @@
* Wait for all pending {@link IApplicationThread} events to be processed in
* all currently running apps.
*/
- public void waitForApplicationBarrier(@NonNull PrintWriter pw) {
+ void waitForApplicationBarrier(@NonNull PrintWriter pw) {
final CountDownLatch finishedLatch = new CountDownLatch(1);
final AtomicInteger pingCount = new AtomicInteger(0);
final AtomicInteger pongCount = new AtomicInteger(0);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 979874e..add22bd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -3447,7 +3447,17 @@
int runWaitForBroadcastIdle(PrintWriter pw) throws RemoteException {
pw = new PrintWriter(new TeeWriter(LOG_WRITER_INFO, pw));
- mInternal.waitForBroadcastIdle(pw);
+ boolean flushBroadcastLoopers = false;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--flush-broadcast-loopers")) {
+ flushBroadcastLoopers = true;
+ } else {
+ getErrPrintWriter().println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ }
+ mInternal.waitForBroadcastIdle(pw, flushBroadcastLoopers);
return 0;
}
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 87214de..030d596 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -247,6 +247,26 @@
private static final long DEFAULT_DELAY_URGENT_MILLIS = -120_000;
/**
+ * For {@link BroadcastQueueModernImpl}: Delay to apply to broadcasts to
+ * foreground processes, typically a negative value to indicate they should be
+ * executed before most other pending broadcasts.
+ */
+ public long DELAY_FOREGROUND_PROC_MILLIS = DEFAULT_DELAY_FOREGROUND_PROC_MILLIS;
+ private static final String KEY_DELAY_FOREGROUND_PROC_MILLIS =
+ "bcast_delay_foreground_proc_millis";
+ private static final long DEFAULT_DELAY_FOREGROUND_PROC_MILLIS = -120_000;
+
+ /**
+ * For {@link BroadcastQueueModernImpl}: Delay to apply to broadcasts to
+ * persistent processes, typically a negative value to indicate they should be
+ * executed before most other pending broadcasts.
+ */
+ public long DELAY_PERSISTENT_PROC_MILLIS = DEFAULT_DELAY_FOREGROUND_PROC_MILLIS;
+ private static final String KEY_DELAY_PERSISTENT_PROC_MILLIS =
+ "bcast_delay_persistent_proc_millis";
+ private static final long DEFAULT_DELAY_PERSISTENT_PROC_MILLIS = -120_000;
+
+ /**
* For {@link BroadcastQueueModernImpl}: Maximum number of complete
* historical broadcasts to retain for debugging purposes.
*/
@@ -411,6 +431,10 @@
DEFAULT_DELAY_CACHED_MILLIS);
DELAY_URGENT_MILLIS = getDeviceConfigLong(KEY_DELAY_URGENT_MILLIS,
DEFAULT_DELAY_URGENT_MILLIS);
+ DELAY_FOREGROUND_PROC_MILLIS = getDeviceConfigLong(KEY_DELAY_FOREGROUND_PROC_MILLIS,
+ DEFAULT_DELAY_FOREGROUND_PROC_MILLIS);
+ DELAY_PERSISTENT_PROC_MILLIS = getDeviceConfigLong(KEY_DELAY_PERSISTENT_PROC_MILLIS,
+ DEFAULT_DELAY_PERSISTENT_PROC_MILLIS);
MAX_HISTORY_COMPLETE_SIZE = getDeviceConfigInt(KEY_MAX_HISTORY_COMPLETE_SIZE,
DEFAULT_MAX_HISTORY_COMPLETE_SIZE);
MAX_HISTORY_SUMMARY_SIZE = getDeviceConfigInt(KEY_MAX_HISTORY_SUMMARY_SIZE,
@@ -463,6 +487,10 @@
TimeUtils.formatDuration(DELAY_CACHED_MILLIS)).println();
pw.print(KEY_DELAY_URGENT_MILLIS,
TimeUtils.formatDuration(DELAY_URGENT_MILLIS)).println();
+ pw.print(KEY_DELAY_FOREGROUND_PROC_MILLIS,
+ TimeUtils.formatDuration(DELAY_FOREGROUND_PROC_MILLIS)).println();
+ pw.print(KEY_DELAY_PERSISTENT_PROC_MILLIS,
+ TimeUtils.formatDuration(DELAY_PERSISTENT_PROC_MILLIS)).println();
pw.print(KEY_MAX_HISTORY_COMPLETE_SIZE, MAX_HISTORY_COMPLETE_SIZE).println();
pw.print(KEY_MAX_HISTORY_SUMMARY_SIZE, MAX_HISTORY_SUMMARY_SIZE).println();
pw.print(KEY_MAX_CONSECUTIVE_URGENT_DISPATCHES,
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 2803b4b..3ac2b2b 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -1098,8 +1098,11 @@
mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS;
mRunnableAtReason = REASON_INSTRUMENTED;
} else if (mUidForeground) {
- mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS;
+ mRunnableAt = runnableAt + constants.DELAY_FOREGROUND_PROC_MILLIS;
mRunnableAtReason = REASON_FOREGROUND;
+ } else if (mProcessPersistent) {
+ mRunnableAt = runnableAt + constants.DELAY_PERSISTENT_PROC_MILLIS;
+ mRunnableAtReason = REASON_PERSISTENT;
} else if (mCountOrdered > 0) {
mRunnableAt = runnableAt;
mRunnableAtReason = REASON_CONTAINS_ORDERED;
@@ -1112,9 +1115,6 @@
} else if (mCountManifest > 0) {
mRunnableAt = runnableAt;
mRunnableAtReason = REASON_CONTAINS_MANIFEST;
- } else if (mProcessPersistent) {
- mRunnableAt = runnableAt;
- mRunnableAtReason = REASON_PERSISTENT;
} else if (mUidCached) {
if (r.deferUntilActive) {
// All enqueued broadcasts are deferrable, defer
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/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index dd422c3..355981a 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -11053,6 +11053,11 @@
dumpAccessibilityServiceUids(pw);
dumpAssistantServicesUids(pw);
+ pw.print(" supportsBluetoothVariableLatency=");
+ pw.println(AudioSystem.supportsBluetoothVariableLatency());
+ pw.print(" isBluetoothVariableLatencyEnabled=");
+ pw.println(AudioSystem.isBluetoothVariableLatencyEnabled());
+
dumpAudioPolicies(pw);
mDynPolicyLogger.dump(pw);
mPlaybackMonitor.dump(pw);
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/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index e5c50e6..4edc8bc 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -23,7 +23,6 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerInternal;
-import android.text.TextUtils;
import android.util.ArraySet;
import android.util.SparseArray;
import android.view.Display;
@@ -333,6 +332,10 @@
return mPrimaryDisplayDevice != null;
}
+ boolean isDirtyLocked() {
+ return mDirty;
+ }
+
/**
* Updates the {@link DisplayGroup} to which the logical display belongs.
*
@@ -341,8 +344,7 @@
public void updateDisplayGroupIdLocked(int groupId) {
if (groupId != mDisplayGroupId) {
mDisplayGroupId = groupId;
- mBaseDisplayInfo.displayGroupId = groupId;
- mInfo.set(null);
+ mDirty = true;
}
}
@@ -932,18 +934,6 @@
return mDisplayGroupName;
}
- /**
- * Returns whether a display group other than the default display group needs to be assigned.
- *
- * <p>If display group name is empty or {@code Display.FLAG_OWN_DISPLAY_GROUP} is set, the
- * display is assigned to the default display group.
- */
- public boolean needsOwnDisplayGroupLocked() {
- DisplayInfo info = getDisplayInfoLocked();
- return (info.flags & Display.FLAG_OWN_DISPLAY_GROUP) != 0
- || !TextUtils.isEmpty(mDisplayGroupName);
- }
-
public void dumpLocked(PrintWriter pw) {
pw.println("mDisplayId=" + mDisplayId);
pw.println("mIsEnabled=" + mIsEnabled);
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 254441c2..d01b03f 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -679,7 +679,9 @@
for (int i = mLogicalDisplays.size() - 1; i >= 0; i--) {
final int displayId = mLogicalDisplays.keyAt(i);
LogicalDisplay display = mLogicalDisplays.valueAt(i);
+ assignDisplayGroupLocked(display);
+ boolean wasDirty = display.isDirtyLocked();
mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked());
display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo);
@@ -713,19 +715,14 @@
// The display is new.
} else if (!wasPreviouslyUpdated) {
Slog.i(TAG, "Adding new display: " + displayId + ": " + newDisplayInfo);
- assignDisplayGroupLocked(display);
mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_ADDED);
// Underlying displays device has changed to a different one.
} else if (!TextUtils.equals(mTempDisplayInfo.uniqueId, newDisplayInfo.uniqueId)) {
- // FLAG_OWN_DISPLAY_GROUP could have changed, recalculate just in case
- assignDisplayGroupLocked(display);
mLogicalDisplaysToUpdate.put(displayId, LOGICAL_DISPLAY_EVENT_SWAPPED);
// Something about the display device has changed.
- } else if (!mTempDisplayInfo.equals(newDisplayInfo)) {
- // FLAG_OWN_DISPLAY_GROUP could have changed, recalculate just in case
- assignDisplayGroupLocked(display);
+ } else if (wasDirty || !mTempDisplayInfo.equals(newDisplayInfo)) {
// If only the hdr/sdr ratio changed, then send just the event for that case
if ((diff == DisplayDeviceInfo.DIFF_HDR_SDR_RATIO)) {
mLogicalDisplaysToUpdate.put(displayId,
@@ -851,9 +848,18 @@
}
}
+ /** This method should be called before LogicalDisplay.updateLocked,
+ * DisplayInfo in LogicalDisplay (display.getDisplayInfoLocked()) is not updated yet,
+ * and should not be used directly or indirectly in this method */
private void assignDisplayGroupLocked(LogicalDisplay display) {
+ if (!display.isValidLocked()) { // null check for display.mPrimaryDisplayDevice
+ return;
+ }
+ // updated primary device directly from LogicalDisplay (not from DisplayInfo)
+ final DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
+ // final in LogicalDisplay
final int displayId = display.getDisplayIdLocked();
- final String primaryDisplayUniqueId = display.getPrimaryDisplayDeviceLocked().getUniqueId();
+ final String primaryDisplayUniqueId = displayDevice.getUniqueId();
final Integer linkedDeviceUniqueId =
mVirtualDeviceDisplayMapping.get(primaryDisplayUniqueId);
@@ -866,8 +872,17 @@
}
final DisplayGroup oldGroup = getDisplayGroupLocked(groupId);
- // Get the new display group if a change is needed
- final boolean needsOwnDisplayGroup = display.needsOwnDisplayGroupLocked();
+ // groupName directly from LogicalDisplay (not from DisplayInfo)
+ final String groupName = display.getDisplayGroupNameLocked();
+ // DisplayDeviceInfo is safe to use, it is updated earlier
+ final DisplayDeviceInfo displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked();
+ // Get the new display group if a change is needed, if display group name is empty and
+ // {@code DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP} is not set, the display is assigned
+ // to the default display group.
+ final boolean needsOwnDisplayGroup =
+ (displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0
+ || !TextUtils.isEmpty(groupName);
+
final boolean hasOwnDisplayGroup = groupId != Display.DEFAULT_DISPLAY_GROUP;
final boolean needsDeviceDisplayGroup =
!needsOwnDisplayGroup && linkedDeviceUniqueId != null;
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/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 4e043aa..7e88e13 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2854,9 +2854,8 @@
UserHandle.USER_NULL, UserManager.RESTRICTION_SOURCE_SYSTEM));
}
- synchronized (mRestrictionsLock) {
- result.addAll(mDevicePolicyUserRestrictions.getEnforcingUsers(restrictionKey, userId));
- }
+ result.addAll(getDevicePolicyManagerInternal()
+ .getUserRestrictionSources(restrictionKey, userId));
return result;
}
diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java
index 2cbe8db..7a5664f 100644
--- a/services/core/java/com/android/server/policy/AppOpsPolicy.java
+++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java
@@ -37,7 +37,6 @@
import android.os.IBinder;
import android.os.PackageTagsList;
import android.os.Process;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.service.voice.VoiceInteractionManagerInternal;
import android.service.voice.VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity;
@@ -69,8 +68,6 @@
private static final String ACTIVITY_RECOGNITION_TAGS =
"android:activity_recognition_allow_listed_tags";
private static final String ACTIVITY_RECOGNITION_TAGS_SEPARATOR = ";";
- private static final boolean SYSPROP_HOTWORD_DETECTION_SERVICE_REQUIRED =
- SystemProperties.getBoolean("ro.hotword.detection_service_required", false);
@NonNull
private final Object mLock = new Object();
@@ -204,11 +201,8 @@
private static boolean isHotwordDetectionServiceRequired(PackageManager pm) {
// The HotwordDetectionService APIs aren't ready yet for Auto or TV.
- if (pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
- || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
- return false;
- }
- return SYSPROP_HOTWORD_DETECTION_SERVICE_REQUIRED;
+ return !(pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK));
}
@Override
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/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 778951a..98ee98b 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -248,7 +248,10 @@
Slog.e(TAG, "WM sent Transaction to organized, but never received" +
" commit callback. Application ANR likely to follow.");
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- onCommitted(merged);
+ synchronized (mWm.mGlobalLock) {
+ onCommitted(merged.mNativeObject != 0
+ ? merged : mWm.mTransactionFactory.get());
+ }
}
};
CommitCallback callback = new CommitCallback();
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index b808a55d..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/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index df968f9..cf7c718 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -605,6 +605,22 @@
}
/**
+ * Retrieves the values set for the provided {@code policyDefinition} by each admin.
+ */
+ @NonNull
+ <V> LinkedHashMap<EnforcingAdmin, PolicyValue<V>> getGlobalPoliciesSetByAdmins(
+ @NonNull PolicyDefinition<V> policyDefinition) {
+ Objects.requireNonNull(policyDefinition);
+
+ synchronized (mLock) {
+ if (!hasGlobalPolicyLocked(policyDefinition)) {
+ return new LinkedHashMap<>();
+ }
+ return getGlobalPolicyStateLocked(policyDefinition).getPoliciesSetByAdmins();
+ }
+ }
+
+ /**
* Returns the policies set by the given admin that share the same
* {@link PolicyKey#getIdentifier()} as the provided {@code policyDefinition}.
*
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f875e34..2846b39 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -410,6 +410,7 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.UserManager.EnforcingUser;
import android.os.UserManager.UserRestrictionSource;
import android.os.storage.StorageManager;
import android.permission.AdminPermissionControlParams;
@@ -16346,6 +16347,39 @@
return List.of(bundle);
});
}
+
+ public List<EnforcingUser> getUserRestrictionSources(String restriction,
+ @UserIdInt int userId) {
+ PolicyDefinition<Boolean> policy =
+ PolicyDefinition.getPolicyDefinitionForUserRestriction(restriction);
+
+ Set<EnforcingAdmin> localAdmins =
+ mDevicePolicyEngine.getLocalPoliciesSetByAdmins(policy, userId).keySet();
+
+ Set<EnforcingAdmin> globalAdmins =
+ mDevicePolicyEngine.getGlobalPoliciesSetByAdmins(policy).keySet();
+
+ List<EnforcingUser> enforcingUsers = new ArrayList();
+ enforcingUsers.addAll(getEnforcingUsers(localAdmins));
+ enforcingUsers.addAll(getEnforcingUsers(globalAdmins));
+ return enforcingUsers;
+ }
+
+ private List<EnforcingUser> getEnforcingUsers(Set<EnforcingAdmin> admins) {
+ List<EnforcingUser> enforcingUsers = new ArrayList();
+ ComponentName deviceOwner = mOwners.getDeviceOwnerComponent();
+ for (EnforcingAdmin admin : admins) {
+ if (deviceOwner != null
+ && deviceOwner.getPackageName().equals(admin.getPackageName())) {
+ enforcingUsers.add(new EnforcingUser(admin.getUserId(),
+ UserManager.RESTRICTION_SOURCE_DEVICE_OWNER));
+ } else {
+ enforcingUsers.add(new EnforcingUser(admin.getUserId(),
+ UserManager.RESTRICTION_SOURCE_PROFILE_OWNER));
+ }
+ }
+ return enforcingUsers;
+ }
}
private Intent createShowAdminSupportIntent(int userId) {
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/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 581fe4a..f47954b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -537,6 +537,30 @@
assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
}
+ @Test
+ public void testRunnableAt_persistentProc() {
+ final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants, PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+
+ final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+ final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick,
+ List.of(makeMockRegisteredReceiver()));
+ enqueueOrReplaceBroadcast(queue, timeTickRecord, 0);
+
+ assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+
+ doReturn(true).when(mProcess).isPersistent();
+ queue.setProcessAndUidState(mProcess, false, false);
+ assertThat(queue.getRunnableAt()).isLessThan(timeTickRecord.enqueueTime);
+ assertEquals(BroadcastProcessQueue.REASON_PERSISTENT, queue.getRunnableAtReason());
+
+ doReturn(false).when(mProcess).isPersistent();
+ queue.setProcessAndUidState(mProcess, false, false);
+ assertThat(queue.getRunnableAt()).isGreaterThan(timeTickRecord.enqueueTime);
+ assertEquals(BroadcastProcessQueue.REASON_NORMAL, queue.getRunnableAtReason());
+ }
+
/**
* Verify that a cached process that would normally be delayed becomes
* immediately runnable when the given broadcast is enqueued.
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 9cd22dd..b19a13c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -402,13 +402,15 @@
JobStatus rescheduledJob = mService.getRescheduleJobForFailureLocked(originalJob,
JobParameters.STOP_REASON_DEVICE_STATE,
JobParameters.INTERNAL_STOP_REASON_DEVICE_THERMAL);
- assertEquals(nowElapsed + initialBackoffMs, rescheduledJob.getEarliestRunTime());
+ assertEquals(JobStatus.NO_EARLIEST_RUNTIME, rescheduledJob.getEarliestRunTime());
assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
// failure = 0, systemStop = 2
rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
JobParameters.STOP_REASON_DEVICE_STATE,
JobParameters.INTERNAL_STOP_REASON_PREEMPT);
+ assertEquals(JobStatus.NO_EARLIEST_RUNTIME, rescheduledJob.getEarliestRunTime());
+ assertEquals(JobStatus.NO_LATEST_RUNTIME, rescheduledJob.getLatestRunTimeElapsed());
// failure = 0, systemStop = 3
rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob,
JobParameters.STOP_REASON_CONSTRAINT_CHARGING,
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/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
index f6cf571..30ff8ba 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
@@ -17,6 +17,7 @@
package com.android.server.display;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
@@ -191,6 +192,19 @@
}
@Test
+ public void testUpdateLayoutLimitedRefreshRate_setsDirtyFlag() {
+ SurfaceControl.RefreshRateRange layoutLimitedRefreshRate =
+ new SurfaceControl.RefreshRateRange(0, 120);
+ assertFalse(mLogicalDisplay.isDirtyLocked());
+
+ mLogicalDisplay.updateLayoutLimitedRefreshRateLocked(layoutLimitedRefreshRate);
+ assertTrue(mLogicalDisplay.isDirtyLocked());
+
+ mLogicalDisplay.updateLocked(mDeviceRepo);
+ assertFalse(mLogicalDisplay.isDirtyLocked());
+ }
+
+ @Test
public void testUpdateRefreshRateThermalThrottling() {
SparseArray<SurfaceControl.RefreshRateRange> refreshRanges = new SparseArray<>();
refreshRanges.put(0, new SurfaceControl.RefreshRateRange(0, 120));
@@ -207,6 +221,45 @@
}
@Test
+ public void testUpdateRefreshRateThermalThrottling_setsDirtyFlag() {
+ SparseArray<SurfaceControl.RefreshRateRange> refreshRanges = new SparseArray<>();
+ refreshRanges.put(0, new SurfaceControl.RefreshRateRange(0, 120));
+ assertFalse(mLogicalDisplay.isDirtyLocked());
+
+ mLogicalDisplay.updateThermalRefreshRateThrottling(refreshRanges);
+ assertTrue(mLogicalDisplay.isDirtyLocked());
+
+ mLogicalDisplay.updateLocked(mDeviceRepo);
+ assertFalse(mLogicalDisplay.isDirtyLocked());
+ }
+
+ @Test
+ public void testUpdateDisplayGroupIdLocked() {
+ int newId = 999;
+ DisplayInfo info1 = mLogicalDisplay.getDisplayInfoLocked();
+ mLogicalDisplay.updateDisplayGroupIdLocked(newId);
+ DisplayInfo info2 = mLogicalDisplay.getDisplayInfoLocked();
+ // Display info should only be updated when updateLocked is called
+ assertEquals(info2, info1);
+
+ mLogicalDisplay.updateLocked(mDeviceRepo);
+ DisplayInfo info3 = mLogicalDisplay.getDisplayInfoLocked();
+ assertNotEquals(info3, info2);
+ assertEquals(newId, info3.displayGroupId);
+ }
+
+ @Test
+ public void testUpdateDisplayGroupIdLocked_setsDirtyFlag() {
+ assertFalse(mLogicalDisplay.isDirtyLocked());
+
+ mLogicalDisplay.updateDisplayGroupIdLocked(99);
+ assertTrue(mLogicalDisplay.isDirtyLocked());
+
+ mLogicalDisplay.updateLocked(mDeviceRepo);
+ assertFalse(mLogicalDisplay.isDirtyLocked());
+ }
+
+ @Test
public void testSetThermalBrightnessThrottlingDataId() {
String brightnessThrottlingDataId = "throttling_data_id";
DisplayInfo info1 = mLogicalDisplay.getDisplayInfoLocked();
@@ -220,4 +273,15 @@
assertNotEquals(info3, info2);
assertEquals(brightnessThrottlingDataId, info3.thermalBrightnessThrottlingDataId);
}
+
+ @Test
+ public void testSetThermalBrightnessThrottlingDataId_setsDirtyFlag() {
+ assertFalse(mLogicalDisplay.isDirtyLocked());
+
+ mLogicalDisplay.setThermalBrightnessThrottlingDataIdLocked("99");
+ assertTrue(mLogicalDisplay.isDirtyLocked());
+
+ mLogicalDisplay.updateLocked(mDeviceRepo);
+ assertFalse(mLogicalDisplay.isDirtyLocked());
+ }
}
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;