Merge "Use UserManagerInternal#hasUserRestriction() in IMMS"
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 812e422..0a99c36 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -784,6 +784,8 @@
mNonInteractiveUiTimeout = other.mNonInteractiveUiTimeout;
mInteractiveUiTimeout = other.mInteractiveUiTimeout;
flags = other.flags;
+ // NOTE: Ensure that only properties that are safe to be modified by the service itself
+ // are included here (regardless of hidden setters, etc.).
}
private boolean isRequestAccessibilityButtonChangeEnabled(IPlatformCompat platformCompat) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 422b6a3..32d0d75 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -87,11 +87,13 @@
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.StrictMode;
import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
+import android.service.voice.VoiceInteractionSession;
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
@@ -155,6 +157,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ToolbarActionBar;
import com.android.internal.app.WindowDecorActionBar;
@@ -1602,6 +1605,25 @@
return callbacks;
}
+ private void notifyVoiceInteractionManagerServiceActivityEvent(
+ @VoiceInteractionSession.VoiceInteractionActivityEventType int type) {
+
+ final IVoiceInteractionManagerService service =
+ IVoiceInteractionManagerService.Stub.asInterface(
+ ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
+ if (service == null) {
+ Log.w(TAG, "notifyVoiceInteractionManagerServiceActivityEvent: Can not get "
+ + "VoiceInteractionManagerService");
+ return;
+ }
+
+ try {
+ service.notifyActivityEventChanged(mToken, type);
+ } catch (RemoteException e) {
+ // Empty
+ }
+ }
+
/**
* Called when the activity is starting. This is where most initialization
* should go: calling {@link #setContentView(int)} to inflate the
@@ -1877,6 +1899,9 @@
mCalled = true;
notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_START);
+
+ notifyVoiceInteractionManagerServiceActivityEvent(
+ VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_START);
}
/**
@@ -2020,6 +2045,12 @@
final Window win = getWindow();
if (win != null) win.makeActive();
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true);
+
+ // Because the test case "com.android.launcher3.jank.BinderTests#testPressHome" doesn't
+ // allow any binder call in onResume, we call this method in onPostResume.
+ notifyVoiceInteractionManagerServiceActivityEvent(
+ VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_RESUME);
+
mCalled = true;
}
@@ -2395,6 +2426,10 @@
getAutofillClientController().onActivityPaused();
notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_PAUSE);
+
+ notifyVoiceInteractionManagerServiceActivityEvent(
+ VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_PAUSE);
+
mCalled = true;
}
@@ -2624,6 +2659,9 @@
getAutofillClientController().onActivityStopped(mIntent, mChangingConfigurations);
mEnterAnimationComplete = false;
+
+ notifyVoiceInteractionManagerServiceActivityEvent(
+ VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_STOP);
}
/**
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 8da9631..7d19ed4 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -747,10 +747,9 @@
*/
public interface VoiceInteractionManagerProvider {
/**
- * Notifies the service when a high-level activity event has been changed, for example,
- * an activity was resumed or stopped.
+ * Notifies the service when an activity is destroyed.
*/
- void notifyActivityEventChanged();
+ void notifyActivityDestroyed(IBinder activityToken);
}
/**
diff --git a/core/java/android/app/time/TimeState.java b/core/java/android/app/time/TimeState.java
index 15411e5..01c869d 100644
--- a/core/java/android/app/time/TimeState.java
+++ b/core/java/android/app/time/TimeState.java
@@ -63,6 +63,12 @@
return new TimeState(unixEpochTime, userShouldConfirmId);
}
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mUnixEpochTime, 0);
+ dest.writeBoolean(mUserShouldConfirmTime);
+ }
+
/** @hide */
@Nullable
public static TimeState parseCommandLineArgs(@NonNull ShellCommand cmd) {
@@ -119,12 +125,6 @@
return 0;
}
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeParcelable(mUnixEpochTime, 0);
- dest.writeBoolean(mUserShouldConfirmTime);
- }
-
@NonNull
public UnixEpochTime getUnixEpochTime() {
return mUnixEpochTime;
diff --git a/core/java/android/app/time/TimeZoneState.java b/core/java/android/app/time/TimeZoneState.java
index efdff81..8e87111 100644
--- a/core/java/android/app/time/TimeZoneState.java
+++ b/core/java/android/app/time/TimeZoneState.java
@@ -58,11 +58,17 @@
}
private static TimeZoneState createFromParcel(Parcel in) {
- String zoneId = in.readString();
+ String zoneId = in.readString8();
boolean userShouldConfirmId = in.readBoolean();
return new TimeZoneState(zoneId, userShouldConfirmId);
}
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mId);
+ dest.writeBoolean(mUserShouldConfirmId);
+ }
+
/** @hide */
@Nullable
public static TimeZoneState parseCommandLineArgs(@NonNull ShellCommand cmd) {
@@ -107,12 +113,6 @@
return 0;
}
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString(mId);
- dest.writeBoolean(mUserShouldConfirmId);
- }
-
@NonNull
public String getId() {
return mId;
diff --git a/core/java/android/app/time/UnixEpochTime.java b/core/java/android/app/time/UnixEpochTime.java
index f1a176e..576bf64 100644
--- a/core/java/android/app/time/UnixEpochTime.java
+++ b/core/java/android/app/time/UnixEpochTime.java
@@ -94,7 +94,6 @@
}
/** Returns the unix epoch time value. See {@link UnixEpochTime} for more information. */
- @Nullable
public long getUnixEpochTimeMillis() {
return mUnixEpochTimeMillis;
}
@@ -109,7 +108,7 @@
}
UnixEpochTime that = (UnixEpochTime) o;
return mElapsedRealtimeMillis == that.mElapsedRealtimeMillis
- && Objects.equals(mUnixEpochTimeMillis, that.mUnixEpochTimeMillis);
+ && mUnixEpochTimeMillis == that.mUnixEpochTimeMillis;
}
@Override
@@ -125,32 +124,19 @@
+ '}';
}
- public static final @NonNull Creator<UnixEpochTime> CREATOR =
- new ClassLoaderCreator<UnixEpochTime>() {
+ public static final @NonNull Creator<UnixEpochTime> CREATOR = new Creator<>() {
+ @Override
+ public UnixEpochTime createFromParcel(@NonNull Parcel source) {
+ long elapsedRealtimeMillis = source.readLong();
+ long unixEpochTimeMillis = source.readLong();
+ return new UnixEpochTime(elapsedRealtimeMillis, unixEpochTimeMillis);
+ }
- @Override
- public UnixEpochTime createFromParcel(@NonNull Parcel source) {
- return createFromParcel(source, null);
- }
-
- @Override
- public UnixEpochTime createFromParcel(
- @NonNull Parcel source, @Nullable ClassLoader classLoader) {
- long elapsedRealtimeMillis = source.readLong();
- long unixEpochTimeMillis = source.readLong();
- return new UnixEpochTime(elapsedRealtimeMillis, unixEpochTimeMillis);
- }
-
- @Override
- public UnixEpochTime[] newArray(int size) {
- return new UnixEpochTime[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
+ @Override
+ public UnixEpochTime[] newArray(int size) {
+ return new UnixEpochTime[size];
+ }
+ };
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -158,6 +144,11 @@
dest.writeLong(mUnixEpochTimeMillis);
}
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
/**
* Creates a new Unix epoch time value at {@code elapsedRealtimeTimeMillis} by adjusting this
* Unix epoch time by the difference between the elapsed realtime value supplied and the one
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index a13eada..36ac1a0 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -279,7 +279,6 @@
mPrimaryId = Objects.requireNonNull(primaryId);
mSecondaryIds = secondaryIds;
mVendorIds = vendorIds;
- Arrays.sort(mSecondaryIds);
}
/**
@@ -525,7 +524,9 @@
// vendorIds are ignored for equality
// programType can be inferred from primaryId, thus not checked
return mPrimaryId.equals(other.getPrimaryId())
- && Arrays.equals(mSecondaryIds, other.mSecondaryIds);
+ && mSecondaryIds.length == other.mSecondaryIds.length
+ && Arrays.asList(mSecondaryIds).containsAll(
+ Arrays.asList(other.mSecondaryIds));
}
private ProgramSelector(Parcel in) {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index d71b023..adeb722 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -296,8 +296,10 @@
* New in version 35:
* - Fixed bug that was not reporting high cellular tx power correctly
* - Added out of service and emergency service modes to data connection types
+ * New in version 36:
+ * - Added PowerStats and CPU time-in-state data
*/
- static final int CHECKIN_VERSION = 35;
+ static final int CHECKIN_VERSION = 36;
/**
* Old version, we hit 9 and ran out of room, need to remove.
@@ -1810,6 +1812,36 @@
}
/**
+ * CPU usage for a given UID.
+ */
+ public static final class CpuUsageDetails {
+ /**
+ * Descriptions of CPU power brackets, see PowerProfile.getCpuPowerBracketDescription
+ */
+ public String[] cpuBracketDescriptions;
+ public int uid;
+ /**
+ * The delta, in milliseconds, per CPU power bracket, from the previous record for the
+ * same UID.
+ */
+ public long[] cpuUsageMs;
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ UserHandle.formatUid(sb, uid);
+ sb.append(": ");
+ for (int bracket = 0; bracket < cpuUsageMs.length; bracket++) {
+ if (bracket != 0) {
+ sb.append(", ");
+ }
+ sb.append(cpuUsageMs[bracket]);
+ }
+ return sb.toString();
+ }
+ }
+
+ /**
* Battery history record.
*/
public static final class HistoryItem {
@@ -1952,6 +1984,9 @@
// Non-null when there is measured energy information
public MeasuredEnergyDetails measuredEnergyDetails;
+ // Non-null when there is CPU usage information
+ public CpuUsageDetails cpuUsageDetails;
+
public static final int EVENT_FLAG_START = 0x8000;
public static final int EVENT_FLAG_FINISH = 0x4000;
@@ -2161,6 +2196,7 @@
eventTag = null;
tagsFirstOccurrence = false;
measuredEnergyDetails = null;
+ cpuUsageDetails = null;
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -2211,6 +2247,7 @@
tagsFirstOccurrence = o.tagsFirstOccurrence;
currentTime = o.currentTime;
measuredEnergyDetails = o.measuredEnergyDetails;
+ cpuUsageDetails = o.cpuUsageDetails;
}
public boolean sameNonEvent(HistoryItem o) {
@@ -6808,6 +6845,25 @@
private String printNextItem(HistoryItem rec, long baseTime, boolean checkin,
boolean verbose) {
StringBuilder item = new StringBuilder();
+
+ if (rec.cpuUsageDetails != null
+ && rec.cpuUsageDetails.cpuBracketDescriptions != null
+ && checkin) {
+ String[] descriptions = rec.cpuUsageDetails.cpuBracketDescriptions;
+ for (int bracket = 0; bracket < descriptions.length; bracket++) {
+ item.append(BATTERY_STATS_CHECKIN_VERSION);
+ item.append(',');
+ item.append(HISTORY_DATA);
+ item.append(",0,XB,");
+ item.append(descriptions.length);
+ item.append(',');
+ item.append(bracket);
+ item.append(',');
+ item.append(descriptions[bracket]);
+ item.append("\n");
+ }
+ }
+
if (!checkin) {
item.append(" ");
TimeUtils.formatDuration(
@@ -7000,14 +7056,6 @@
item.append("\"");
}
}
- if ((rec.states2 & HistoryItem.STATE2_EXTENSIONS_FLAG) != 0) {
- if (!checkin) {
- item.append(" ext=");
- if (rec.measuredEnergyDetails != null) {
- item.append("E");
- }
- }
- }
if (rec.eventCode != HistoryItem.EVENT_NONE) {
item.append(checkin ? "," : " ");
if ((rec.eventCode&HistoryItem.EVENT_FLAG_START) != 0) {
@@ -7036,6 +7084,58 @@
item.append("\"");
}
}
+ boolean firstExtension = true;
+ if (rec.measuredEnergyDetails != null) {
+ firstExtension = false;
+ if (!checkin) {
+ item.append(" ext=energy:");
+ item.append(rec.measuredEnergyDetails);
+ } else {
+ item.append(",XE");
+ for (int i = 0; i < rec.measuredEnergyDetails.consumers.length; i++) {
+ if (rec.measuredEnergyDetails.chargeUC[i] != POWER_DATA_UNAVAILABLE) {
+ item.append(',');
+ item.append(rec.measuredEnergyDetails.consumers[i].name);
+ item.append('=');
+ item.append(rec.measuredEnergyDetails.chargeUC[i]);
+ }
+ }
+ }
+ }
+ if (rec.cpuUsageDetails != null) {
+ if (!checkin) {
+ if (!firstExtension) {
+ item.append("\n ");
+ }
+ String[] descriptions = rec.cpuUsageDetails.cpuBracketDescriptions;
+ if (descriptions != null) {
+ for (int bracket = 0; bracket < descriptions.length; bracket++) {
+ item.append(" ext=cpu-bracket:");
+ item.append(bracket);
+ item.append(":");
+ item.append(descriptions[bracket]);
+ item.append("\n ");
+ }
+ }
+ item.append(" ext=cpu:");
+ item.append(rec.cpuUsageDetails);
+ } else {
+ if (!firstExtension) {
+ item.append('\n');
+ item.append(BATTERY_STATS_CHECKIN_VERSION);
+ item.append(',');
+ item.append(HISTORY_DATA);
+ item.append(",0");
+ }
+ item.append(",XC,");
+ item.append(rec.cpuUsageDetails.uid);
+ for (int i = 0; i < rec.cpuUsageDetails.cpuUsageMs.length; i++) {
+ item.append(',');
+ item.append(rec.cpuUsageDetails.cpuUsageMs[i]);
+ }
+ }
+ firstExtension = false;
+ }
item.append("\n");
if (rec.stepDetails != null) {
if (!checkin) {
@@ -7132,25 +7232,6 @@
item.append("\n");
}
}
- if (rec.measuredEnergyDetails != null) {
- if (!checkin) {
- item.append(" Energy: ");
- item.append(rec.measuredEnergyDetails);
- item.append("\n");
- } else {
- item.append(BATTERY_STATS_CHECKIN_VERSION); item.append(',');
- item.append(HISTORY_DATA); item.append(",0,XE");
- for (int i = 0; i < rec.measuredEnergyDetails.consumers.length; i++) {
- if (rec.measuredEnergyDetails.chargeUC[i] != POWER_DATA_UNAVAILABLE) {
- item.append(',');
- item.append(rec.measuredEnergyDetails.consumers[i].name);
- item.append('=');
- item.append(rec.measuredEnergyDetails.chargeUC[i]);
- }
- }
- item.append("\n");
- }
- }
oldState = rec.states;
oldState2 = rec.states2;
// Clear High Tx Power Flag for volta positioning
@@ -7158,7 +7239,6 @@
rec.states2 &= ~HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG;
}
}
-
return item.toString();
}
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 82e1d5f0..fc47778 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -20,6 +20,7 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -73,6 +74,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
@@ -146,6 +149,25 @@
*/
public static final int SHOW_SOURCE_AUTOMOTIVE_SYSTEM_UI = 1 << 7;
+ /** @hide */
+ public static final int VOICE_INTERACTION_ACTIVITY_EVENT_START = 1;
+ /** @hide */
+ public static final int VOICE_INTERACTION_ACTIVITY_EVENT_RESUME = 2;
+ /** @hide */
+ public static final int VOICE_INTERACTION_ACTIVITY_EVENT_PAUSE = 3;
+ /** @hide */
+ public static final int VOICE_INTERACTION_ACTIVITY_EVENT_STOP = 4;
+
+ /** @hide */
+ @IntDef(prefix = { "VOICE_INTERACTION_ACTIVITY_EVENT_" }, value = {
+ VOICE_INTERACTION_ACTIVITY_EVENT_START,
+ VOICE_INTERACTION_ACTIVITY_EVENT_RESUME,
+ VOICE_INTERACTION_ACTIVITY_EVENT_PAUSE,
+ VOICE_INTERACTION_ACTIVITY_EVENT_STOP
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VoiceInteractionActivityEventType{}
+
final Context mContext;
final HandlerCaller mHandlerCaller;
diff --git a/core/java/android/app/usage/TimeSparseArray.java b/core/java/android/util/TimeSparseArray.java
similarity index 65%
rename from core/java/android/app/usage/TimeSparseArray.java
rename to core/java/android/util/TimeSparseArray.java
index 2bd6b24..6efc683 100644
--- a/core/java/android/app/usage/TimeSparseArray.java
+++ b/core/java/android/util/TimeSparseArray.java
@@ -14,13 +14,11 @@
* under the License.
*/
-package android.app.usage;
-
-import android.util.LongSparseArray;
-import android.util.Slog;
+package android.util;
/**
* An array that indexes by a long timestamp, representing milliseconds since the epoch.
+ * @param <E> The type of values this container maps to a timestamp.
*
* {@hide}
*/
@@ -29,53 +27,41 @@
private boolean mWtfReported;
- public TimeSparseArray() {
- super();
- }
-
/**
* Finds the index of the first element whose timestamp is greater or equal to
* the given time.
*
* @param time The timestamp for which to search the array.
- * @return The index of the matched element, or -1 if no such match exists.
+ * @return The smallest {@code index} for which {@code (keyAt(index) >= timeStamp)} is
+ * {@code true}, or {@link #size() size} if no such {@code index} exists.
*/
public int closestIndexOnOrAfter(long time) {
- // This is essentially a binary search, except that if no match is found
- // the closest index is returned.
final int size = size();
+ int result = size;
int lo = 0;
int hi = size - 1;
- int mid = -1;
- long key = -1;
while (lo <= hi) {
- mid = lo + ((hi - lo) / 2);
- key = keyAt(mid);
+ final int mid = lo + ((hi - lo) / 2);
+ final long key = keyAt(mid);
if (time > key) {
lo = mid + 1;
} else if (time < key) {
hi = mid - 1;
+ result = mid;
} else {
return mid;
}
}
-
- if (time < key) {
- return mid;
- } else if (time > key && lo < size) {
- return lo;
- } else {
- return -1;
- }
+ return result;
}
/**
* {@inheritDoc}
*
- * <p> As this container is being used only to keep {@link android.util.AtomicFile files},
- * there should not be any collisions. Reporting a {@link Slog#wtf(String, String)} in case that
- * happens, as that will lead to one whole file being dropped.
+ * <p> This container can store only one value for each timestamp. And so ideally, the caller
+ * should ensure that there are no collisions. Reporting a {@link Slog#wtf(String, String)}
+ * if that happens, as that will lead to the previous value being overwritten.
*/
@Override
public void put(long key, E value) {
@@ -93,16 +79,13 @@
* the given time.
*
* @param time The timestamp for which to search the array.
- * @return The index of the matched element, or -1 if no such match exists.
+ * @return The largest {@code index} for which {@code (keyAt(index) <= timeStamp)} is
+ * {@code true}, or -1 if no such {@code index} exists.
*/
public int closestIndexOnOrBefore(long time) {
final int index = closestIndexOnOrAfter(time);
- if (index < 0) {
- // Everything is larger, so we use the last element, or -1 if the list is empty.
- return size() - 1;
- }
- if (keyAt(index) == time) {
+ if (index < size() && keyAt(index) == time) {
return index;
}
return index - 1;
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 5ec7962..b5dff5e 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1273,8 +1273,8 @@
public static final int AXIS_GENERIC_16 = 47;
// NOTE: If you add a new axis here you must also add it to:
- // native/include/android/input.h
- // frameworks/base/include/ui/KeycodeLabels.h
+ // frameworks/native/include/android/input.h
+ // frameworks/native/libs/input/InputEventLabels.cpp
// Symbolic names of all axes.
private static final SparseArray<String> AXIS_SYMBOLIC_NAMES = new SparseArray<String>();
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 641d1a1..fbdd325 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -132,8 +132,11 @@
*/
public static final int FLAG_IS_BEHIND_STARTING_WINDOW = 1 << 14;
+ /** This change happened underneath something else. */
+ public static final int FLAG_IS_OCCLUDED = 1 << 15;
+
/** The first unused bit. This can be used by remotes to attach custom flags to this change. */
- public static final int FLAG_FIRST_CUSTOM = 1 << 15;
+ public static final int FLAG_FIRST_CUSTOM = 1 << 16;
/** @hide */
@IntDef(prefix = { "FLAG_" }, value = {
@@ -153,6 +156,7 @@
FLAG_CROSS_PROFILE_OWNER_THUMBNAIL,
FLAG_CROSS_PROFILE_WORK_THUMBNAIL,
FLAG_IS_BEHIND_STARTING_WINDOW,
+ FLAG_IS_OCCLUDED,
FLAG_FIRST_CUSTOM
})
public @interface ChangeFlags {}
@@ -362,6 +366,9 @@
if ((flags & FLAG_IS_BEHIND_STARTING_WINDOW) != 0) {
sb.append(sb.length() == 0 ? "" : "|").append("IS_BEHIND_STARTING_WINDOW");
}
+ if ((flags & FLAG_IS_OCCLUDED) != 0) {
+ sb.append(sb.length() == 0 ? "" : "|").append("IS_OCCLUDED");
+ }
if ((flags & FLAG_FIRST_CUSTOM) != 0) {
sb.append(sb.length() == 0 ? "" : "|").append("FIRST_CUSTOM");
}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 8d51c9c..bbcf982 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -18,6 +18,8 @@
import android.content.ComponentName;
import android.content.Intent;
+import android.hardware.soundtrigger.KeyphraseMetadata;
+import android.hardware.soundtrigger.SoundTrigger;
import android.media.AudioFormat;
import android.media.permission.Identity;
import android.os.Bundle;
@@ -25,18 +27,17 @@
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.SharedMemory;
+import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
+import android.service.voice.IVoiceInteractionService;
+import android.service.voice.IVoiceInteractionSession;
+import android.service.voice.VisibleActivityInfo;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVoiceActionCheckCallback;
-import com.android.internal.app.IVoiceInteractionSessionShowCallback;
-import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.IVoiceInteractionSessionListener;
+import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractionSoundTriggerSession;
-import android.hardware.soundtrigger.KeyphraseMetadata;
-import android.hardware.soundtrigger.SoundTrigger;
-import android.service.voice.IVoiceInteractionService;
-import android.service.voice.IVoiceInteractionSession;
-import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
+import com.android.internal.app.IVoiceInteractor;
interface IVoiceInteractionManagerService {
void showSession(in Bundle sessionArgs, int flags, String attributionTag);
@@ -303,4 +304,14 @@
* Notifies when the session window is shown or hidden.
*/
void setSessionWindowVisible(in IBinder token, boolean visible);
+
+ /**
+ * Notifies when the Activity lifecycle event changed.
+ *
+ * @param activityToken The token of activity.
+ * @param type The type of lifecycle event of the activity lifecycle.
+ */
+ oneway void notifyActivityEventChanged(
+ in IBinder activityToken,
+ int type);
}
diff --git a/core/java/com/android/internal/display/BrightnessSynchronizer.java b/core/java/com/android/internal/display/BrightnessSynchronizer.java
index 627631a..d503904 100644
--- a/core/java/com/android/internal/display/BrightnessSynchronizer.java
+++ b/core/java/com/android/internal/display/BrightnessSynchronizer.java
@@ -115,7 +115,7 @@
Slog.i(TAG, "Setting initial brightness to default value of: " + defaultBrightness);
}
- mBrightnessSyncObserver.startObserving();
+ mBrightnessSyncObserver.startObserving(mHandler);
mHandler.sendEmptyMessageAtTime(MSG_RUN_UPDATE, mClock.uptimeMillis());
}
@@ -482,30 +482,31 @@
}
};
- private final ContentObserver mContentObserver = new ContentObserver(mHandler) {
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- if (selfChange) {
- return;
+ private ContentObserver createBrightnessContentObserver(Handler handler) {
+ return new ContentObserver(handler) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ if (selfChange) {
+ return;
+ }
+ if (BRIGHTNESS_URI.equals(uri)) {
+ handleBrightnessChangeInt(getScreenBrightnessInt());
+ }
}
- if (BRIGHTNESS_URI.equals(uri)) {
- handleBrightnessChangeInt(getScreenBrightnessInt());
- }
- }
- };
+ };
+ }
boolean isObserving() {
return mIsObserving;
}
- void startObserving() {
+ void startObserving(Handler handler) {
final ContentResolver cr = mContext.getContentResolver();
- cr.registerContentObserver(BRIGHTNESS_URI, false, mContentObserver,
- UserHandle.USER_ALL);
- mDisplayManager.registerDisplayListener(mListener, mHandler,
+ cr.registerContentObserver(BRIGHTNESS_URI, false,
+ createBrightnessContentObserver(handler), UserHandle.USER_ALL);
+ mDisplayManager.registerDisplayListener(mListener, handler,
DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS);
mIsObserving = true;
}
-
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 5523344..0614cd2 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -21,6 +21,7 @@
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.BatteryStats.BitDescription;
+import android.os.BatteryStats.CpuUsageDetails;
import android.os.BatteryStats.HistoryItem;
import android.os.BatteryStats.HistoryStepDetails;
import android.os.BatteryStats.HistoryTag;
@@ -122,6 +123,8 @@
static final int EXTENSION_MEASURED_ENERGY_HEADER_FLAG = 0x00000001;
static final int EXTENSION_MEASURED_ENERGY_FLAG = 0x00000002;
+ static final int EXTENSION_CPU_USAGE_HEADER_FLAG = 0x00000004;
+ static final int EXTENSION_CPU_USAGE_FLAG = 0x00000008;
private final Parcel mHistoryBuffer;
private final File mSystemDir;
@@ -194,6 +197,7 @@
private long mTrackRunningHistoryUptimeMs = 0;
private long mHistoryBaseTimeMs;
private boolean mMeasuredEnergyHeaderWritten = false;
+ private boolean mCpuUsageHeaderWritten = false;
private byte mLastHistoryStepLevel = 0;
@@ -351,6 +355,7 @@
mTrackRunningHistoryElapsedRealtimeMs = 0;
mTrackRunningHistoryUptimeMs = 0;
mMeasuredEnergyHeaderWritten = false;
+ mCpuUsageHeaderWritten = false;
mHistoryBuffer.setDataSize(0);
mHistoryBuffer.setDataPosition(0);
@@ -1180,7 +1185,7 @@
final int idx = code & HistoryItem.EVENT_TYPE_MASK;
final String prefix = (code & HistoryItem.EVENT_FLAG_START) != 0 ? "+" :
- (code & HistoryItem.EVENT_FLAG_FINISH) != 0 ? "-" : "";
+ (code & HistoryItem.EVENT_FLAG_FINISH) != 0 ? "-" : "";
final String[] names = BatteryStats.HISTORY_EVENT_NAMES;
if (idx < 0 || idx >= names.length) return;
@@ -1191,6 +1196,17 @@
}
/**
+ * Records CPU usage by a specific UID. The recorded data is the delta from
+ * the previous record for the same UID.
+ */
+ public void recordCpuUsage(long elapsedRealtimeMs, long uptimeMs,
+ CpuUsageDetails cpuUsageDetails) {
+ mHistoryCur.cpuUsageDetails = cpuUsageDetails;
+ mHistoryCur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ }
+
+ /**
* Writes changes to a HistoryItem state bitmap to Atrace.
*/
private void recordTraceCounters(int oldval, int newval, BitDescription[] descriptions) {
@@ -1338,6 +1354,7 @@
entry.setValue(entry.getValue() | BatteryStatsHistory.TAG_FIRST_OCCURRENCE_FLAG);
}
mMeasuredEnergyHeaderWritten = false;
+ mCpuUsageHeaderWritten = false;
// Make a copy of mHistoryCur.
HistoryItem copy = new HistoryItem();
@@ -1377,6 +1394,7 @@
cur.eventTag = null;
cur.tagsFirstOccurrence = false;
cur.measuredEnergyDetails = null;
+ cur.cpuUsageDetails = null;
if (DEBUG) {
Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
+ " now " + mHistoryBuffer.dataPosition()
@@ -1502,12 +1520,18 @@
extensionFlags |= BatteryStatsHistory.EXTENSION_MEASURED_ENERGY_HEADER_FLAG;
}
}
+ if (cur.cpuUsageDetails != null) {
+ extensionFlags |= EXTENSION_CPU_USAGE_FLAG;
+ if (!mCpuUsageHeaderWritten) {
+ extensionFlags |= BatteryStatsHistory.EXTENSION_CPU_USAGE_HEADER_FLAG;
+ }
+ }
if (extensionFlags != 0) {
cur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG;
} else {
cur.states2 &= ~HistoryItem.STATE2_EXTENSIONS_FLAG;
}
- final boolean state2IntChanged = cur.states2 != last.states2;
+ final boolean state2IntChanged = cur.states2 != last.states2 || extensionFlags != 0;
if (state2IntChanged) {
firstToken |= BatteryStatsHistory.DELTA_STATE2_FLAG;
}
@@ -1645,6 +1669,20 @@
dest.writeLong(chargeUC);
}
}
+
+ if (cur.cpuUsageDetails != null) {
+ if (DEBUG) {
+ Slog.i(TAG, "WRITE DELTA: cpuUsageDetails=" + cur.cpuUsageDetails);
+ }
+ if (!mCpuUsageHeaderWritten) {
+ dest.writeStringArray(cur.cpuUsageDetails.cpuBracketDescriptions);
+ mCpuUsageHeaderWritten = true;
+ }
+ dest.writeInt(cur.cpuUsageDetails.uid);
+ for (long cpuUsageMs: cur.cpuUsageDetails.cpuUsageMs) {
+ dest.writeLong(cpuUsageMs);
+ }
+ }
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
index ee3d15b..2429a88 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -34,6 +34,7 @@
new BatteryStats.HistoryStepDetails();
private final SparseArray<BatteryStats.HistoryTag> mHistoryTags = new SparseArray<>();
private BatteryStats.MeasuredEnergyDetails mMeasuredEnergyDetails;
+ private BatteryStats.CpuUsageDetails mCpuUsageDetails;
public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history) {
mBatteryStatsHistory = history;
@@ -229,9 +230,35 @@
mMeasuredEnergyDetails.chargeUC[i] = src.readLong();
}
cur.measuredEnergyDetails = mMeasuredEnergyDetails;
+ } else {
+ cur.measuredEnergyDetails = null;
+ }
+
+ if ((extensionFlags & BatteryStatsHistory.EXTENSION_CPU_USAGE_HEADER_FLAG) != 0) {
+ mCpuUsageDetails = new BatteryStats.CpuUsageDetails();
+ mCpuUsageDetails.cpuBracketDescriptions = src.readStringArray();
+ mCpuUsageDetails.cpuUsageMs =
+ new long[mCpuUsageDetails.cpuBracketDescriptions.length];
+ } else if (mCpuUsageDetails != null) {
+ mCpuUsageDetails.cpuBracketDescriptions = null;
+ }
+
+ if ((extensionFlags & BatteryStatsHistory.EXTENSION_CPU_USAGE_FLAG) != 0) {
+ if (mCpuUsageDetails == null) {
+ throw new IllegalStateException("CpuUsageDetails without a header");
+ }
+
+ mCpuUsageDetails.uid = src.readInt();
+ for (int i = 0; i < mCpuUsageDetails.cpuUsageMs.length; i++) {
+ mCpuUsageDetails.cpuUsageMs[i] = src.readLong();
+ }
+ cur.cpuUsageDetails = mCpuUsageDetails;
+ } else {
+ cur.cpuUsageDetails = null;
}
} else {
cur.measuredEnergyDetails = null;
+ cur.cpuUsageDetails = null;
}
}
diff --git a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
index 07a8998..de3edeb 100644
--- a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
@@ -33,7 +33,6 @@
import java.nio.file.Paths;
import java.util.Arrays;
-@VisibleForTesting(visibility = PACKAGE)
public class KernelSingleUidTimeReader {
private static final String TAG = KernelSingleUidTimeReader.class.getName();
private static final boolean DBG = false;
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 55a26fe..953b36b 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -1508,8 +1508,7 @@
STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
STRONG_AUTH_REQUIRED_AFTER_TIMEOUT,
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
- STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT,
- SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED})
+ STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT})
@Retention(RetentionPolicy.SOURCE)
public @interface StrongAuthFlags {}
@@ -1562,12 +1561,6 @@
public static final int STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT = 0x80;
/**
- * Some authentication is required because the trustagent either timed out or was disabled
- * manually.
- */
- public static final int SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED = 0x100;
-
- /**
* Strong auth flags that do not prevent biometric methods from being accepted as auth.
* If any other flags are set, biometric authentication is disabled.
*/
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 061eb48..47faf2a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3667,10 +3667,6 @@
is interactive. -->
<bool name="config_volumeHushGestureEnabled">true</bool>
- <!-- Name of the component to handle network policy notifications. If present,
- disables NetworkPolicyManagerService's presentation of data-usage notifications. -->
- <string translatable="false" name="config_networkPolicyNotificationComponent"></string>
-
<!-- The BT name of the keyboard packaged with the device. If this is defined, SystemUI will
automatically try to pair with it when the device exits tablet mode. -->
<string translatable="false" name="config_packagedKeyboardName"></string>
diff --git a/core/tests/coretests/src/android/app/time/TimeStateTest.java b/core/tests/coretests/src/android/app/time/TimeStateTest.java
index a032290..bce0909 100644
--- a/core/tests/coretests/src/android/app/time/TimeStateTest.java
+++ b/core/tests/coretests/src/android/app/time/TimeStateTest.java
@@ -16,12 +16,12 @@
package android.app.time;
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
import static android.app.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import android.os.Parcel;
import android.os.ShellCommand;
import androidx.test.runner.AndroidJUnit4;
@@ -60,19 +60,8 @@
@Test
public void testParceling() {
UnixEpochTime time = new UnixEpochTime(1, 2);
- TimeState value = new TimeState(time, true);
- Parcel parcel = Parcel.obtain();
- try {
- parcel.writeParcelable(value, 0);
-
- parcel.setDataPosition(0);
-
- TimeState stringValueCopy =
- parcel.readParcelable(null /* classLoader */, TimeState.class);
- assertEquals(value, stringValueCopy);
- } finally {
- parcel.recycle();
- }
+ assertRoundTripParcelable(new TimeState(time, true));
+ assertRoundTripParcelable(new TimeState(time, false));
}
@Test(expected = IllegalArgumentException.class)
diff --git a/core/tests/coretests/src/android/app/time/TimeZoneStateTest.java b/core/tests/coretests/src/android/app/time/TimeZoneStateTest.java
index 9786bb0..35a9dbc 100644
--- a/core/tests/coretests/src/android/app/time/TimeZoneStateTest.java
+++ b/core/tests/coretests/src/android/app/time/TimeZoneStateTest.java
@@ -16,12 +16,12 @@
package android.app.time;
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
import static android.app.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import android.os.Parcel;
import android.os.ShellCommand;
import androidx.test.runner.AndroidJUnit4;
@@ -59,19 +59,8 @@
@Test
public void testParceling() {
- TimeZoneState value = new TimeZoneState("Europe/London", true);
- Parcel parcel = Parcel.obtain();
- try {
- parcel.writeParcelable(value, 0);
-
- parcel.setDataPosition(0);
-
- TimeZoneState stringValueCopy =
- parcel.readParcelable(null /* classLoader */, TimeZoneState.class);
- assertEquals(value, stringValueCopy);
- } finally {
- parcel.recycle();
- }
+ assertRoundTripParcelable(new TimeZoneState("Europe/London", true));
+ assertRoundTripParcelable(new TimeZoneState("Europe/London", false));
}
@Test(expected = IllegalArgumentException.class)
diff --git a/core/tests/coretests/src/android/app/time/UnixEpochTimeTest.java b/core/tests/coretests/src/android/app/time/UnixEpochTimeTest.java
index cd75348..3ab01f3 100644
--- a/core/tests/coretests/src/android/app/time/UnixEpochTimeTest.java
+++ b/core/tests/coretests/src/android/app/time/UnixEpochTimeTest.java
@@ -16,12 +16,12 @@
package android.app.time;
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
import static android.app.timezonedetector.ShellCommandTestSupport.createShellCommandWithArgsAndOptions;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
-import android.os.Parcel;
import android.os.ShellCommand;
import androidx.test.runner.AndroidJUnit4;
@@ -57,19 +57,7 @@
@Test
public void testParceling() {
- UnixEpochTime value = new UnixEpochTime(1000, 1);
- Parcel parcel = Parcel.obtain();
- try {
- parcel.writeParcelable(value, 0);
-
- parcel.setDataPosition(0);
-
- UnixEpochTime stringValueCopy =
- parcel.readParcelable(null /* classLoader */, UnixEpochTime.class);
- assertEquals(value, stringValueCopy);
- } finally {
- parcel.recycle();
- }
+ assertRoundTripParcelable(new UnixEpochTime(1000, 1));
}
@Test(expected = IllegalArgumentException.class)
diff --git a/core/tests/coretests/src/android/util/TimeSparseArrayTest.java b/core/tests/coretests/src/android/util/TimeSparseArrayTest.java
new file mode 100644
index 0000000..c8e2364
--- /dev/null
+++ b/core/tests/coretests/src/android/util/TimeSparseArrayTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link TimeSparseArray}.
+ * This class only tests subclass specific functionality. Tests for the super class
+ * {@link LongSparseArray} should be covered under {@link LongSparseArrayTest}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TimeSparseArrayTest {
+
+ @Test
+ public void closestIndexOnOrAfter() {
+ final TimeSparseArray<Object> timeSparseArray = new TimeSparseArray<>();
+
+ // Values don't matter for this test.
+ timeSparseArray.put(51, new Object());
+ timeSparseArray.put(10, new Object());
+ timeSparseArray.put(59, new Object());
+
+ assertThat(timeSparseArray.size()).isEqualTo(3);
+
+ // Testing any number arbitrarily smaller than 10.
+ assertThat(timeSparseArray.closestIndexOnOrAfter(-141213)).isEqualTo(0);
+ for (long time = -43; time <= 10; time++) {
+ assertThat(timeSparseArray.closestIndexOnOrAfter(time)).isEqualTo(0);
+ }
+
+ for (long time = 11; time <= 51; time++) {
+ assertThat(timeSparseArray.closestIndexOnOrAfter(time)).isEqualTo(1);
+ }
+
+ for (long time = 52; time <= 59; time++) {
+ assertThat(timeSparseArray.closestIndexOnOrAfter(time)).isEqualTo(2);
+ }
+
+ for (long time = 60; time <= 102; time++) {
+ assertThat(timeSparseArray.closestIndexOnOrAfter(time)).isEqualTo(3);
+ }
+ // Testing any number arbitrarily larger than 59.
+ assertThat(timeSparseArray.closestIndexOnOrAfter(15332)).isEqualTo(3);
+ }
+
+ @Test
+ public void closestIndexOnOrBefore() {
+ final TimeSparseArray<Object> timeSparseArray = new TimeSparseArray<>();
+
+ // Values don't matter for this test.
+ timeSparseArray.put(21, new Object());
+ timeSparseArray.put(4, new Object());
+ timeSparseArray.put(91, new Object());
+ timeSparseArray.put(39, new Object());
+
+ assertThat(timeSparseArray.size()).isEqualTo(4);
+
+ // Testing any number arbitrarily smaller than 4.
+ assertThat(timeSparseArray.closestIndexOnOrBefore(-1478133)).isEqualTo(-1);
+ for (long time = -42; time < 4; time++) {
+ assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(-1);
+ }
+
+ for (long time = 4; time < 21; time++) {
+ assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(0);
+ }
+
+ for (long time = 21; time < 39; time++) {
+ assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(1);
+ }
+
+ for (long time = 39; time < 91; time++) {
+ assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(2);
+ }
+
+ for (long time = 91; time < 109; time++) {
+ assertThat(timeSparseArray.closestIndexOnOrBefore(time)).isEqualTo(3);
+ }
+ // Testing any number arbitrarily larger than 91.
+ assertThat(timeSparseArray.closestIndexOnOrBefore(1980732)).isEqualTo(3);
+ }
+}
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 7960dec..9ee52cc 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -100,6 +100,21 @@
out: ["wm_shell_protolog.json"],
}
+genrule {
+ name: "protolog.json.gz",
+ srcs: [":generate-wm_shell_protolog.json"],
+ out: ["wmshell.protolog.json.gz"],
+ cmd: "$(location minigzip) -c < $(in) > $(out)",
+ tools: ["minigzip"],
+}
+
+prebuilt_etc {
+ name: "wmshell.protolog.json.gz",
+ system_ext_specific: true,
+ src: ":protolog.json.gz",
+ filename_from_src: true,
+}
+
// End ProtoLog
java_library {
@@ -123,9 +138,6 @@
resource_dirs: [
"res",
],
- java_resources: [
- ":generate-wm_shell_protolog.json",
- ],
static_libs: [
"androidx.appcompat_appcompat",
"androidx.arch.core_core-runtime",
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 2214a98..7c3125b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -293,17 +293,17 @@
// Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
@BindsOptionalOf
@DynamicOverride
- abstract FullscreenTaskListener<?> optionalFullscreenTaskListener();
+ abstract FullscreenTaskListener optionalFullscreenTaskListener();
@WMSingleton
@Provides
- static FullscreenTaskListener<?> provideFullscreenTaskListener(
- @DynamicOverride Optional<FullscreenTaskListener<?>> fullscreenTaskListener,
+ static FullscreenTaskListener provideFullscreenTaskListener(
+ @DynamicOverride Optional<FullscreenTaskListener> fullscreenTaskListener,
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue,
Optional<RecentTasksController> recentTasksOptional,
- Optional<WindowDecorViewModel<?>> windowDecorViewModelOptional) {
+ Optional<WindowDecorViewModel> windowDecorViewModelOptional) {
if (fullscreenTaskListener.isPresent()) {
return fullscreenTaskListener.get();
} else {
@@ -317,7 +317,7 @@
//
@BindsOptionalOf
- abstract WindowDecorViewModel<?> optionalWindowDecorViewModel();
+ abstract WindowDecorViewModel optionalWindowDecorViewModel();
//
// Unfold transition
@@ -769,7 +769,7 @@
Optional<SplitScreenController> splitScreenOptional,
Optional<Pip> pipOptional,
Optional<PipTouchHandler> pipTouchHandlerOptional,
- FullscreenTaskListener<?> fullscreenTaskListener,
+ FullscreenTaskListener fullscreenTaskListener,
Optional<UnfoldAnimationController> unfoldAnimationController,
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
Optional<FreeformComponents> freeformComponents,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 47b6659..461d7dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -55,7 +55,6 @@
import com.android.wm.shell.freeform.FreeformTaskListener;
import com.android.wm.shell.freeform.FreeformTaskTransitionHandler;
import com.android.wm.shell.freeform.FreeformTaskTransitionObserver;
-import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.pip.Pip;
@@ -183,7 +182,7 @@
@WMSingleton
@Provides
- static WindowDecorViewModel<?> provideWindowDecorViewModel(
+ static WindowDecorViewModel provideWindowDecorViewModel(
Context context,
@ShellMainThread Handler mainHandler,
@ShellMainThread Choreographer mainChoreographer,
@@ -209,7 +208,7 @@
@Provides
@DynamicOverride
static FreeformComponents provideFreeformComponents(
- FreeformTaskListener<?> taskListener,
+ FreeformTaskListener taskListener,
FreeformTaskTransitionHandler transitionHandler,
FreeformTaskTransitionObserver transitionObserver) {
return new FreeformComponents(
@@ -218,18 +217,18 @@
@WMSingleton
@Provides
- static FreeformTaskListener<?> provideFreeformTaskListener(
+ static FreeformTaskListener provideFreeformTaskListener(
Context context,
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
- WindowDecorViewModel<?> windowDecorViewModel) {
+ WindowDecorViewModel windowDecorViewModel) {
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
// override for this controller from the base module
ShellInit init = FreeformComponents.isFreeformEnabled(context)
? shellInit
: null;
- return new FreeformTaskListener<>(init, shellTaskOrganizer, desktopModeTaskRepository,
+ return new FreeformTaskListener(init, shellTaskOrganizer, desktopModeTaskRepository,
windowDecorViewModel);
}
@@ -238,7 +237,7 @@
static FreeformTaskTransitionHandler provideFreeformTaskTransitionHandler(
ShellInit shellInit,
Transitions transitions,
- WindowDecorViewModel<?> windowDecorViewModel) {
+ WindowDecorViewModel windowDecorViewModel) {
return new FreeformTaskTransitionHandler(shellInit, transitions, windowDecorViewModel);
}
@@ -248,10 +247,9 @@
Context context,
ShellInit shellInit,
Transitions transitions,
- FullscreenTaskListener<?> fullscreenTaskListener,
- FreeformTaskListener<?> freeformTaskListener) {
+ WindowDecorViewModel windowDecorViewModel) {
return new FreeformTaskTransitionObserver(
- context, shellInit, transitions, fullscreenTaskListener, freeformTaskListener);
+ context, shellInit, transitions, windowDecorViewModel);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index e2d5a49..f82a346 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -19,12 +19,8 @@
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FREEFORM;
import android.app.ActivityManager.RunningTaskInfo;
-import android.util.Log;
import android.util.SparseArray;
import android.view.SurfaceControl;
-import android.window.TransitionInfo;
-
-import androidx.annotation.Nullable;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -41,31 +37,26 @@
/**
* {@link ShellTaskOrganizer.TaskListener} for {@link
* ShellTaskOrganizer#TASK_LISTENER_TYPE_FREEFORM}.
- *
- * @param <T> the type of window decoration instance
*/
-public class FreeformTaskListener<T extends AutoCloseable>
- implements ShellTaskOrganizer.TaskListener {
+public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
private static final String TAG = "FreeformTaskListener";
private final ShellTaskOrganizer mShellTaskOrganizer;
private final Optional<DesktopModeTaskRepository> mDesktopModeTaskRepository;
- private final WindowDecorViewModel<T> mWindowDecorationViewModel;
+ private final WindowDecorViewModel mWindowDecorationViewModel;
- private final SparseArray<State<T>> mTasks = new SparseArray<>();
- private final SparseArray<T> mWindowDecorOfVanishedTasks = new SparseArray<>();
+ private final SparseArray<State> mTasks = new SparseArray<>();
- private static class State<T extends AutoCloseable> {
+ private static class State {
RunningTaskInfo mTaskInfo;
SurfaceControl mLeash;
- T mWindowDecoration;
}
public FreeformTaskListener(
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
- WindowDecorViewModel<T> windowDecorationViewModel) {
+ WindowDecorViewModel windowDecorationViewModel) {
mShellTaskOrganizer = shellTaskOrganizer;
mWindowDecorationViewModel = windowDecorationViewModel;
mDesktopModeTaskRepository = desktopModeTaskRepository;
@@ -80,13 +71,18 @@
@Override
public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) {
+ if (mTasks.get(taskInfo.taskId) != null) {
+ throw new IllegalStateException("Task appeared more than once: #" + taskInfo.taskId);
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Appeared: #%d",
taskInfo.taskId);
- final State<T> state = createOrUpdateTaskState(taskInfo, leash);
+ final State state = new State();
+ state.mTaskInfo = taskInfo;
+ state.mLeash = leash;
+ mTasks.put(taskInfo.taskId, state);
if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- state.mWindowDecoration =
- mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash, t, t);
+ mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash, t, t);
t.apply();
}
@@ -97,28 +93,8 @@
}
}
- private State<T> createOrUpdateTaskState(RunningTaskInfo taskInfo, SurfaceControl leash) {
- State<T> state = mTasks.get(taskInfo.taskId);
- if (state != null) {
- updateTaskInfo(taskInfo);
- return state;
- }
-
- state = new State<>();
- state.mTaskInfo = taskInfo;
- state.mLeash = leash;
- mTasks.put(taskInfo.taskId, state);
-
- return state;
- }
-
@Override
public void onTaskVanished(RunningTaskInfo taskInfo) {
- final State<T> state = mTasks.get(taskInfo.taskId);
- if (state == null) {
- // This is possible if the transition happens before this method.
- return;
- }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d",
taskInfo.taskId);
mTasks.remove(taskInfo.taskId);
@@ -129,26 +105,18 @@
mDesktopModeTaskRepository.ifPresent(it -> it.removeActiveTask(taskInfo.taskId));
}
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- // Save window decorations of closing tasks so that we can hand them over to the
- // transition system if this method happens before the transition. In case where the
- // transition didn't happen, it'd be cleared when the next transition finished.
- if (state.mWindowDecoration != null) {
- mWindowDecorOfVanishedTasks.put(taskInfo.taskId, state.mWindowDecoration);
- }
- return;
+ if (!Transitions.ENABLE_SHELL_TRANSITIONS) {
+ mWindowDecorationViewModel.destroyWindowDecoration(taskInfo);
}
- releaseWindowDecor(state.mWindowDecoration);
}
@Override
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
- final State<T> state = updateTaskInfo(taskInfo);
+ final State state = mTasks.get(taskInfo.taskId);
+ state.mTaskInfo = taskInfo;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Info Changed: #%d",
taskInfo.taskId);
- if (state.mWindowDecoration != null) {
- mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo, state.mWindowDecoration);
- }
+ mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo);
if (DesktopModeStatus.IS_SUPPORTED) {
if (taskInfo.isVisible) {
@@ -159,15 +127,6 @@
}
}
- private State<T> updateTaskInfo(RunningTaskInfo taskInfo) {
- final State<T> state = mTasks.get(taskInfo.taskId);
- if (state == null) {
- throw new RuntimeException("Task info changed before appearing: #" + taskInfo.taskId);
- }
- state.mTaskInfo = taskInfo;
- return state;
- }
-
@Override
public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) {
b.setParent(findTaskSurface(taskId));
@@ -186,103 +145,6 @@
return mTasks.get(taskId).mLeash;
}
- /**
- * Creates a window decoration for a transition.
- *
- * @param change the change of this task transition that needs to have the task layer as the
- * leash
- * @return {@code true} if it creates the window decoration; {@code false} otherwise
- */
- boolean createWindowDecoration(
- TransitionInfo.Change change,
- SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT) {
- final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash());
- if (state.mWindowDecoration != null) {
- return false;
- }
- state.mWindowDecoration = mWindowDecorationViewModel.createWindowDecoration(
- state.mTaskInfo, state.mLeash, startT, finishT);
- return true;
- }
-
- /**
- * Gives out the ownership of the task's window decoration. The given task is leaving (of has
- * left) this task listener. This is the transition system asking for the ownership.
- *
- * @param taskInfo the maximizing task
- * @return the window decor of the maximizing task if any
- */
- T giveWindowDecoration(
- RunningTaskInfo taskInfo,
- SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT) {
- T windowDecor;
- final State<T> state = mTasks.get(taskInfo.taskId);
- if (state != null) {
- windowDecor = state.mWindowDecoration;
- state.mWindowDecoration = null;
- } else {
- windowDecor =
- mWindowDecorOfVanishedTasks.removeReturnOld(taskInfo.taskId);
- }
- if (windowDecor == null) {
- return null;
- }
- mWindowDecorationViewModel.setupWindowDecorationForTransition(
- taskInfo, startT, finishT, windowDecor);
- return windowDecor;
- }
-
- /**
- * Adopt the incoming window decoration and lets the window decoration prepare for a transition.
- *
- * @param change the change of this task transition that needs to have the task layer as the
- * leash
- * @param startT the start transaction of this transition
- * @param finishT the finish transaction of this transition
- * @param windowDecor the window decoration to adopt
- * @return {@code true} if it adopts the window decoration; {@code false} otherwise
- */
- boolean adoptWindowDecoration(
- TransitionInfo.Change change,
- SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT,
- @Nullable AutoCloseable windowDecor) {
- final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash());
- state.mWindowDecoration = mWindowDecorationViewModel.adoptWindowDecoration(windowDecor);
- if (state.mWindowDecoration != null) {
- mWindowDecorationViewModel.setupWindowDecorationForTransition(
- state.mTaskInfo, startT, finishT, state.mWindowDecoration);
- return true;
- } else {
- state.mWindowDecoration = mWindowDecorationViewModel.createWindowDecoration(
- state.mTaskInfo, state.mLeash, startT, finishT);
- return false;
- }
- }
-
- void onTaskTransitionFinished() {
- if (mWindowDecorOfVanishedTasks.size() == 0) {
- return;
- }
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
- "Clearing window decors of vanished tasks. There could be visual defects "
- + "if any of them is used later in transitions.");
- for (int i = 0; i < mWindowDecorOfVanishedTasks.size(); ++i) {
- releaseWindowDecor(mWindowDecorOfVanishedTasks.valueAt(i));
- }
- mWindowDecorOfVanishedTasks.clear();
- }
-
- private void releaseWindowDecor(T windowDecor) {
- try {
- windowDecor.close();
- } catch (Exception e) {
- Log.e(TAG, "Failed to release window decoration.", e);
- }
- }
-
@Override
public void dump(PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index fd4c85fa..04fc79a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -46,14 +46,14 @@
implements Transitions.TransitionHandler, FreeformTaskTransitionStarter {
private final Transitions mTransitions;
- private final WindowDecorViewModel<?> mWindowDecorViewModel;
+ private final WindowDecorViewModel mWindowDecorViewModel;
private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
public FreeformTaskTransitionHandler(
ShellInit shellInit,
Transitions transitions,
- WindowDecorViewModel<?> windowDecorViewModel) {
+ WindowDecorViewModel windowDecorViewModel) {
mTransitions = transitions;
mWindowDecorViewModel = windowDecorViewModel;
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index 17d6067..f4888fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -16,13 +16,9 @@
package com.android.wm.shell.freeform;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-
import android.app.ActivityManager;
import android.content.Context;
import android.os.IBinder;
-import android.util.Log;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
@@ -31,9 +27,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
-import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import java.util.ArrayList;
import java.util.Collections;
@@ -47,23 +43,19 @@
* be a part of transitions.
*/
public class FreeformTaskTransitionObserver implements Transitions.TransitionObserver {
- private static final String TAG = "FreeformTO";
-
private final Transitions mTransitions;
- private final FreeformTaskListener<?> mFreeformTaskListener;
- private final FullscreenTaskListener<?> mFullscreenTaskListener;
+ private final WindowDecorViewModel mWindowDecorViewModel;
- private final Map<IBinder, List<AutoCloseable>> mTransitionToWindowDecors = new HashMap<>();
+ private final Map<IBinder, List<ActivityManager.RunningTaskInfo>> mTransitionToTaskInfo =
+ new HashMap<>();
public FreeformTaskTransitionObserver(
Context context,
ShellInit shellInit,
Transitions transitions,
- FullscreenTaskListener<?> fullscreenTaskListener,
- FreeformTaskListener<?> freeformTaskListener) {
+ WindowDecorViewModel windowDecorViewModel) {
mTransitions = transitions;
- mFreeformTaskListener = freeformTaskListener;
- mFullscreenTaskListener = fullscreenTaskListener;
+ mWindowDecorViewModel = windowDecorViewModel;
if (Transitions.ENABLE_SHELL_TRANSITIONS && FreeformComponents.isFreeformEnabled(context)) {
shellInit.addInitCallback(this::onInit, this);
}
@@ -80,7 +72,7 @@
@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startT,
@NonNull SurfaceControl.Transaction finishT) {
- final ArrayList<AutoCloseable> windowDecors = new ArrayList<>();
+ final ArrayList<ActivityManager.RunningTaskInfo> taskInfoList = new ArrayList<>();
final ArrayList<WindowContainerToken> taskParents = new ArrayList<>();
for (TransitionInfo.Change change : info.getChanges()) {
if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) {
@@ -110,92 +102,40 @@
onOpenTransitionReady(change, startT, finishT);
break;
case WindowManager.TRANSIT_CLOSE: {
- onCloseTransitionReady(change, windowDecors, startT, finishT);
+ taskInfoList.add(change.getTaskInfo());
+ onCloseTransitionReady(change, startT, finishT);
break;
}
case WindowManager.TRANSIT_CHANGE:
- onChangeTransitionReady(info.getType(), change, startT, finishT);
+ onChangeTransitionReady(change, startT, finishT);
break;
}
}
- if (!windowDecors.isEmpty()) {
- mTransitionToWindowDecors.put(transition, windowDecors);
- }
+ mTransitionToTaskInfo.put(transition, taskInfoList);
}
private void onOpenTransitionReady(
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- switch (change.getTaskInfo().getWindowingMode()){
- case WINDOWING_MODE_FREEFORM:
- mFreeformTaskListener.createWindowDecoration(change, startT, finishT);
- break;
- case WINDOWING_MODE_FULLSCREEN:
- mFullscreenTaskListener.createWindowDecoration(change, startT, finishT);
- break;
- }
+ mWindowDecorViewModel.createWindowDecoration(
+ change.getTaskInfo(), change.getLeash(), startT, finishT);
}
private void onCloseTransitionReady(
TransitionInfo.Change change,
- ArrayList<AutoCloseable> windowDecors,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- final AutoCloseable windowDecor;
- switch (change.getTaskInfo().getWindowingMode()) {
- case WINDOWING_MODE_FREEFORM:
- windowDecor = mFreeformTaskListener.giveWindowDecoration(change.getTaskInfo(),
- startT, finishT);
- break;
- case WINDOWING_MODE_FULLSCREEN:
- windowDecor = mFullscreenTaskListener.giveWindowDecoration(change.getTaskInfo(),
- startT, finishT);
- break;
- default:
- windowDecor = null;
- }
- if (windowDecor != null) {
- windowDecors.add(windowDecor);
- }
+ mWindowDecorViewModel.setupWindowDecorationForTransition(
+ change.getTaskInfo(), startT, finishT);
}
private void onChangeTransitionReady(
- int type,
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- AutoCloseable windowDecor = null;
-
- boolean adopted = false;
- final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
- windowDecor = mFreeformTaskListener.giveWindowDecoration(
- change.getTaskInfo(), startT, finishT);
- if (windowDecor != null) {
- adopted = mFullscreenTaskListener.adoptWindowDecoration(
- change, startT, finishT, windowDecor);
- } else {
- // will return false if it already has the window decor.
- adopted = mFullscreenTaskListener.createWindowDecoration(change, startT, finishT);
- }
- }
-
- if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
- windowDecor = mFullscreenTaskListener.giveWindowDecoration(
- change.getTaskInfo(), startT, finishT);
- if (windowDecor != null) {
- adopted = mFreeformTaskListener.adoptWindowDecoration(
- change, startT, finishT, windowDecor);
- } else {
- // will return false if it already has the window decor.
- adopted = mFreeformTaskListener.createWindowDecoration(change, startT, finishT);
- }
- }
-
- if (!adopted) {
- releaseWindowDecor(windowDecor);
- }
+ mWindowDecorViewModel.setupWindowDecorationForTransition(
+ change.getTaskInfo(), startT, finishT);
}
@Override
@@ -203,43 +143,32 @@
@Override
public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) {
- final List<AutoCloseable> windowDecorsOfMerged = mTransitionToWindowDecors.get(merged);
- if (windowDecorsOfMerged == null) {
+ final List<ActivityManager.RunningTaskInfo> infoOfMerged =
+ mTransitionToTaskInfo.get(merged);
+ if (infoOfMerged == null) {
// We are adding window decorations of the merged transition to them of the playing
// transition so if there is none of them there is nothing to do.
return;
}
- mTransitionToWindowDecors.remove(merged);
+ mTransitionToTaskInfo.remove(merged);
- final List<AutoCloseable> windowDecorsOfPlaying = mTransitionToWindowDecors.get(playing);
- if (windowDecorsOfPlaying != null) {
- windowDecorsOfPlaying.addAll(windowDecorsOfMerged);
+ final List<ActivityManager.RunningTaskInfo> infoOfPlaying =
+ mTransitionToTaskInfo.get(playing);
+ if (infoOfPlaying != null) {
+ infoOfPlaying.addAll(infoOfMerged);
} else {
- mTransitionToWindowDecors.put(playing, windowDecorsOfMerged);
+ mTransitionToTaskInfo.put(playing, infoOfMerged);
}
}
@Override
public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) {
- final List<AutoCloseable> windowDecors = mTransitionToWindowDecors.getOrDefault(
- transition, Collections.emptyList());
- mTransitionToWindowDecors.remove(transition);
+ final List<ActivityManager.RunningTaskInfo> taskInfo =
+ mTransitionToTaskInfo.getOrDefault(transition, Collections.emptyList());
+ mTransitionToTaskInfo.remove(transition);
- for (AutoCloseable windowDecor : windowDecors) {
- releaseWindowDecor(windowDecor);
- }
- mFullscreenTaskListener.onTaskTransitionFinished();
- mFreeformTaskListener.onTaskTransitionFinished();
- }
-
- private static void releaseWindowDecor(AutoCloseable windowDecor) {
- if (windowDecor == null) {
- return;
- }
- try {
- windowDecor.close();
- } catch (Exception e) {
- Log.e(TAG, "Failed to release window decoration.", e);
+ for (int i = 0; i < taskInfo.size(); ++i) {
+ mWindowDecorViewModel.destroyWindowDecoration(taskInfo.get(i));
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
index 76e296b..75a4091 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -22,13 +22,10 @@
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.graphics.Point;
-import android.util.Log;
import android.util.SparseArray;
import android.view.SurfaceControl;
-import android.window.TransitionInfo;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
@@ -46,23 +43,20 @@
* Organizes tasks presented in {@link android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN}.
* @param <T> the type of window decoration instance
*/
-public class FullscreenTaskListener<T extends AutoCloseable>
- implements ShellTaskOrganizer.TaskListener {
+public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener {
private static final String TAG = "FullscreenTaskListener";
private final ShellTaskOrganizer mShellTaskOrganizer;
- private final SparseArray<State<T>> mTasks = new SparseArray<>();
- private final SparseArray<T> mWindowDecorOfVanishedTasks = new SparseArray<>();
+ private final SparseArray<State> mTasks = new SparseArray<>();
- private static class State<T extends AutoCloseable> {
+ private static class State {
RunningTaskInfo mTaskInfo;
SurfaceControl mLeash;
- T mWindowDecoration;
}
private final SyncTransactionQueue mSyncQueue;
private final Optional<RecentTasksController> mRecentTasksOptional;
- private final Optional<WindowDecorViewModel<T>> mWindowDecorViewModelOptional;
+ private final Optional<WindowDecorViewModel> mWindowDecorViewModelOptional;
/**
* This constructor is used by downstream products.
*/
@@ -75,7 +69,7 @@
ShellTaskOrganizer shellTaskOrganizer,
SyncTransactionQueue syncQueue,
Optional<RecentTasksController> recentTasksOptional,
- Optional<WindowDecorViewModel<T>> windowDecorViewModelOptional) {
+ Optional<WindowDecorViewModel> windowDecorViewModelOptional) {
mShellTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
mRecentTasksOptional = recentTasksOptional;
@@ -98,21 +92,21 @@
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Appeared: #%d",
taskInfo.taskId);
final Point positionInParent = taskInfo.positionInParent;
- final State<T> state = new State();
+ final State state = new State();
state.mLeash = leash;
state.mTaskInfo = taskInfo;
mTasks.put(taskInfo.taskId, state);
if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
updateRecentsForVisibleFullscreenTask(taskInfo);
+ boolean createdWindowDecor = false;
if (mWindowDecorViewModelOptional.isPresent()) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- state.mWindowDecoration =
- mWindowDecorViewModelOptional.get().createWindowDecoration(taskInfo,
- leash, t, t);
+ createdWindowDecor = mWindowDecorViewModelOptional.get()
+ .createWindowDecoration(taskInfo, leash, t, t);
t.apply();
}
- if (state.mWindowDecoration == null) {
+ if (!createdWindowDecor) {
mSyncQueue.runInSync(t -> {
// Reset several properties back to fullscreen (PiP, for example, leaves all these
// properties in a bad state).
@@ -127,12 +121,11 @@
@Override
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
- final State<T> state = mTasks.get(taskInfo.taskId);
+ final State state = mTasks.get(taskInfo.taskId);
final Point oldPositionInParent = state.mTaskInfo.positionInParent;
state.mTaskInfo = taskInfo;
- if (state.mWindowDecoration != null) {
- mWindowDecorViewModelOptional.get().onTaskInfoChanged(
- state.mTaskInfo, state.mWindowDecoration);
+ if (mWindowDecorViewModelOptional.isPresent()) {
+ mWindowDecorViewModelOptional.get().onTaskInfoChanged(state.mTaskInfo);
}
if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
updateRecentsForVisibleFullscreenTask(taskInfo);
@@ -147,160 +140,13 @@
@Override
public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
- final State<T> state = mTasks.get(taskInfo.taskId);
- if (state == null) {
- // This is possible if the transition happens before this method.
- return;
- }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Fullscreen Task Vanished: #%d",
taskInfo.taskId);
mTasks.remove(taskInfo.taskId);
- if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- // Save window decorations of closing tasks so that we can hand them over to the
- // transition system if this method happens before the transition. In case where the
- // transition didn't happen, it'd be cleared when the next transition finished.
- if (state.mWindowDecoration != null) {
- mWindowDecorOfVanishedTasks.put(taskInfo.taskId, state.mWindowDecoration);
- }
- return;
- }
- releaseWindowDecor(state.mWindowDecoration);
- }
-
- /**
- * Creates a window decoration for a transition.
- *
- * @param change the change of this task transition that needs to have the task layer as the
- * leash
- * @return {@code true} if a decoration was actually created.
- */
- public boolean createWindowDecoration(TransitionInfo.Change change,
- SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
- final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash());
- if (!mWindowDecorViewModelOptional.isPresent()) return false;
- if (state.mWindowDecoration != null) {
- // Already has a decoration.
- return false;
- }
- T newWindowDecor = mWindowDecorViewModelOptional.get().createWindowDecoration(
- state.mTaskInfo, state.mLeash, startT, finishT);
- if (newWindowDecor != null) {
- state.mWindowDecoration = newWindowDecor;
- return true;
- }
- return false;
- }
-
- /**
- * Adopt the incoming window decoration and lets the window decoration prepare for a transition.
- *
- * @param change the change of this task transition that needs to have the task layer as the
- * leash
- * @param startT the start transaction of this transition
- * @param finishT the finish transaction of this transition
- * @param windowDecor the window decoration to adopt
- * @return {@code true} if it adopts the window decoration; {@code false} otherwise
- */
- public boolean adoptWindowDecoration(
- TransitionInfo.Change change,
- SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT,
- @Nullable AutoCloseable windowDecor) {
- if (!mWindowDecorViewModelOptional.isPresent()) {
- return false;
- }
- final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash());
- state.mWindowDecoration = mWindowDecorViewModelOptional.get().adoptWindowDecoration(
- windowDecor);
- if (state.mWindowDecoration != null) {
- mWindowDecorViewModelOptional.get().setupWindowDecorationForTransition(
- state.mTaskInfo, startT, finishT, state.mWindowDecoration);
- return true;
- } else {
- T newWindowDecor = mWindowDecorViewModelOptional.get().createWindowDecoration(
- state.mTaskInfo, state.mLeash, startT, finishT);
- if (newWindowDecor != null) {
- state.mWindowDecoration = newWindowDecor;
- }
- return false;
- }
- }
-
- /**
- * Clear window decors of vanished tasks.
- */
- public void onTaskTransitionFinished() {
- if (mWindowDecorOfVanishedTasks.size() == 0) {
- return;
- }
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
- "Clearing window decors of vanished tasks. There could be visual defects "
- + "if any of them is used later in transitions.");
- for (int i = 0; i < mWindowDecorOfVanishedTasks.size(); ++i) {
- releaseWindowDecor(mWindowDecorOfVanishedTasks.valueAt(i));
- }
- mWindowDecorOfVanishedTasks.clear();
- }
-
- /**
- * Gives out the ownership of the task's window decoration. The given task is leaving (of has
- * left) this task listener. This is the transition system asking for the ownership.
- *
- * @param taskInfo the maximizing task
- * @return the window decor of the maximizing task if any
- */
- public T giveWindowDecoration(
- ActivityManager.RunningTaskInfo taskInfo,
- SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT) {
- T windowDecor;
- final State<T> state = mTasks.get(taskInfo.taskId);
- if (state != null) {
- windowDecor = state.mWindowDecoration;
- state.mWindowDecoration = null;
- } else {
- windowDecor =
- mWindowDecorOfVanishedTasks.removeReturnOld(taskInfo.taskId);
- }
- if (mWindowDecorViewModelOptional.isPresent() && windowDecor != null) {
- mWindowDecorViewModelOptional.get().setupWindowDecorationForTransition(
- taskInfo, startT, finishT, windowDecor);
- }
-
- return windowDecor;
- }
-
- private State<T> createOrUpdateTaskState(ActivityManager.RunningTaskInfo taskInfo,
- SurfaceControl leash) {
- State<T> state = mTasks.get(taskInfo.taskId);
- if (state != null) {
- updateTaskInfo(taskInfo);
- return state;
- }
-
- state = new State<T>();
- state.mTaskInfo = taskInfo;
- state.mLeash = leash;
- mTasks.put(taskInfo.taskId, state);
-
- return state;
- }
-
- private State<T> updateTaskInfo(ActivityManager.RunningTaskInfo taskInfo) {
- final State<T> state = mTasks.get(taskInfo.taskId);
- state.mTaskInfo = taskInfo;
- return state;
- }
-
- private void releaseWindowDecor(T windowDecor) {
- if (windowDecor == null) {
- return;
- }
- try {
- windowDecor.close();
- } catch (Exception e) {
- Log.e(TAG, "Failed to release window decoration.", e);
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
+ if (mWindowDecorViewModelOptional.isPresent()) {
+ mWindowDecorViewModelOptional.get().destroyWindowDecoration(taskInfo);
}
}
@@ -342,6 +188,4 @@
public String toString() {
return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN);
}
-
-
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
index 552ebde..93ffb3d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogImpl.java
@@ -17,22 +17,14 @@
package com.android.wm.shell.protolog;
import android.annotation.Nullable;
-import android.content.Context;
-import android.util.Log;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.BaseProtoLogImpl;
import com.android.internal.protolog.ProtoLogViewerConfigReader;
import com.android.internal.protolog.common.IProtoLogGroup;
-import com.android.wm.shell.R;
import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
import java.io.PrintWriter;
-import org.json.JSONException;
-
/**
* A service for the ProtoLog logging system.
@@ -40,8 +32,9 @@
public class ShellProtoLogImpl extends BaseProtoLogImpl {
private static final String TAG = "ProtoLogImpl";
private static final int BUFFER_CAPACITY = 1024 * 1024;
- // TODO: Get the right path for the proto log file when we initialize the shell components
- private static final String LOG_FILENAME = new File("wm_shell_log.pb").getAbsolutePath();
+ // TODO: find a proper location to save the protolog message file
+ private static final String LOG_FILENAME = "/data/misc/wmtrace/shell_log.winscope";
+ private static final String VIEWER_CONFIG_FILENAME = "/system_ext/etc/wmshell.protolog.json.gz";
private static ShellProtoLogImpl sServiceInstance = null;
@@ -111,18 +104,8 @@
}
public int startTextLogging(String[] groups, PrintWriter pw) {
- try (InputStream is =
- getClass().getClassLoader().getResourceAsStream("wm_shell_protolog.json")){
- mViewerConfig.loadViewerConfig(is);
- return setLogging(true /* setTextLogging */, true, pw, groups);
- } catch (IOException e) {
- Log.i(TAG, "Unable to load log definitions: IOException while reading "
- + "wm_shell_protolog. " + e);
- } catch (JSONException e) {
- Log.i(TAG, "Unable to load log definitions: JSON parsing exception while reading "
- + "wm_shell_protolog. " + e);
- }
- return -1;
+ mViewerConfig.loadViewerConfig(pw, VIEWER_CONFIG_FILENAME);
+ return setLogging(true /* setTextLogging */, true, pw, groups);
}
public int stopTextLogging(String[] groups, PrintWriter pw) {
@@ -130,7 +113,8 @@
}
private ShellProtoLogImpl() {
- super(new File(LOG_FILENAME), null, BUFFER_CAPACITY, new ProtoLogViewerConfigReader());
+ super(new File(LOG_FILENAME), VIEWER_CONFIG_FILENAME, BUFFER_CAPACITY,
+ new ProtoLogViewerConfigReader());
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index fb5a504..63e01a7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -25,6 +25,7 @@
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.fixScale;
import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD;
+import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
@@ -441,31 +442,34 @@
return;
}
- // apply transfer starting window directly if there is no other task change. Since this
- // is an activity->activity situation, we can detect it by selecting transitions with only
- // 2 changes where neither are tasks and one is a starting-window recipient.
final int changeSize = info.getChanges().size();
- if (changeSize == 2) {
- boolean nonTaskChange = true;
- boolean transferStartingWindow = false;
- for (int i = changeSize - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- if (change.getTaskInfo() != null) {
- nonTaskChange = false;
- break;
- }
- if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
- transferStartingWindow = true;
- }
+ boolean taskChange = false;
+ boolean transferStartingWindow = false;
+ boolean allOccluded = changeSize > 0;
+ for (int i = changeSize - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ taskChange |= change.getTaskInfo() != null;
+ transferStartingWindow |= change.hasFlags(FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT);
+ if (!change.hasFlags(FLAG_IS_OCCLUDED)) {
+ allOccluded = false;
}
- if (nonTaskChange && transferStartingWindow) {
- t.apply();
- finishT.apply();
- // Treat this as an abort since we are bypassing any merge logic and effectively
- // finishing immediately.
- onAbort(transitionToken);
- return;
- }
+ }
+ // There does not need animation when:
+ // A. Transfer starting window. Apply transfer starting window directly if there is no other
+ // task change. Since this is an activity->activity situation, we can detect it by selecting
+ // transitions with only 2 changes where neither are tasks and one is a starting-window
+ // recipient.
+ if (!taskChange && transferStartingWindow && changeSize == 2
+ // B. It's visibility change if the TRANSIT_TO_BACK/TO_FRONT happened when all
+ // changes are underneath another change.
+ || ((info.getType() == TRANSIT_TO_BACK || info.getType() == TRANSIT_TO_FRONT)
+ && allOccluded)) {
+ t.apply();
+ finishT.apply();
+ // Treat this as an abort since we are bypassing any merge logic and effectively
+ // finishing immediately.
+ onAbort(transitionToken);
+ return;
}
final ActiveTransition active = mActiveTransitions.get(activeIdx);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 9e49b51..3df33f3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -26,6 +26,7 @@
import android.app.ActivityTaskManager;
import android.content.Context;
import android.os.Handler;
+import android.util.SparseArray;
import android.view.Choreographer;
import android.view.MotionEvent;
import android.view.SurfaceControl;
@@ -46,7 +47,7 @@
* View model for the window decoration with a caption and shadows. Works with
* {@link CaptionWindowDecoration}.
*/
-public class CaptionWindowDecorViewModel implements WindowDecorViewModel<CaptionWindowDecoration> {
+public class CaptionWindowDecorViewModel implements WindowDecorViewModel {
private final ActivityTaskManager mActivityTaskManager;
private final ShellTaskOrganizer mTaskOrganizer;
private final Context mContext;
@@ -57,6 +58,8 @@
private FreeformTaskTransitionStarter mTransitionStarter;
private DesktopModeController mDesktopModeController;
+ private final SparseArray<CaptionWindowDecoration> mWindowDecorByTaskId = new SparseArray<>();
+
public CaptionWindowDecorViewModel(
Context context,
Handler mainHandler,
@@ -81,12 +84,12 @@
}
@Override
- public CaptionWindowDecoration createWindowDecoration(
+ public boolean createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
- if (!shouldShowWindowDecor(taskInfo)) return null;
+ if (!shouldShowWindowDecor(taskInfo)) return false;
final CaptionWindowDecoration windowDecoration = new CaptionWindowDecoration(
mContext,
mDisplayController,
@@ -96,30 +99,24 @@
mMainHandler,
mMainChoreographer,
mSyncQueue);
+ mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
+
TaskPositioner taskPositioner = new TaskPositioner(mTaskOrganizer, windowDecoration);
CaptionTouchEventListener touchEventListener =
new CaptionTouchEventListener(taskInfo, taskPositioner);
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
windowDecoration.setDragResizeCallback(taskPositioner);
- setupWindowDecorationForTransition(taskInfo, startT, finishT, windowDecoration);
+ setupWindowDecorationForTransition(taskInfo, startT, finishT);
setupCaptionColor(taskInfo, windowDecoration);
- return windowDecoration;
+ return true;
}
@Override
- public CaptionWindowDecoration adoptWindowDecoration(AutoCloseable windowDecor) {
- if (!(windowDecor instanceof CaptionWindowDecoration)) return null;
- final CaptionWindowDecoration captionWindowDecor = (CaptionWindowDecoration) windowDecor;
- if (!shouldShowWindowDecor(captionWindowDecor.mTaskInfo)) {
- return null;
- }
- return captionWindowDecor;
- }
+ public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+ final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+ if (decoration == null) return;
- @Override
- public void onTaskInfoChanged(RunningTaskInfo taskInfo, CaptionWindowDecoration decoration) {
decoration.relayout(taskInfo);
-
setupCaptionColor(taskInfo, decoration);
}
@@ -132,11 +129,22 @@
public void setupWindowDecorationForTransition(
RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT,
- CaptionWindowDecoration decoration) {
+ SurfaceControl.Transaction finishT) {
+ final CaptionWindowDecoration decoration = mWindowDecorByTaskId.get(taskInfo.taskId);
+ if (decoration == null) return;
+
decoration.relayout(taskInfo, startT, finishT);
}
+ @Override
+ public void destroyWindowDecoration(RunningTaskInfo taskInfo) {
+ final CaptionWindowDecoration decoration =
+ mWindowDecorByTaskId.removeReturnOld(taskInfo.taskId);
+ if (decoration == null) return;
+
+ decoration.close();
+ }
+
private class CaptionTouchEventListener implements
View.OnClickListener, View.OnTouchListener {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
index d9697d2..d7f71c8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -19,8 +19,6 @@
import android.app.ActivityManager;
import android.view.SurfaceControl;
-import androidx.annotation.Nullable;
-
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
/**
@@ -28,10 +26,8 @@
* customize {@link WindowDecoration}. Its implementations are responsible to interpret user's
* interactions with UI widgets in window decorations and send corresponding requests to system
* servers.
- *
- * @param <T> The actual decoration type
*/
-public interface WindowDecorViewModel<T extends AutoCloseable> {
+public interface WindowDecorViewModel {
/**
* Sets the transition starter that starts freeform task transitions.
@@ -50,29 +46,19 @@
* @param finishT the finish transaction to restore states after the transition
* @return the window decoration object
*/
- @Nullable T createWindowDecoration(
+ boolean createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT);
/**
- * Adopts the window decoration if possible.
- * May be {@code null} if a window decor is not needed or the given one is incompatible.
- *
- * @param windowDecor the potential window decoration to adopt
- * @return the window decoration if it can be adopted, or {@code null} otherwise.
- */
- @Nullable T adoptWindowDecoration(@Nullable AutoCloseable windowDecor);
-
- /**
* Notifies a task info update on the given task, with the window decoration created previously
* for this task by {@link #createWindowDecoration}.
*
* @param taskInfo the new task info of the task
- * @param windowDecoration the window decoration created for the task
*/
- void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo, T windowDecoration);
+ void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo);
/**
* Notifies a transition is about to start about the given task to give the window decoration a
@@ -80,11 +66,16 @@
*
* @param startT the start transaction to be applied before the transition
* @param finishT the finish transaction to restore states after the transition
- * @param windowDecoration the window decoration created for the task
*/
void setupWindowDecorationForTransition(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl.Transaction startT,
- SurfaceControl.Transaction finishT,
- T windowDecoration);
+ SurfaceControl.Transaction finishT);
+
+ /**
+ * Destroys the window decoration of the give task.
+ *
+ * @param taskInfo the info of the task
+ */
+ void destroyWindowDecoration(ActivityManager.RunningTaskInfo taskInfo);
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
index 275f6c8..47167b8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleFromLockScreen.kt
@@ -143,4 +143,10 @@
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+
+ @FlakyTest(bugId = 251217773)
+ @Test
+ override fun entireScreenCovered() {
+ super.entireScreenCovered()
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index 1fc0375..7d498dc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -92,7 +92,7 @@
}
/** Checks that [pipApp] window is animated towards default position in right bottom corner */
- @Presubmit
+ @FlakyTest(bugId = 251135384)
@Test
fun pipLayerMovesTowardsRightBottomCorner() {
// in gestural nav the swipe makes PiP first go upwards
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
index 104b409..b4594de 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt
@@ -141,6 +141,12 @@
@Test
fun entireScreenCoveredAtStartAndEnd() = testSpec.entireScreenCovered(allStates = false)
+ @FlakyTest(bugId = 251219769)
+ @Test
+ override fun entireScreenCovered() {
+ super.entireScreenCovered()
+ }
+
/**
* Checks [pipApp] window remains visible and on top throughout the transition
*/
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
index 5e3194c..6bf7e8c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.view.Surface
import androidx.test.filters.RequiresDevice
@@ -81,7 +82,7 @@
override fun entireScreenCovered() = super.entireScreenCovered()
/** {@inheritDoc} */
- @Presubmit
+ @FlakyTest(bugId = 197726610)
@Test
override fun pipLayerExpands() = super.pipLayerExpands()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index cb49e18..a2eefec 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -106,7 +106,7 @@
fun secondaryAppWindowBecomesInvisible() = testSpec.appWindowBecomesInvisible(secondaryApp)
/** {@inheritDoc} */
- @Presubmit
+ @FlakyTest(bugId = 251268711)
@Test
override fun entireScreenCovered() =
super.entireScreenCovered()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
index 504238f..201594b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2022 The Android Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.Assume
+import org.junit.Before
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -50,7 +51,6 @@
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
class EnterSplitScreenFromOverview(testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) {
-
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
@@ -117,7 +117,7 @@
fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
/** {@inheritDoc} */
- @Presubmit
+ @FlakyTest(bugId = 251269324)
@Test
override fun entireScreenCovered() =
super.entireScreenCovered()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
index 2ecf819..553840c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -104,7 +105,7 @@
fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
/** {@inheritDoc} */
- @Presubmit
+ @FlakyTest
@Test
override fun entireScreenCovered() =
super.entireScreenCovered()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index 384489d..e2f7f7e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -103,7 +104,7 @@
fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
/** {@inheritDoc} */
- @Presubmit
+ @FlakyTest
@Test
override fun entireScreenCovered() =
super.entireScreenCovered()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
index 04ebbf5..d7b3ec2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
@@ -150,7 +151,7 @@
override fun taskBarWindowIsAlwaysVisible() = super.taskBarWindowIsAlwaysVisible()
/** {@inheritDoc} */
- @Presubmit
+ @FlakyTest
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
super.visibleLayersShownMoreThanOneConsecutiveEntry()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index 0fd5cb0..7068a84 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -17,17 +17,10 @@
package com.android.wm.shell.freeform;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
-import static com.android.wm.shell.transition.Transitions.TRANSIT_MAXIMIZE;
-import static com.android.wm.shell.transition.Transitions.TRANSIT_RESTORE_FROM_MAXIMIZE;
-
-import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.same;
@@ -44,9 +37,9 @@
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import org.junit.Before;
import org.junit.Test;
@@ -65,9 +58,7 @@
@Mock
private Transitions mTransitions;
@Mock
- private FullscreenTaskListener<?> mFullscreenTaskListener;
- @Mock
- private FreeformTaskListener<?> mFreeformTaskListener;
+ private WindowDecorViewModel mWindowDecorViewModel;
private FreeformTaskTransitionObserver mTransitionObserver;
@@ -82,7 +73,7 @@
doReturn(pm).when(context).getPackageManager();
mTransitionObserver = new FreeformTaskTransitionObserver(
- context, mShellInit, mTransitions, mFullscreenTaskListener, mFreeformTaskListener);
+ context, mShellInit, mTransitions, mWindowDecorViewModel);
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
final ArgumentCaptor<Runnable> initRunnableCaptor = ArgumentCaptor.forClass(
Runnable.class);
@@ -112,11 +103,12 @@
mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition);
- verify(mFreeformTaskListener).createWindowDecoration(change, startT, finishT);
+ verify(mWindowDecorViewModel).createWindowDecoration(
+ change.getTaskInfo(), change.getLeash(), startT, finishT);
}
@Test
- public void testObtainsWindowDecorOnCloseTransition_freeform() {
+ public void testPreparesWindowDecorOnCloseTransition_freeform() {
final TransitionInfo.Change change =
createChange(TRANSIT_CLOSE, 1, WINDOWING_MODE_FREEFORM);
final TransitionInfo info = new TransitionInfo(TRANSIT_CLOSE, 0);
@@ -128,7 +120,8 @@
mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition);
- verify(mFreeformTaskListener).giveWindowDecoration(change.getTaskInfo(), startT, finishT);
+ verify(mWindowDecorViewModel).setupWindowDecorationForTransition(
+ change.getTaskInfo(), startT, finishT);
}
@Test
@@ -138,17 +131,13 @@
final TransitionInfo info = new TransitionInfo(TRANSIT_CLOSE, 0);
info.addChange(change);
- final AutoCloseable windowDecor = mock(AutoCloseable.class);
- doReturn(windowDecor).when(mFreeformTaskListener).giveWindowDecoration(
- eq(change.getTaskInfo()), any(), any());
-
final IBinder transition = mock(IBinder.class);
final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
mTransitionObserver.onTransitionStarting(transition);
- verify(windowDecor, never()).close();
+ verify(mWindowDecorViewModel, never()).destroyWindowDecoration(change.getTaskInfo());
}
@Test
@@ -159,8 +148,6 @@
info.addChange(change);
final AutoCloseable windowDecor = mock(AutoCloseable.class);
- doReturn(windowDecor).when(mFreeformTaskListener).giveWindowDecoration(
- eq(change.getTaskInfo()), any(), any());
final IBinder transition = mock(IBinder.class);
final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
@@ -169,7 +156,7 @@
mTransitionObserver.onTransitionStarting(transition);
mTransitionObserver.onTransitionFinished(transition, false);
- verify(windowDecor).close();
+ verify(mWindowDecorViewModel).destroyWindowDecoration(change.getTaskInfo());
}
@Test
@@ -192,10 +179,6 @@
final TransitionInfo info2 = new TransitionInfo(TRANSIT_CLOSE, 0);
info2.addChange(change2);
- final AutoCloseable windowDecor2 = mock(AutoCloseable.class);
- doReturn(windowDecor2).when(mFreeformTaskListener).giveWindowDecoration(
- eq(change2.getTaskInfo()), any(), any());
-
final IBinder transition2 = mock(IBinder.class);
final SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class);
@@ -204,7 +187,7 @@
mTransitionObserver.onTransitionFinished(transition1, false);
- verify(windowDecor2).close();
+ verify(mWindowDecorViewModel).destroyWindowDecoration(change2.getTaskInfo());
}
@Test
@@ -215,10 +198,6 @@
final TransitionInfo info1 = new TransitionInfo(TRANSIT_CLOSE, 0);
info1.addChange(change1);
- final AutoCloseable windowDecor1 = mock(AutoCloseable.class);
- doReturn(windowDecor1).when(mFreeformTaskListener).giveWindowDecoration(
- eq(change1.getTaskInfo()), any(), any());
-
final IBinder transition1 = mock(IBinder.class);
final SurfaceControl.Transaction startT1 = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT1 = mock(SurfaceControl.Transaction.class);
@@ -231,10 +210,6 @@
final TransitionInfo info2 = new TransitionInfo(TRANSIT_CLOSE, 0);
info2.addChange(change2);
- final AutoCloseable windowDecor2 = mock(AutoCloseable.class);
- doReturn(windowDecor2).when(mFreeformTaskListener).giveWindowDecoration(
- eq(change2.getTaskInfo()), any(), any());
-
final IBinder transition2 = mock(IBinder.class);
final SurfaceControl.Transaction startT2 = mock(SurfaceControl.Transaction.class);
final SurfaceControl.Transaction finishT2 = mock(SurfaceControl.Transaction.class);
@@ -243,48 +218,8 @@
mTransitionObserver.onTransitionFinished(transition1, false);
- verify(windowDecor1).close();
- verify(windowDecor2).close();
- }
-
- @Test
- public void testTransfersWindowDecorOnMaximize() {
- final TransitionInfo.Change change =
- createChange(TRANSIT_CHANGE, 1, WINDOWING_MODE_FULLSCREEN);
- final TransitionInfo info = new TransitionInfo(TRANSIT_MAXIMIZE, 0);
- info.addChange(change);
-
- final AutoCloseable windowDecor = mock(AutoCloseable.class);
- doReturn(windowDecor).when(mFreeformTaskListener).giveWindowDecoration(
- eq(change.getTaskInfo()), any(), any());
-
- final IBinder transition = mock(IBinder.class);
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
- mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
- mTransitionObserver.onTransitionStarting(transition);
-
- verify(mFreeformTaskListener).giveWindowDecoration(change.getTaskInfo(), startT, finishT);
- verify(mFullscreenTaskListener).adoptWindowDecoration(
- eq(change), same(startT), same(finishT), any());
- }
-
- @Test
- public void testTransfersWindowDecorOnRestoreFromMaximize() {
- final TransitionInfo.Change change =
- createChange(TRANSIT_CHANGE, 1, WINDOWING_MODE_FREEFORM);
- final TransitionInfo info = new TransitionInfo(TRANSIT_RESTORE_FROM_MAXIMIZE, 0);
- info.addChange(change);
-
- final IBinder transition = mock(IBinder.class);
- final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
- final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
- mTransitionObserver.onTransitionReady(transition, info, startT, finishT);
- mTransitionObserver.onTransitionStarting(transition);
-
- verify(mFullscreenTaskListener).giveWindowDecoration(change.getTaskInfo(), startT, finishT);
- verify(mFreeformTaskListener).adoptWindowDecoration(
- eq(change), same(startT), same(finishT), any());
+ verify(mWindowDecorViewModel).destroyWindowDecoration(change1.getTaskInfo());
+ verify(mWindowDecorViewModel).destroyWindowDecoration(change2.getTaskInfo());
}
private static TransitionInfo.Change createChange(int mode, int taskId, int windowingMode) {
diff --git a/packages/SettingsLib/res/values-w320dp-port/dimens.xml b/packages/SettingsLib/res/values-w320dp-port/dimens.xml
new file mode 100644
index 0000000..bddf391
--- /dev/null
+++ b/packages/SettingsLib/res/values-w320dp-port/dimens.xml
@@ -0,0 +1,21 @@
+<!--
+ 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.
+ -->
+<resources>
+ <integer name="avatar_picker_columns">2</integer>
+ <dimen name="avatar_size_in_picker">96dp</dimen>
+ <dimen name="avatar_picker_padding">6dp</dimen>
+ <dimen name="avatar_picker_margin">2dp</dimen>
+</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/MobileNetworkDatabase.java b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/MobileNetworkDatabase.java
index 3c75abf..c1ee7ad 100644
--- a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/MobileNetworkDatabase.java
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/MobileNetworkDatabase.java
@@ -32,7 +32,6 @@
public abstract class MobileNetworkDatabase extends RoomDatabase {
public static final String TAG = "MobileNetworkDatabase";
- public static final String DATABASE_NAME = "mobilenetworksettings_db";
public abstract SubscriptionInfoDao mSubscriptionInfoDao();
@@ -47,8 +46,7 @@
* @return The MobileNetworkDatabase.
*/
public static MobileNetworkDatabase createDatabase(Context context) {
- return Room.databaseBuilder(context,
- MobileNetworkDatabase.class, DATABASE_NAME)
+ return Room.inMemoryDatabaseBuilder(context, MobileNetworkDatabase.class)
.fallbackToDestructiveMigration()
.enableMultiInstanceInvalidation()
.build();
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index f3614d3..5e0d935 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -300,5 +300,6 @@
dxflags: ["--multi-dex"],
required: [
"privapp_whitelist_com.android.systemui",
+ "wmshell.protolog.json.gz",
],
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/user/ui/compose/UserSwitcherScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/user/ui/compose/UserSwitcherScreen.kt
index 3175dcf..4d94bab 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/user/ui/compose/UserSwitcherScreen.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/user/ui/compose/UserSwitcherScreen.kt
@@ -17,8 +17,6 @@
package com.android.systemui.user.ui.compose
-import android.graphics.Bitmap
-import android.graphics.Canvas
import android.graphics.drawable.Drawable
import androidx.appcompat.content.res.AppCompatResources
import androidx.compose.foundation.Image
@@ -50,10 +48,8 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.painter.ColorPainter
-import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
@@ -62,6 +58,7 @@
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
+import androidx.core.graphics.drawable.toBitmap
import com.android.systemui.common.ui.compose.load
import com.android.systemui.compose.SysUiOutlinedButton
import com.android.systemui.compose.SysUiTextButton
@@ -356,10 +353,11 @@
remember(viewModel.iconResourceId) {
val drawable =
checkNotNull(AppCompatResources.getDrawable(context, viewModel.iconResourceId))
+ val size = with(density) { 20.dp.toPx() }.toInt()
drawable
.toBitmap(
- size = with(density) { 20.dp.toPx() }.toInt(),
- tintColor = Color.White,
+ width = size,
+ height = size,
)
.asImageBitmap()
}
@@ -392,32 +390,3 @@
),
)
}
-
-/**
- * Converts the [Drawable] to a [Bitmap].
- *
- * Note that this is a relatively memory-heavy operation as it allocates a whole bitmap and draws
- * the `Drawable` onto it. Use sparingly and with care.
- */
-private fun Drawable.toBitmap(
- size: Int? = null,
- tintColor: Color? = null,
-): Bitmap {
- val bitmap =
- if (intrinsicWidth <= 0 || intrinsicHeight <= 0) {
- Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
- } else {
- Bitmap.createBitmap(
- size ?: intrinsicWidth,
- size ?: intrinsicHeight,
- Bitmap.Config.ARGB_8888
- )
- }
- val canvas = Canvas(bitmap)
- setBounds(0, 0, canvas.width, canvas.height)
- if (tintColor != null) {
- setTint(tintColor.toArgb())
- }
- draw(canvas)
- return bitmap
-}
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions.xml b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
index 716c4fe..1ce106e 100644
--- a/packages/SystemUI/res-keyguard/layout/footer_actions.xml
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
@@ -61,7 +61,7 @@
</com.android.systemui.statusbar.phone.MultiUserSwitch>
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
- android:id="@id/settings_button_container"
+ android:id="@+id/settings_button_container"
android:layout_width="@dimen/qs_footer_action_button_size"
android:layout_height="@dimen/qs_footer_action_button_size"
android:background="@drawable/qs_footer_action_circle"
@@ -85,7 +85,7 @@
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
<com.android.systemui.statusbar.AlphaOptimizedImageView
- android:id="@id/pm_lite"
+ android:id="@+id/pm_lite"
android:layout_width="@dimen/qs_footer_action_button_size"
android:layout_height="@dimen/qs_footer_action_button_size"
android:background="@drawable/qs_footer_action_circle_color"
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index ba5f67f..f22e797 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -188,10 +188,5 @@
<item type="id" name="face_scanning_anim"/>
<item type="id" name="qqs_tile_layout"/>
-
- <!-- The buttons in the Quick Settings footer actions.-->
- <item type="id" name="settings_button_container"/>
- <item type="id" name="pm_lite"/>
-
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 1e5c53d..2cc5ccdc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -24,7 +24,6 @@
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
-import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
import android.animation.Animator;
@@ -107,8 +106,6 @@
return R.string.kg_prompt_reason_timeout_password;
case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
return R.string.kg_prompt_reason_timeout_password;
- case PROMPT_REASON_TRUSTAGENT_EXPIRED:
- return R.string.kg_prompt_reason_timeout_password;
case PROMPT_REASON_NONE:
return 0;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 5b22324..9871645 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -330,9 +330,6 @@
case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
break;
- case PROMPT_REASON_TRUSTAGENT_EXPIRED:
- mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
- break;
case PROMPT_REASON_NONE:
break;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 0a91150..c46e33d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -22,7 +22,6 @@
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
-import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
import android.animation.Animator;
@@ -124,8 +123,6 @@
return R.string.kg_prompt_reason_timeout_pin;
case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
return R.string.kg_prompt_reason_timeout_pin;
- case PROMPT_REASON_TRUSTAGENT_EXPIRED:
- return R.string.kg_prompt_reason_timeout_pin;
case PROMPT_REASON_NONE:
return 0;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
index 9d0a8ac..ac00e94 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
@@ -61,12 +61,6 @@
int PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT = 7;
/**
- * Some auth is required because the trustagent expired either from timeout or manually by
- * the user
- */
- int PROMPT_REASON_TRUSTAGENT_EXPIRED = 8;
-
- /**
* Reset the view and prepare to take input. This should do things like clearing the
* password or pattern and clear error messages.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
index 80b9c4e..50012a5 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.keyguard.logging
import com.android.systemui.log.LogBuffer
@@ -14,6 +30,11 @@
private const val TAG = "KeyguardLog"
+/**
+ * Generic logger for keyguard that's wrapping [LogBuffer]. This class should be used for adding
+ * temporary logs or logs for smaller classes when creating whole new [LogBuffer] wrapper might be
+ * an overkill.
+ */
class KeyguardLogger @Inject constructor(@KeyguardLog private val buffer: LogBuffer) {
fun d(@CompileTimeConstant msg: String) = log(msg, DEBUG)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 00236f1..2cca086 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -881,6 +881,7 @@
static WindowManager.LayoutParams getLayoutParams(IBinder windowToken, CharSequence title) {
final int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
| WindowManager.LayoutParams.FLAG_SECURE
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DIM_BEHIND;
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
diff --git a/packages/SystemUI/src/com/android/systemui/containeddrawable/ContainedDrawable.kt b/packages/SystemUI/src/com/android/systemui/containeddrawable/ContainedDrawable.kt
deleted file mode 100644
index d6a059d..0000000
--- a/packages/SystemUI/src/com/android/systemui/containeddrawable/ContainedDrawable.kt
+++ /dev/null
@@ -1,27 +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.systemui.containeddrawable
-
-import android.graphics.drawable.Drawable
-import androidx.annotation.DrawableRes
-
-/** Convenience container for [Drawable] or a way to load it later. */
-sealed class ContainedDrawable {
- data class WithDrawable(val drawable: Drawable) : ContainedDrawable()
- data class WithResource(@DrawableRes val resourceId: Int) : ContainedDrawable()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 3b629f7..2e46b8e 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -172,7 +172,7 @@
public static final ResourceBooleanFlag FULL_SCREEN_USER_SWITCHER =
new ResourceBooleanFlag(506, R.bool.config_enableFullscreenUserSwitcher);
- public static final ReleasedFlag NEW_FOOTER_ACTIONS = new ReleasedFlag(507);
+ public static final UnreleasedFlag NEW_FOOTER_ACTIONS = new UnreleasedFlag(507, true);
/***************************************/
// 600- status bar
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index edd6188f..5816f6b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -25,7 +25,6 @@
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_OCCLUSION;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_UNLOCK_ANIMATION;
-import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -804,9 +803,6 @@
} else if (trustAgentsEnabled
&& (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) {
return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
- } else if (trustAgentsEnabled
- && (strongAuth & SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED) != 0) {
- return KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED;
} else if (any && ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0
|| mUpdateMonitor.isFingerprintLockedOut())) {
return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 4c4b588..45b668e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -157,7 +157,11 @@
}
}
dozeHost.addCallback(callback)
- trySendWithFailureLogging(false, TAG, "initial isDozing: false")
+ trySendWithFailureLogging(
+ statusBarStateController.isDozing,
+ TAG,
+ "initial isDozing",
+ )
awaitClose { dozeHost.removeCallback(callback) }
}
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 95acc0b..01cd3e2 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
@@ -104,7 +104,6 @@
KeyguardQuickAffordanceModel.Visible(
configKey = configs[index]::class,
icon = visibleState.icon,
- contentDescriptionResourceId = visibleState.contentDescriptionResourceId,
)
} else {
KeyguardQuickAffordanceModel.Hidden
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordanceModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordanceModel.kt
index eff1469..eb6bb92 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordanceModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/model/KeyguardQuickAffordanceModel.kt
@@ -17,8 +17,7 @@
package com.android.systemui.keyguard.domain.model
-import androidx.annotation.StringRes
-import com.android.systemui.containeddrawable.ContainedDrawable
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
import kotlin.reflect.KClass
@@ -35,11 +34,6 @@
/** Identifier for the affordance this is modeling. */
val configKey: KClass<out KeyguardQuickAffordanceConfig>,
/** An icon for the affordance. */
- val icon: ContainedDrawable,
- /**
- * Resource ID for a string to use for the accessibility content description text of the
- * affordance.
- */
- @StringRes val contentDescriptionResourceId: Int,
+ val icon: Icon,
) : KeyguardQuickAffordanceModel()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
index ac2c9b1..89604f0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/HomeControlsKeyguardQuickAffordanceConfig.kt
@@ -23,7 +23,8 @@
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.containeddrawable.ContainedDrawable
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.controller.StructureInfo
import com.android.systemui.controls.dagger.ControlsComponent
@@ -122,8 +123,14 @@
visibility == ControlsComponent.Visibility.AVAILABLE
) {
KeyguardQuickAffordanceConfig.State.Visible(
- icon = ContainedDrawable.WithResource(iconResourceId),
- contentDescriptionResourceId = component.getTileTitleId(),
+ icon =
+ Icon.Resource(
+ res = iconResourceId,
+ contentDescription =
+ ContentDescription.Resource(
+ res = component.getTileTitleId(),
+ ),
+ ),
)
} else {
KeyguardQuickAffordanceConfig.State.Hidden
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceConfig.kt
index 8fb952c..8e1c6b7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceConfig.kt
@@ -18,9 +18,8 @@
package com.android.systemui.keyguard.domain.quickaffordance
import android.content.Intent
-import androidx.annotation.StringRes
import com.android.systemui.animation.ActivityLaunchAnimator
-import com.android.systemui.containeddrawable.ContainedDrawable
+import com.android.systemui.common.shared.model.Icon
import kotlinx.coroutines.flow.Flow
/** Defines interface that can act as data source for a single quick affordance model. */
@@ -44,12 +43,7 @@
/** An affordance is visible. */
data class Visible(
/** An icon for the affordance. */
- val icon: ContainedDrawable,
- /**
- * Resource ID for a string to use for the accessibility content description text of the
- * affordance.
- */
- @StringRes val contentDescriptionResourceId: Int,
+ val icon: Icon,
) : State()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
index c8e5e4a..d97deaf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt
@@ -21,7 +21,8 @@
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.containeddrawable.ContainedDrawable
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
import javax.inject.Inject
@@ -76,8 +77,14 @@
private fun state(): KeyguardQuickAffordanceConfig.State {
return if (controller.isEnabledForLockScreenButton) {
KeyguardQuickAffordanceConfig.State.Visible(
- icon = ContainedDrawable.WithResource(R.drawable.ic_qr_code_scanner),
- contentDescriptionResourceId = R.string.accessibility_qr_code_scanner_button,
+ icon =
+ Icon.Resource(
+ res = R.drawable.ic_qr_code_scanner,
+ contentDescription =
+ ContentDescription.Resource(
+ res = R.string.accessibility_qr_code_scanner_button,
+ ),
+ ),
)
} else {
KeyguardQuickAffordanceConfig.State.Hidden
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
index 885af33..9196b09 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt
@@ -26,7 +26,8 @@
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
-import com.android.systemui.containeddrawable.ContainedDrawable
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.wallet.controller.QuickAccessWalletController
@@ -100,8 +101,14 @@
): KeyguardQuickAffordanceConfig.State {
return if (isFeatureEnabled && hasCard && tileIcon != null) {
KeyguardQuickAffordanceConfig.State.Visible(
- icon = ContainedDrawable.WithDrawable(tileIcon),
- contentDescriptionResourceId = R.string.accessibility_wallet_button,
+ icon =
+ Icon.Loaded(
+ drawable = tileIcon,
+ contentDescription =
+ ContentDescription.Resource(
+ res = R.string.accessibility_wallet_button,
+ ),
+ ),
)
} else {
KeyguardQuickAffordanceConfig.State.Hidden
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index c4e3d4e..65b85c0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -31,7 +31,7 @@
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.animation.Interpolators
-import com.android.systemui.containeddrawable.ContainedDrawable
+import com.android.systemui.common.ui.binder.IconViewBinder
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordanceViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -236,10 +236,7 @@
}
}
- when (viewModel.icon) {
- is ContainedDrawable.WithDrawable -> view.setImageDrawable(viewModel.icon.drawable)
- is ContainedDrawable.WithResource -> view.setImageResource(viewModel.icon.resourceId)
- }
+ IconViewBinder.bind(viewModel.icon, view)
view.drawable.setTint(
Utils.getColorAttrDefaultColor(
@@ -250,7 +247,6 @@
view.backgroundTintList =
Utils.getColorAttr(view.context, com.android.internal.R.attr.colorSurface)
- view.contentDescription = view.context.getString(viewModel.contentDescriptionResourceId)
view.isClickable = viewModel.isClickable
if (viewModel.isClickable) {
view.setOnClickListener(OnClickListener(viewModel, falsingManager))
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
index e3ebac6..970ee4c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt
@@ -116,7 +116,6 @@
isVisible = true,
animateReveal = animateReveal,
icon = icon,
- contentDescriptionResourceId = contentDescriptionResourceId,
onClicked = { parameters ->
quickAffordanceInteractor.onQuickAffordanceClicked(
configKey = parameters.configKey,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
index b1de27d..0971f13 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordanceViewModel.kt
@@ -16,9 +16,8 @@
package com.android.systemui.keyguard.ui.viewmodel
-import androidx.annotation.StringRes
import com.android.systemui.animation.ActivityLaunchAnimator
-import com.android.systemui.containeddrawable.ContainedDrawable
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
import kotlin.reflect.KClass
@@ -28,8 +27,7 @@
val isVisible: Boolean = false,
/** Whether to animate the transition of the quick affordance from invisible to visible. */
val animateReveal: Boolean = false,
- val icon: ContainedDrawable = ContainedDrawable.WithResource(0),
- @StringRes val contentDescriptionResourceId: Int = 0,
+ val icon: Icon = Icon.Resource(res = 0, contentDescription = null),
val onClicked: (OnClickedParameters) -> Unit = {},
val isClickable: Boolean = false,
) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
index dd1ffcc..28ddead 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
@@ -222,7 +222,6 @@
private fun bindButton(button: IconButtonViewHolder, model: FooterActionsButtonViewModel?) {
val buttonView = button.view
- buttonView.id = model?.id ?: View.NO_ID
buttonView.isVisible = model != null
if (model == null) {
return
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
index 5a8f684..2ad0513 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsButtonViewModel.kt
@@ -25,7 +25,6 @@
* power buttons.
*/
data class FooterActionsButtonViewModel(
- val id: Int?,
val icon: Icon,
val iconTint: Int?,
@DrawableRes val background: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index 8b3f4b4..a935338 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -138,7 +138,6 @@
/** The model for the settings button. */
val settings: FooterActionsButtonViewModel =
FooterActionsButtonViewModel(
- id = R.id.settings_button_container,
Icon.Resource(
R.drawable.ic_settings,
ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
@@ -152,7 +151,6 @@
val power: FooterActionsButtonViewModel? =
if (showPowerButton) {
FooterActionsButtonViewModel(
- id = R.id.pm_lite,
Icon.Resource(
android.R.drawable.ic_lock_power_off,
ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
@@ -258,7 +256,6 @@
}
return FooterActionsButtonViewModel(
- id = null,
Icon.Loaded(
icon,
ContentDescription.Loaded(userSwitcherContentDescription(status.currentUserName)),
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index 0f7e143..6b3beeb 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -16,21 +16,17 @@
package com.android.systemui.wallpapers;
-import static android.view.Display.DEFAULT_DISPLAY;
-
import static com.android.systemui.flags.Flags.USE_CANVAS_RENDERER;
import android.app.WallpaperColors;
import android.app.WallpaperManager;
-import android.content.ComponentCallbacks2;
-import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
-import android.os.AsyncTask;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
@@ -40,8 +36,6 @@
import android.util.Log;
import android.util.MathUtils;
import android.util.Size;
-import android.view.Display;
-import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.WindowManager;
@@ -49,8 +43,11 @@
import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.wallpapers.canvas.ImageCanvasWallpaperRenderer;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.wallpapers.canvas.WallpaperColorExtractor;
import com.android.systemui.wallpapers.gl.EglHelper;
import com.android.systemui.wallpapers.gl.ImageWallpaperRenderer;
@@ -59,6 +56,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -78,15 +76,28 @@
private final ArrayList<RectF> mLocalColorsToAdd = new ArrayList<>();
private final ArraySet<RectF> mColorAreas = new ArraySet<>();
private volatile int mPages = 1;
+ private boolean mPagesComputed = false;
private HandlerThread mWorker;
// scaled down version
private Bitmap mMiniBitmap;
private final FeatureFlags mFeatureFlags;
+ // used in canvasEngine to load/unload the bitmap and extract the colors
+ @Background
+ private final DelayableExecutor mBackgroundExecutor;
+ private static final int DELAY_UNLOAD_BITMAP = 2000;
+
+ @Main
+ private final Executor mMainExecutor;
+
@Inject
- public ImageWallpaper(FeatureFlags featureFlags) {
+ public ImageWallpaper(FeatureFlags featureFlags,
+ @Background DelayableExecutor backgroundExecutor,
+ @Main Executor mainExecutor) {
super();
mFeatureFlags = featureFlags;
+ mBackgroundExecutor = backgroundExecutor;
+ mMainExecutor = mainExecutor;
}
@Override
@@ -339,7 +350,6 @@
imgArea.left = 0;
imgArea.right = 1;
}
-
return imgArea;
}
@@ -510,69 +520,84 @@
class CanvasEngine extends WallpaperService.Engine implements DisplayListener {
-
- // time [ms] before unloading the wallpaper after it is loaded
- private static final int DELAY_FORGET_WALLPAPER = 5000;
-
- private final Runnable mUnloadWallpaperCallback = this::unloadWallpaper;
-
private WallpaperManager mWallpaperManager;
- private ImageCanvasWallpaperRenderer mImageCanvasWallpaperRenderer;
+ private final WallpaperColorExtractor mWallpaperColorExtractor;
+ private SurfaceHolder mSurfaceHolder;
+ @VisibleForTesting
+ static final int MIN_SURFACE_WIDTH = 128;
+ @VisibleForTesting
+ static final int MIN_SURFACE_HEIGHT = 128;
private Bitmap mBitmap;
- private Display mDisplay;
- private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
-
- private AsyncTask<Void, Void, Bitmap> mLoader;
- private boolean mNeedsDrawAfterLoadingWallpaper = false;
+ /*
+ * Counter to unload the bitmap as soon as possible.
+ * Before any bitmap operation, this is incremented.
+ * After an operation completion, this is decremented (synchronously),
+ * and if the count is 0, unload the bitmap
+ */
+ private int mBitmapUsages = 0;
+ private final Object mLock = new Object();
CanvasEngine() {
super();
setFixedSizeAllowed(true);
setShowForAllUsers(true);
- }
+ mWallpaperColorExtractor = new WallpaperColorExtractor(
+ mBackgroundExecutor,
+ new WallpaperColorExtractor.WallpaperColorExtractorCallback() {
+ @Override
+ public void onColorsProcessed(List<RectF> regions,
+ List<WallpaperColors> colors) {
+ CanvasEngine.this.onColorsProcessed(regions, colors);
+ }
- void trimMemory(int level) {
- if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW
- && level <= ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL
- && isBitmapLoaded()) {
- if (DEBUG) {
- Log.d(TAG, "trimMemory");
- }
- unloadWallpaper();
+ @Override
+ public void onMiniBitmapUpdated() {
+ CanvasEngine.this.onMiniBitmapUpdated();
+ }
+
+ @Override
+ public void onActivated() {
+ setOffsetNotificationsEnabled(true);
+ }
+
+ @Override
+ public void onDeactivated() {
+ setOffsetNotificationsEnabled(false);
+ }
+ });
+
+ // if the number of pages is already computed, transmit it to the color extractor
+ if (mPagesComputed) {
+ mWallpaperColorExtractor.onPageChanged(mPages);
}
}
@Override
public void onCreate(SurfaceHolder surfaceHolder) {
+ Trace.beginSection("ImageWallpaper.CanvasEngine#onCreate");
if (DEBUG) {
Log.d(TAG, "onCreate");
}
+ mWallpaperManager = getDisplayContext().getSystemService(WallpaperManager.class);
+ mSurfaceHolder = surfaceHolder;
+ Rect dimensions = mWallpaperManager.peekBitmapDimensions();
+ int width = Math.max(MIN_SURFACE_WIDTH, dimensions.width());
+ int height = Math.max(MIN_SURFACE_HEIGHT, dimensions.height());
+ mSurfaceHolder.setFixedSize(width, height);
- mWallpaperManager = getSystemService(WallpaperManager.class);
- super.onCreate(surfaceHolder);
-
- final Context displayContext = getDisplayContext();
- final int displayId = displayContext == null ? DEFAULT_DISPLAY :
- displayContext.getDisplayId();
- DisplayManager dm = getSystemService(DisplayManager.class);
- if (dm != null) {
- mDisplay = dm.getDisplay(displayId);
- if (mDisplay == null) {
- Log.e(TAG, "Cannot find display! Fallback to default.");
- mDisplay = dm.getDisplay(DEFAULT_DISPLAY);
- }
- }
- setOffsetNotificationsEnabled(false);
-
- mImageCanvasWallpaperRenderer = new ImageCanvasWallpaperRenderer(surfaceHolder);
- loadWallpaper(false);
+ getDisplayContext().getSystemService(DisplayManager.class)
+ .registerDisplayListener(this, null);
+ getDisplaySizeAndUpdateColorExtractor();
+ Trace.endSection();
}
@Override
public void onDestroy() {
- super.onDestroy();
- unloadWallpaper();
+ getDisplayContext().getSystemService(DisplayManager.class)
+ .unregisterDisplayListener(this);
+ mWallpaperColorExtractor.cleanUp();
+ unloadBitmap();
}
@Override
@@ -581,31 +606,30 @@
}
@Override
+ public boolean shouldWaitForEngineShown() {
+ return true;
+ }
+
+ @Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (DEBUG) {
Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height);
}
- super.onSurfaceChanged(holder, format, width, height);
- mImageCanvasWallpaperRenderer.setSurfaceHolder(holder);
- drawFrame(false);
}
@Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
- super.onSurfaceDestroyed(holder);
if (DEBUG) {
Log.i(TAG, "onSurfaceDestroyed");
}
- mImageCanvasWallpaperRenderer.setSurfaceHolder(null);
+ mSurfaceHolder = null;
}
@Override
public void onSurfaceCreated(SurfaceHolder holder) {
- super.onSurfaceCreated(holder);
if (DEBUG) {
Log.i(TAG, "onSurfaceCreated");
}
- mImageCanvasWallpaperRenderer.setSurfaceHolder(holder);
}
@Override
@@ -613,135 +637,88 @@
if (DEBUG) {
Log.d(TAG, "onSurfaceRedrawNeeded");
}
- super.onSurfaceRedrawNeeded(holder);
- // At the end of this method we should have drawn into the surface.
- // This means that the bitmap should be loaded synchronously if
- // it was already unloaded.
- if (!isBitmapLoaded()) {
- setBitmap(mWallpaperManager.getBitmap(true /* hardware */));
+ drawFrame();
+ }
+
+ private void drawFrame() {
+ mBackgroundExecutor.execute(this::drawFrameSynchronized);
+ }
+
+ private void drawFrameSynchronized() {
+ synchronized (mLock) {
+ drawFrameInternal();
}
- drawFrame(true);
}
- private DisplayInfo getDisplayInfo() {
- mDisplay.getDisplayInfo(mTmpDisplayInfo);
- return mTmpDisplayInfo;
- }
-
- private void drawFrame(boolean forceRedraw) {
- if (!mImageCanvasWallpaperRenderer.isSurfaceHolderLoaded()) {
+ private void drawFrameInternal() {
+ if (mSurfaceHolder == null) {
Log.e(TAG, "attempt to draw a frame without a valid surface");
return;
}
+ // load the wallpaper if not already done
if (!isBitmapLoaded()) {
- // ensure that we load the wallpaper.
- // if the wallpaper is currently loading, this call will have no effect.
- loadWallpaper(true);
- return;
- }
- mImageCanvasWallpaperRenderer.drawFrame(mBitmap, forceRedraw);
- }
-
- private void setBitmap(Bitmap bitmap) {
- if (bitmap == null) {
- Log.e(TAG, "Attempt to set a null bitmap");
- } else if (mBitmap == bitmap) {
- Log.e(TAG, "The value of bitmap is the same");
- } else if (bitmap.getWidth() < 1 || bitmap.getHeight() < 1) {
- Log.e(TAG, "Attempt to set an invalid wallpaper of length "
- + bitmap.getWidth() + "x" + bitmap.getHeight());
+ loadWallpaperAndDrawFrameInternal();
} else {
- if (mBitmap != null) {
- mBitmap.recycle();
- }
- mBitmap = bitmap;
+ mBitmapUsages++;
+
+ // drawing is done on the main thread
+ mMainExecutor.execute(() -> {
+ drawFrameOnCanvas(mBitmap);
+ reportEngineShown(false);
+ unloadBitmapIfNotUsed();
+ });
}
}
- private boolean isBitmapLoaded() {
+ @VisibleForTesting
+ void drawFrameOnCanvas(Bitmap bitmap) {
+ Trace.beginSection("ImageWallpaper.CanvasEngine#drawFrame");
+ // TODO change SurfaceHolder API to add wcg support
+ Canvas c = mSurfaceHolder.lockHardwareCanvas();
+ if (c != null) {
+ Rect dest = mSurfaceHolder.getSurfaceFrame();
+ try {
+ c.drawBitmap(bitmap, null, dest, null);
+ } finally {
+ mSurfaceHolder.unlockCanvasAndPost(c);
+ }
+ }
+ Trace.endSection();
+ }
+
+ @VisibleForTesting
+ boolean isBitmapLoaded() {
return mBitmap != null && !mBitmap.isRecycled();
}
- /**
- * Loads the wallpaper on background thread and schedules updating the surface frame,
- * and if {@code needsDraw} is set also draws a frame.
- *
- * If loading is already in-flight, subsequent loads are ignored (but needDraw is or-ed to
- * the active request).
- *
- */
- private void loadWallpaper(boolean needsDraw) {
- mNeedsDrawAfterLoadingWallpaper |= needsDraw;
- if (mLoader != null) {
- if (DEBUG) {
- Log.d(TAG, "Skipping loadWallpaper, already in flight ");
- }
- return;
- }
- mLoader = new AsyncTask<Void, Void, Bitmap>() {
- @Override
- protected Bitmap doInBackground(Void... params) {
- Throwable exception;
- try {
- Bitmap wallpaper = mWallpaperManager.getBitmap(true /* hardware */);
- if (wallpaper != null
- && wallpaper.getByteCount() > RecordingCanvas.MAX_BITMAP_SIZE) {
- throw new RuntimeException("Wallpaper is too large to draw!");
- }
- return wallpaper;
- } catch (RuntimeException | OutOfMemoryError e) {
- exception = e;
- }
-
- if (isCancelled()) {
- return null;
- }
-
- // Note that if we do fail at this, and the default wallpaper can't
- // be loaded, we will go into a cycle. Don't do a build where the
- // default wallpaper can't be loaded.
- Log.w(TAG, "Unable to load wallpaper!", exception);
- try {
- mWallpaperManager.clear();
- } catch (IOException ex) {
- // now we're really screwed.
- Log.w(TAG, "Unable reset to default wallpaper!", ex);
- }
-
- if (isCancelled()) {
- return null;
- }
-
- try {
- return mWallpaperManager.getBitmap(true /* hardware */);
- } catch (RuntimeException | OutOfMemoryError e) {
- Log.w(TAG, "Unable to load default wallpaper!", e);
- }
- return null;
- }
-
- @Override
- protected void onPostExecute(Bitmap bitmap) {
- setBitmap(bitmap);
-
- if (mNeedsDrawAfterLoadingWallpaper) {
- drawFrame(true);
- }
-
- mLoader = null;
- mNeedsDrawAfterLoadingWallpaper = false;
- scheduleUnloadWallpaper();
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ private void unloadBitmapIfNotUsed() {
+ mBackgroundExecutor.execute(this::unloadBitmapIfNotUsedSynchronized);
}
- private void unloadWallpaper() {
- if (mLoader != null) {
- mLoader.cancel(false);
- mLoader = null;
+ private void unloadBitmapIfNotUsedSynchronized() {
+ synchronized (mLock) {
+ mBitmapUsages -= 1;
+ if (mBitmapUsages <= 0) {
+ mBitmapUsages = 0;
+ unloadBitmapInternal();
+ }
}
+ }
+ private void unloadBitmap() {
+ mBackgroundExecutor.execute(this::unloadBitmapSynchronized);
+ }
+
+ private void unloadBitmapSynchronized() {
+ synchronized (mLock) {
+ mBitmapUsages = 0;
+ unloadBitmapInternal();
+ }
+ }
+
+ private void unloadBitmapInternal() {
+ Trace.beginSection("ImageWallpaper.CanvasEngine#unloadBitmap");
if (mBitmap != null) {
mBitmap.recycle();
}
@@ -750,12 +727,133 @@
final Surface surface = getSurfaceHolder().getSurface();
surface.hwuiDestroy();
mWallpaperManager.forgetLoadedWallpaper();
+ Trace.endSection();
}
- private void scheduleUnloadWallpaper() {
- Handler handler = getMainThreadHandler();
- handler.removeCallbacks(mUnloadWallpaperCallback);
- handler.postDelayed(mUnloadWallpaperCallback, DELAY_FORGET_WALLPAPER);
+ private void loadWallpaperAndDrawFrameInternal() {
+ Trace.beginSection("ImageWallpaper.CanvasEngine#loadWallpaper");
+ boolean loadSuccess = false;
+ Bitmap bitmap;
+ try {
+ bitmap = mWallpaperManager.getBitmap(false);
+ if (bitmap != null
+ && bitmap.getByteCount() > RecordingCanvas.MAX_BITMAP_SIZE) {
+ throw new RuntimeException("Wallpaper is too large to draw!");
+ }
+ } catch (RuntimeException | OutOfMemoryError exception) {
+
+ // Note that if we do fail at this, and the default wallpaper can't
+ // be loaded, we will go into a cycle. Don't do a build where the
+ // default wallpaper can't be loaded.
+ Log.w(TAG, "Unable to load wallpaper!", exception);
+ try {
+ mWallpaperManager.clear(WallpaperManager.FLAG_SYSTEM);
+ } catch (IOException ex) {
+ // now we're really screwed.
+ Log.w(TAG, "Unable reset to default wallpaper!", ex);
+ }
+
+ try {
+ bitmap = mWallpaperManager.getBitmap(false);
+ } catch (RuntimeException | OutOfMemoryError e) {
+ Log.w(TAG, "Unable to load default wallpaper!", e);
+ bitmap = null;
+ }
+ }
+
+ if (bitmap == null) {
+ Log.w(TAG, "Could not load bitmap");
+ } else if (bitmap.isRecycled()) {
+ Log.e(TAG, "Attempt to load a recycled bitmap");
+ } else if (mBitmap == bitmap) {
+ Log.e(TAG, "Loaded a bitmap that was already loaded");
+ } else if (bitmap.getWidth() < 1 || bitmap.getHeight() < 1) {
+ Log.e(TAG, "Attempt to load an invalid wallpaper of length "
+ + bitmap.getWidth() + "x" + bitmap.getHeight());
+ } else {
+ // at this point, loading is done correctly.
+ loadSuccess = true;
+ // recycle the previously loaded bitmap
+ if (mBitmap != null) {
+ mBitmap.recycle();
+ }
+ mBitmap = bitmap;
+
+ // +2 usages for the color extraction and the delayed unload.
+ mBitmapUsages += 2;
+ recomputeColorExtractorMiniBitmap();
+ drawFrameInternal();
+
+ /*
+ * after loading, the bitmap will be unloaded after all these conditions:
+ * - the frame is redrawn
+ * - the mini bitmap from color extractor is recomputed
+ * - the DELAY_UNLOAD_BITMAP has passed
+ */
+ mBackgroundExecutor.executeDelayed(
+ this::unloadBitmapIfNotUsedSynchronized, DELAY_UNLOAD_BITMAP);
+ }
+ // even if the bitmap cannot be loaded, call reportEngineShown
+ if (!loadSuccess) reportEngineShown(false);
+ Trace.endSection();
+ }
+
+ private void onColorsProcessed(List<RectF> regions, List<WallpaperColors> colors) {
+ try {
+ notifyLocalColorsChanged(regions, colors);
+ } catch (RuntimeException e) {
+ Log.e(TAG, e.getMessage(), e);
+ }
+ }
+
+ @VisibleForTesting
+ void recomputeColorExtractorMiniBitmap() {
+ mWallpaperColorExtractor.onBitmapChanged(mBitmap);
+ }
+
+ @VisibleForTesting
+ void onMiniBitmapUpdated() {
+ unloadBitmapIfNotUsed();
+ }
+
+ @Override
+ public boolean supportsLocalColorExtraction() {
+ return true;
+ }
+
+ @Override
+ public void addLocalColorsAreas(@NonNull List<RectF> regions) {
+ // this call will activate the offset notifications
+ // if no colors were being processed before
+ mWallpaperColorExtractor.addLocalColorsAreas(regions);
+ }
+
+ @Override
+ public void removeLocalColorsAreas(@NonNull List<RectF> regions) {
+ // this call will deactivate the offset notifications
+ // if we are no longer processing colors
+ mWallpaperColorExtractor.removeLocalColorAreas(regions);
+ }
+
+ @Override
+ public void onOffsetsChanged(float xOffset, float yOffset,
+ float xOffsetStep, float yOffsetStep,
+ int xPixelOffset, int yPixelOffset) {
+ /*
+ * TODO check this formula. mPages is always >= 4, even when launcher is single-paged
+ * this formula is also used in the GL engine
+ */
+ final int pages;
+ if (xOffsetStep > 0 && xOffsetStep <= 1) {
+ pages = Math.round(1 / xOffsetStep) + 1;
+ } else {
+ pages = 1;
+ }
+ if (pages != mPages || !mPagesComputed) {
+ mPages = pages;
+ mPagesComputed = true;
+ mWallpaperColorExtractor.onPageChanged(mPages);
+ }
}
@Override
@@ -764,13 +862,46 @@
}
@Override
- public void onDisplayChanged(int displayId) {
+ public void onDisplayRemoved(int displayId) {
}
@Override
- public void onDisplayRemoved(int displayId) {
+ public void onDisplayChanged(int displayId) {
+ // changes the display in the color extractor
+ // the new display dimensions will be used in the next color computation
+ if (displayId == getDisplayContext().getDisplayId()) {
+ getDisplaySizeAndUpdateColorExtractor();
+ }
+ }
+ private void getDisplaySizeAndUpdateColorExtractor() {
+ Rect window = getDisplayContext()
+ .getSystemService(WindowManager.class)
+ .getCurrentWindowMetrics()
+ .getBounds();
+ mWallpaperColorExtractor.setDisplayDimensions(window.width(), window.height());
+ }
+
+
+ @Override
+ protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
+ super.dump(prefix, fd, out, args);
+ out.print(prefix); out.print("Engine="); out.println(this);
+ out.print(prefix); out.print("valid surface=");
+ out.println(getSurfaceHolder() != null && getSurfaceHolder().getSurface() != null
+ ? getSurfaceHolder().getSurface().isValid()
+ : "null");
+
+ out.print(prefix); out.print("surface frame=");
+ out.println(getSurfaceHolder() != null ? getSurfaceHolder().getSurfaceFrame() : "null");
+
+ out.print(prefix); out.print("bitmap=");
+ out.println(mBitmap == null ? "null"
+ : mBitmap.isRecycled() ? "recycled"
+ : mBitmap.getWidth() + "x" + mBitmap.getHeight());
+
+ mWallpaperColorExtractor.dump(prefix, fd, out, args);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRenderer.java
deleted file mode 100644
index fdba16e..0000000
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRenderer.java
+++ /dev/null
@@ -1,145 +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.systemui.wallpapers.canvas;
-
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.util.Log;
-import android.view.SurfaceHolder;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Helper to draw a wallpaper on a surface.
- * It handles the geometry regarding the dimensions of the display and the wallpaper,
- * and rescales the surface and the wallpaper accordingly.
- */
-public class ImageCanvasWallpaperRenderer {
-
- private static final String TAG = ImageCanvasWallpaperRenderer.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- private SurfaceHolder mSurfaceHolder;
- //private Bitmap mBitmap = null;
-
- @VisibleForTesting
- static final int MIN_SURFACE_WIDTH = 128;
- @VisibleForTesting
- static final int MIN_SURFACE_HEIGHT = 128;
-
- private boolean mSurfaceRedrawNeeded;
-
- private int mLastSurfaceWidth = -1;
- private int mLastSurfaceHeight = -1;
-
- public ImageCanvasWallpaperRenderer(SurfaceHolder surfaceHolder) {
- mSurfaceHolder = surfaceHolder;
- }
-
- /**
- * Set the surface holder on which to draw.
- * Should be called when the surface holder is created or changed
- * @param surfaceHolder the surface on which to draw the wallpaper
- */
- public void setSurfaceHolder(SurfaceHolder surfaceHolder) {
- mSurfaceHolder = surfaceHolder;
- }
-
- /**
- * Check if a surface holder is loaded
- * @return true if a valid surfaceHolder has been set.
- */
- public boolean isSurfaceHolderLoaded() {
- return mSurfaceHolder != null;
- }
-
- /**
- * Computes and set the surface dimensions, by using the play and the bitmap dimensions.
- * The Bitmap must be loaded before any call to this function
- */
- private boolean updateSurfaceSize(Bitmap bitmap) {
- int surfaceWidth = Math.max(MIN_SURFACE_WIDTH, bitmap.getWidth());
- int surfaceHeight = Math.max(MIN_SURFACE_HEIGHT, bitmap.getHeight());
- boolean surfaceChanged =
- surfaceWidth != mLastSurfaceWidth || surfaceHeight != mLastSurfaceHeight;
- if (surfaceChanged) {
- /*
- Used a fixed size surface, because we are special. We can do
- this because we know the current design of window animations doesn't
- cause this to break.
- */
- mSurfaceHolder.setFixedSize(surfaceWidth, surfaceHeight);
- mLastSurfaceWidth = surfaceWidth;
- mLastSurfaceHeight = surfaceHeight;
- }
- return surfaceChanged;
- }
-
- /**
- * Draw a the wallpaper on the surface.
- * The bitmap and the surface must be loaded before calling
- * this function.
- * @param forceRedraw redraw the wallpaper even if no changes are detected
- */
- public void drawFrame(Bitmap bitmap, boolean forceRedraw) {
-
- if (bitmap == null || bitmap.isRecycled()) {
- Log.e(TAG, "Attempt to draw frame before background is loaded:");
- return;
- }
-
- if (bitmap.getWidth() < 1 || bitmap.getHeight() < 1) {
- Log.e(TAG, "Attempt to set an invalid wallpaper of length "
- + bitmap.getWidth() + "x" + bitmap.getHeight());
- return;
- }
-
- mSurfaceRedrawNeeded |= forceRedraw;
- boolean surfaceChanged = updateSurfaceSize(bitmap);
-
- boolean redrawNeeded = surfaceChanged || mSurfaceRedrawNeeded;
- mSurfaceRedrawNeeded = false;
-
- if (!redrawNeeded) {
- if (DEBUG) {
- Log.d(TAG, "Suppressed drawFrame since redraw is not needed ");
- }
- return;
- }
-
- if (DEBUG) {
- Log.d(TAG, "Redrawing wallpaper");
- }
- drawWallpaperWithCanvas(bitmap);
- }
-
- @VisibleForTesting
- void drawWallpaperWithCanvas(Bitmap bitmap) {
- Canvas c = mSurfaceHolder.lockHardwareCanvas();
- if (c != null) {
- Rect dest = mSurfaceHolder.getSurfaceFrame();
- Log.i(TAG, "Redrawing in rect: " + dest + " with surface size: "
- + mLastSurfaceWidth + "x" + mLastSurfaceHeight);
- try {
- c.drawBitmap(bitmap, null, dest, null);
- } finally {
- mSurfaceHolder.unlockCanvasAndPost(c);
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/canvas/WallpaperColorExtractor.java b/packages/SystemUI/src/com/android/systemui/wallpapers/canvas/WallpaperColorExtractor.java
new file mode 100644
index 0000000..e2e4555
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/canvas/WallpaperColorExtractor.java
@@ -0,0 +1,400 @@
+/*
+ * 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.systemui.wallpapers.canvas;
+
+import android.app.WallpaperColors;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Trace;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.MathUtils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.util.Assert;
+import com.android.systemui.wallpapers.ImageWallpaper;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+/**
+ * This class is used by the {@link ImageWallpaper} to extract colors from areas of a wallpaper.
+ * It uses a background executor, and uses callbacks to inform that the work is done.
+ * It uses a downscaled version of the wallpaper to extract the colors.
+ */
+public class WallpaperColorExtractor {
+
+ private Bitmap mMiniBitmap;
+
+ @VisibleForTesting
+ static final int SMALL_SIDE = 128;
+
+ private static final String TAG = WallpaperColorExtractor.class.getSimpleName();
+ private static final @NonNull RectF LOCAL_COLOR_BOUNDS =
+ new RectF(0, 0, 1, 1);
+
+ private int mDisplayWidth = -1;
+ private int mDisplayHeight = -1;
+ private int mPages = -1;
+ private int mBitmapWidth = -1;
+ private int mBitmapHeight = -1;
+
+ private final Object mLock = new Object();
+
+ private final List<RectF> mPendingRegions = new ArrayList<>();
+ private final Set<RectF> mProcessedRegions = new ArraySet<>();
+
+ @Background
+ private final Executor mBackgroundExecutor;
+
+ private final WallpaperColorExtractorCallback mWallpaperColorExtractorCallback;
+
+ /**
+ * Interface to handle the callbacks after the different steps of the color extraction
+ */
+ public interface WallpaperColorExtractorCallback {
+ /**
+ * Callback after the colors of new regions have been extracted
+ * @param regions the list of new regions that have been processed
+ * @param colors the resulting colors for these regions, in the same order as the regions
+ */
+ void onColorsProcessed(List<RectF> regions, List<WallpaperColors> colors);
+
+ /**
+ * Callback after the mini bitmap is computed, to indicate that the wallpaper bitmap is
+ * no longer used by the color extractor and can be safely recycled
+ */
+ void onMiniBitmapUpdated();
+
+ /**
+ * Callback to inform that the extractor has started processing colors
+ */
+ void onActivated();
+
+ /**
+ * Callback to inform that no more colors are being processed
+ */
+ void onDeactivated();
+ }
+
+ /**
+ * Creates a new color extractor.
+ * @param backgroundExecutor the executor on which the color extraction will be performed
+ * @param wallpaperColorExtractorCallback an interface to handle the callbacks from
+ * the color extractor.
+ */
+ public WallpaperColorExtractor(@Background Executor backgroundExecutor,
+ WallpaperColorExtractorCallback wallpaperColorExtractorCallback) {
+ mBackgroundExecutor = backgroundExecutor;
+ mWallpaperColorExtractorCallback = wallpaperColorExtractorCallback;
+ }
+
+ /**
+ * Used by the outside to inform that the display size has changed.
+ * The new display size will be used in the next computations, but the current colors are
+ * not recomputed.
+ */
+ public void setDisplayDimensions(int displayWidth, int displayHeight) {
+ mBackgroundExecutor.execute(() ->
+ setDisplayDimensionsSynchronized(displayWidth, displayHeight));
+ }
+
+ private void setDisplayDimensionsSynchronized(int displayWidth, int displayHeight) {
+ synchronized (mLock) {
+ if (displayWidth == mDisplayWidth && displayHeight == mDisplayHeight) return;
+ mDisplayWidth = displayWidth;
+ mDisplayHeight = displayHeight;
+ processColorsInternal();
+ }
+ }
+
+ /**
+ * @return whether color extraction is currently in use
+ */
+ private boolean isActive() {
+ return mPendingRegions.size() + mProcessedRegions.size() > 0;
+ }
+
+ /**
+ * Should be called when the wallpaper is changed.
+ * This will recompute the mini bitmap
+ * and restart the extraction of all areas
+ * @param bitmap the new wallpaper
+ */
+ public void onBitmapChanged(@NonNull Bitmap bitmap) {
+ mBackgroundExecutor.execute(() -> onBitmapChangedSynchronized(bitmap));
+ }
+
+ private void onBitmapChangedSynchronized(@NonNull Bitmap bitmap) {
+ synchronized (mLock) {
+ if (bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
+ Log.e(TAG, "Attempt to extract colors from an invalid bitmap");
+ return;
+ }
+ mBitmapWidth = bitmap.getWidth();
+ mBitmapHeight = bitmap.getHeight();
+ mMiniBitmap = createMiniBitmap(bitmap);
+ mWallpaperColorExtractorCallback.onMiniBitmapUpdated();
+ recomputeColors();
+ }
+ }
+
+ /**
+ * Should be called when the number of pages is changed
+ * This will restart the extraction of all areas
+ * @param pages the total number of pages of the launcher
+ */
+ public void onPageChanged(int pages) {
+ mBackgroundExecutor.execute(() -> onPageChangedSynchronized(pages));
+ }
+
+ private void onPageChangedSynchronized(int pages) {
+ synchronized (mLock) {
+ if (mPages == pages) return;
+ mPages = pages;
+ if (mMiniBitmap != null && !mMiniBitmap.isRecycled()) {
+ recomputeColors();
+ }
+ }
+ }
+
+ // helper to recompute colors, to be called in synchronized methods
+ private void recomputeColors() {
+ mPendingRegions.addAll(mProcessedRegions);
+ mProcessedRegions.clear();
+ processColorsInternal();
+ }
+
+ /**
+ * Add new regions to extract
+ * This will trigger the color extraction and call the callback only for these new regions
+ * @param regions The areas of interest in our wallpaper (in screen pixel coordinates)
+ */
+ public void addLocalColorsAreas(@NonNull List<RectF> regions) {
+ if (regions.size() > 0) {
+ mBackgroundExecutor.execute(() -> addLocalColorsAreasSynchronized(regions));
+ } else {
+ Log.w(TAG, "Attempt to add colors with an empty list");
+ }
+ }
+
+ private void addLocalColorsAreasSynchronized(@NonNull List<RectF> regions) {
+ synchronized (mLock) {
+ boolean wasActive = isActive();
+ mPendingRegions.addAll(regions);
+ if (!wasActive && isActive()) {
+ mWallpaperColorExtractorCallback.onActivated();
+ }
+ processColorsInternal();
+ }
+ }
+
+ /**
+ * Remove regions to extract. If a color extraction is ongoing does not stop it.
+ * But if there are subsequent changes that restart the extraction, the removed regions
+ * will not be recomputed.
+ * @param regions The areas of interest in our wallpaper (in screen pixel coordinates)
+ */
+ public void removeLocalColorAreas(@NonNull List<RectF> regions) {
+ mBackgroundExecutor.execute(() -> removeLocalColorAreasSynchronized(regions));
+ }
+
+ private void removeLocalColorAreasSynchronized(@NonNull List<RectF> regions) {
+ synchronized (mLock) {
+ boolean wasActive = isActive();
+ mPendingRegions.removeAll(regions);
+ regions.forEach(mProcessedRegions::remove);
+ if (wasActive && !isActive()) {
+ mWallpaperColorExtractorCallback.onDeactivated();
+ }
+ }
+ }
+
+ /**
+ * Clean up the memory (in particular, the mini bitmap) used by this class.
+ */
+ public void cleanUp() {
+ mBackgroundExecutor.execute(this::cleanUpSynchronized);
+ }
+
+ private void cleanUpSynchronized() {
+ synchronized (mLock) {
+ if (mMiniBitmap != null) {
+ mMiniBitmap.recycle();
+ mMiniBitmap = null;
+ }
+ mProcessedRegions.clear();
+ mPendingRegions.clear();
+ }
+ }
+
+ private Bitmap createMiniBitmap(@NonNull Bitmap bitmap) {
+ Trace.beginSection("WallpaperColorExtractor#createMiniBitmap");
+ // if both sides of the image are larger than SMALL_SIDE, downscale the bitmap.
+ int smallestSide = Math.min(bitmap.getWidth(), bitmap.getHeight());
+ float scale = Math.min(1.0f, (float) SMALL_SIDE / smallestSide);
+ Bitmap result = createMiniBitmap(bitmap,
+ (int) (scale * bitmap.getWidth()),
+ (int) (scale * bitmap.getHeight()));
+ Trace.endSection();
+ return result;
+ }
+
+ @VisibleForTesting
+ Bitmap createMiniBitmap(@NonNull Bitmap bitmap, int width, int height) {
+ return Bitmap.createScaledBitmap(bitmap, width, height, false);
+ }
+
+ private WallpaperColors getLocalWallpaperColors(@NonNull RectF area) {
+ RectF imageArea = pageToImgRect(area);
+ if (imageArea == null || !LOCAL_COLOR_BOUNDS.contains(imageArea)) {
+ return null;
+ }
+ Rect subImage = new Rect(
+ (int) Math.floor(imageArea.left * mMiniBitmap.getWidth()),
+ (int) Math.floor(imageArea.top * mMiniBitmap.getHeight()),
+ (int) Math.ceil(imageArea.right * mMiniBitmap.getWidth()),
+ (int) Math.ceil(imageArea.bottom * mMiniBitmap.getHeight()));
+ if (subImage.isEmpty()) {
+ // Do not notify client. treat it as too small to sample
+ return null;
+ }
+ return getLocalWallpaperColors(subImage);
+ }
+
+ @VisibleForTesting
+ WallpaperColors getLocalWallpaperColors(@NonNull Rect subImage) {
+ Assert.isNotMainThread();
+ Bitmap colorImg = Bitmap.createBitmap(mMiniBitmap,
+ subImage.left, subImage.top, subImage.width(), subImage.height());
+ return WallpaperColors.fromBitmap(colorImg);
+ }
+
+ /**
+ * Transform the logical coordinates into wallpaper coordinates.
+ *
+ * Logical coordinates are organised such that the various pages are non-overlapping. So,
+ * if there are n pages, the first page will have its X coordinate on the range [0-1/n].
+ *
+ * The real pages are overlapping. If the Wallpaper are a width Ww and the screen a width
+ * Ws, the relative width of a page Wr is Ws/Ww. This does not change if the number of
+ * pages increase.
+ * If there are n pages, the page k starts at the offset k * (1 - Wr) / (n - 1), as the
+ * last page is at position (1-Wr) and the others are regularly spread on the range [0-
+ * (1-Wr)].
+ */
+ private RectF pageToImgRect(RectF area) {
+ // Width of a page for the caller of this API.
+ float virtualPageWidth = 1f / (float) mPages;
+ float leftPosOnPage = (area.left % virtualPageWidth) / virtualPageWidth;
+ float rightPosOnPage = (area.right % virtualPageWidth) / virtualPageWidth;
+ int currentPage = (int) Math.floor(area.centerX() / virtualPageWidth);
+
+ if (mDisplayWidth <= 0 || mDisplayHeight <= 0) {
+ Log.e(TAG, "Trying to extract colors with invalid display dimensions");
+ return null;
+ }
+
+ RectF imgArea = new RectF();
+ imgArea.bottom = area.bottom;
+ imgArea.top = area.top;
+
+ float imageScale = Math.min(((float) mBitmapHeight) / mDisplayHeight, 1);
+ float mappedScreenWidth = mDisplayWidth * imageScale;
+ float pageWidth = Math.min(1.0f,
+ mBitmapWidth > 0 ? mappedScreenWidth / (float) mBitmapWidth : 1.f);
+ float pageOffset = (1 - pageWidth) / (float) (mPages - 1);
+
+ imgArea.left = MathUtils.constrain(
+ leftPosOnPage * pageWidth + currentPage * pageOffset, 0, 1);
+ imgArea.right = MathUtils.constrain(
+ rightPosOnPage * pageWidth + currentPage * pageOffset, 0, 1);
+ if (imgArea.left > imgArea.right) {
+ // take full page
+ imgArea.left = 0;
+ imgArea.right = 1;
+ }
+ return imgArea;
+ }
+
+ /**
+ * Extract the colors from the pending regions,
+ * then notify the callback with the resulting colors for these regions
+ * This method should only be called synchronously
+ */
+ private void processColorsInternal() {
+ /*
+ * if the miniBitmap is not yet loaded, that means the onBitmapChanged has not yet been
+ * called, and thus the wallpaper is not yet loaded. In that case, exit, the function
+ * will be called again when the bitmap is loaded and the miniBitmap is computed.
+ */
+ if (mMiniBitmap == null || mMiniBitmap.isRecycled()) return;
+
+ /*
+ * if the screen size or number of pages is not yet known, exit
+ * the function will be called again once the screen size and page are known
+ */
+ if (mDisplayWidth < 0 || mDisplayHeight < 0 || mPages < 0) return;
+
+ Trace.beginSection("WallpaperColorExtractor#processColorsInternal");
+ List<WallpaperColors> processedColors = new ArrayList<>();
+ for (int i = 0; i < mPendingRegions.size(); i++) {
+ RectF nextArea = mPendingRegions.get(i);
+ WallpaperColors colors = getLocalWallpaperColors(nextArea);
+
+ mProcessedRegions.add(nextArea);
+ processedColors.add(colors);
+ }
+ List<RectF> processedRegions = new ArrayList<>(mPendingRegions);
+ mPendingRegions.clear();
+ Trace.endSection();
+
+ mWallpaperColorExtractorCallback.onColorsProcessed(processedRegions, processedColors);
+ }
+
+ /**
+ * Called to dump current state.
+ * @param prefix prefix.
+ * @param fd fd.
+ * @param out out.
+ * @param args args.
+ */
+ public void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
+ out.print(prefix); out.print("display="); out.println(mDisplayWidth + "x" + mDisplayHeight);
+ out.print(prefix); out.print("mPages="); out.println(mPages);
+
+ out.print(prefix); out.print("bitmap dimensions=");
+ out.println(mBitmapWidth + "x" + mBitmapHeight);
+
+ out.print(prefix); out.print("bitmap=");
+ out.println(mMiniBitmap == null ? "null"
+ : mMiniBitmap.isRecycled() ? "recycled"
+ : mMiniBitmap.getWidth() + "x" + mMiniBitmap.getHeight());
+
+ out.print(prefix); out.print("PendingRegions size="); out.print(mPendingRegions.size());
+ out.print(prefix); out.print("ProcessedRegions size="); out.print(mProcessedRegions.size());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 4a5b23c..d52612b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -322,6 +322,13 @@
}
@Test
+ fun testLayoutParams_hasShowWhenLockedFlag() {
+ val layoutParams = AuthContainerView.getLayoutParams(windowToken, "")
+ assertThat((layoutParams.flags and WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED) != 0)
+ .isTrue()
+ }
+
+ @Test
fun testLayoutParams_hasDimbehindWindowFlag() {
val layoutParams = AuthContainerView.getLayoutParams(windowToken, "")
val lpFlags = layoutParams.flags
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index eea2e95..7a15680 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -23,6 +23,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -33,7 +34,6 @@
import org.junit.runners.JUnit4
import org.mockito.Mock
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@SmallTest
@@ -153,6 +153,21 @@
}
@Test
+ fun `isDozing - starts with correct initial value for isDozing`() = runBlockingTest {
+ var latest: Boolean? = null
+
+ whenever(statusBarStateController.isDozing).thenReturn(true)
+ var job = underTest.isDozing.onEach { latest = it }.launchIn(this)
+ assertThat(latest).isTrue()
+ job.cancel()
+
+ whenever(statusBarStateController.isDozing).thenReturn(false)
+ job = underTest.isDozing.onEach { latest = it }.launchIn(this)
+ assertThat(latest).isFalse()
+ job.cancel()
+ }
+
+ @Test
fun dozeAmount() = runBlockingTest {
val values = mutableListOf<Float>()
val job = underTest.dozeAmount.onEach(values::add).launchIn(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
index d4fba41..329c4db 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt
@@ -138,7 +138,7 @@
assertThat(latest).isInstanceOf(KeyguardQuickAffordanceConfig.State.Visible::class.java)
val visibleState = latest as KeyguardQuickAffordanceConfig.State.Visible
assertThat(visibleState.icon).isNotNull()
- assertThat(visibleState.contentDescriptionResourceId).isNotNull()
+ assertThat(visibleState.icon.contentDescription).isNotNull()
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
index 5a3a78e..0a4478f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt
@@ -21,9 +21,11 @@
import android.service.quickaccesswallet.GetWalletCardsResponse
import android.service.quickaccesswallet.QuickAccessWalletClient
import androidx.test.filters.SmallTest
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
-import com.android.systemui.containeddrawable.ContainedDrawable
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -69,8 +71,16 @@
val job = underTest.state.onEach { latest = it }.launchIn(this)
val visibleModel = latest as KeyguardQuickAffordanceConfig.State.Visible
- assertThat(visibleModel.icon).isEqualTo(ContainedDrawable.WithDrawable(ICON))
- assertThat(visibleModel.contentDescriptionResourceId).isNotNull()
+ assertThat(visibleModel.icon)
+ .isEqualTo(
+ Icon.Loaded(
+ drawable = ICON,
+ contentDescription =
+ ContentDescription.Resource(
+ res = R.string.accessibility_wallet_button,
+ ),
+ )
+ )
job.cancel()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index c5e828e..b6d7559 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -21,7 +21,8 @@
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
-import com.android.systemui.containeddrawable.ContainedDrawable
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
@@ -34,6 +35,7 @@
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
import kotlinx.coroutines.test.runBlockingTest
import org.junit.Before
import org.junit.Test
@@ -46,7 +48,6 @@
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@SmallTest
@@ -55,7 +56,15 @@
companion object {
private val INTENT = Intent("some.intent.action")
- private val DRAWABLE = mock<ContainedDrawable>()
+ private val DRAWABLE =
+ mock<Icon> {
+ whenever(this.contentDescription)
+ .thenReturn(
+ ContentDescription.Resource(
+ res = CONTENT_DESCRIPTION_RESOURCE_ID,
+ )
+ )
+ }
private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
@Parameters(
@@ -236,7 +245,6 @@
state =
KeyguardQuickAffordanceConfig.State.Visible(
icon = DRAWABLE,
- contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
)
homeControls.onClickedResult =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorTest.kt
index 19d8412..1dd919a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/KeyguardQuickAffordanceInteractorTest.kt
@@ -19,7 +19,8 @@
import androidx.test.filters.SmallTest
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
-import com.android.systemui.containeddrawable.ContainedDrawable
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
@@ -32,6 +33,7 @@
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@@ -101,7 +103,6 @@
homeControls.setState(
KeyguardQuickAffordanceConfig.State.Visible(
icon = ICON,
- contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
)
@@ -120,8 +121,8 @@
val visibleModel = latest as KeyguardQuickAffordanceModel.Visible
assertThat(visibleModel.configKey).isEqualTo(configKey)
assertThat(visibleModel.icon).isEqualTo(ICON)
- assertThat(visibleModel.contentDescriptionResourceId)
- .isEqualTo(CONTENT_DESCRIPTION_RESOURCE_ID)
+ assertThat(visibleModel.icon.contentDescription)
+ .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
job.cancel()
}
@@ -131,7 +132,6 @@
quickAccessWallet.setState(
KeyguardQuickAffordanceConfig.State.Visible(
icon = ICON,
- contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
)
@@ -150,8 +150,8 @@
val visibleModel = latest as KeyguardQuickAffordanceModel.Visible
assertThat(visibleModel.configKey).isEqualTo(configKey)
assertThat(visibleModel.icon).isEqualTo(ICON)
- assertThat(visibleModel.contentDescriptionResourceId)
- .isEqualTo(CONTENT_DESCRIPTION_RESOURCE_ID)
+ assertThat(visibleModel.icon.contentDescription)
+ .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID))
job.cancel()
}
@@ -161,7 +161,6 @@
homeControls.setState(
KeyguardQuickAffordanceConfig.State.Visible(
icon = ICON,
- contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
)
@@ -182,7 +181,6 @@
homeControls.setState(
KeyguardQuickAffordanceConfig.State.Visible(
icon = ICON,
- contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
)
@@ -197,7 +195,14 @@
}
companion object {
- private val ICON: ContainedDrawable = mock()
+ private val ICON: Icon = mock {
+ whenever(this.contentDescription)
+ .thenReturn(
+ ContentDescription.Resource(
+ res = CONTENT_DESCRIPTION_RESOURCE_ID,
+ )
+ )
+ }
private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
}
}
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 c612091..96544e7 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
@@ -21,7 +21,7 @@
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
-import com.android.systemui.containeddrawable.ContainedDrawable
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.doze.util.BurnInHelperWrapper
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
@@ -505,7 +505,6 @@
}
KeyguardQuickAffordanceConfig.State.Visible(
icon = testConfig.icon ?: error("Icon is unexpectedly null!"),
- contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
} else {
KeyguardQuickAffordanceConfig.State.Hidden
@@ -543,7 +542,7 @@
private data class TestConfig(
val isVisible: Boolean,
val isClickable: Boolean = false,
- val icon: ContainedDrawable? = null,
+ val icon: Icon? = null,
val canShowWhileLocked: Boolean = false,
val intent: Intent? = null,
) {
@@ -555,6 +554,5 @@
companion object {
private const val DEFAULT_BURN_IN_OFFSET = 5
private const val RETURNED_BURN_IN_OFFSET = 3
- private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitorTest.kt
index f4d61c8..1531f88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitorTest.kt
@@ -290,9 +290,7 @@
.isEqualTo(NotificationUtils.logKey("0|test_pkg|0|test|0"))
assertThat(memoryUse.objectUsage.smallIcon).isEqualTo(smallIcon)
assertThat(memoryUse.objectUsage.largeIcon).isEqualTo(largeIcon)
- assertThat(memoryUse.objectUsage.extras).isEqualTo(extras)
assertThat(memoryUse.objectUsage.bigPicture).isEqualTo(bigPicture)
- assertThat(memoryUse.objectUsage.extender).isEqualTo(extender)
if (style == null) {
assertThat(memoryUse.objectUsage.style).isNull()
} else {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
index 3434376..c254358 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
@@ -16,14 +16,23 @@
package com.android.systemui.wallpapers;
+import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.mockito.hamcrest.MockitoHamcrest.intThat;
import android.app.WallpaperManager;
import android.content.Context;
@@ -31,17 +40,25 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.ColorSpace;
+import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Handler;
-import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Display;
import android.view.DisplayInfo;
+import android.view.Surface;
import android.view.SurfaceHolder;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.wallpapers.gl.ImageWallpaperRenderer;
import org.junit.Before;
@@ -56,7 +73,6 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
-@Ignore
public class ImageWallpaperTest extends SysuiTestCase {
private static final int LOW_BMP_WIDTH = 128;
private static final int LOW_BMP_HEIGHT = 128;
@@ -66,44 +82,86 @@
private static final int DISPLAY_HEIGHT = 1080;
@Mock
+ private WindowManager mWindowManager;
+ @Mock
+ private WindowMetrics mWindowMetrics;
+ @Mock
+ private DisplayManager mDisplayManager;
+ @Mock
+ private WallpaperManager mWallpaperManager;
+ @Mock
private SurfaceHolder mSurfaceHolder;
@Mock
+ private Surface mSurface;
+ @Mock
private Context mMockContext;
+
@Mock
private Bitmap mWallpaperBitmap;
+ private int mBitmapWidth = 1;
+ private int mBitmapHeight = 1;
+
@Mock
private Handler mHandler;
@Mock
private FeatureFlags mFeatureFlags;
+ FakeSystemClock mFakeSystemClock = new FakeSystemClock();
+ FakeExecutor mFakeMainExecutor = new FakeExecutor(mFakeSystemClock);
+ FakeExecutor mFakeBackgroundExecutor = new FakeExecutor(mFakeSystemClock);
+
private CountDownLatch mEventCountdown;
@Before
public void setUp() throws Exception {
allowTestableLooperAsMainThread();
MockitoAnnotations.initMocks(this);
- mEventCountdown = new CountDownLatch(1);
+ //mEventCountdown = new CountDownLatch(1);
- WallpaperManager wallpaperManager = mock(WallpaperManager.class);
+ // set up window manager
+ when(mWindowMetrics.getBounds()).thenReturn(
+ new Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
+ when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
+ when(mMockContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager);
+
+ // set up display manager
+ doNothing().when(mDisplayManager).registerDisplayListener(any(), any());
+ when(mMockContext.getSystemService(DisplayManager.class)).thenReturn(mDisplayManager);
+
+ // set up bitmap
+ when(mWallpaperBitmap.getColorSpace()).thenReturn(ColorSpace.get(ColorSpace.Named.SRGB));
+ when(mWallpaperBitmap.getConfig()).thenReturn(Bitmap.Config.ARGB_8888);
+ when(mWallpaperBitmap.getWidth()).thenReturn(mBitmapWidth);
+ when(mWallpaperBitmap.getHeight()).thenReturn(mBitmapHeight);
+
+ // set up wallpaper manager
+ when(mWallpaperManager.peekBitmapDimensions()).thenReturn(
+ new Rect(0, 0, mBitmapWidth, mBitmapHeight));
+ when(mWallpaperManager.getBitmap(false)).thenReturn(mWallpaperBitmap);
+ when(mMockContext.getSystemService(WallpaperManager.class)).thenReturn(mWallpaperManager);
+
+ // set up surface
+ when(mSurfaceHolder.getSurface()).thenReturn(mSurface);
+ doNothing().when(mSurface).hwuiDestroy();
+
+ // TODO remove code below. Outdated, used in only in old GL tests (that are ignored)
Resources resources = mock(Resources.class);
-
- when(mMockContext.getSystemService(WallpaperManager.class)).thenReturn(wallpaperManager);
- when(mMockContext.getResources()).thenReturn(resources);
when(resources.getConfiguration()).thenReturn(mock(Configuration.class));
-
+ when(mMockContext.getResources()).thenReturn(resources);
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = DISPLAY_WIDTH;
displayInfo.logicalHeight = DISPLAY_HEIGHT;
when(mMockContext.getDisplay()).thenReturn(
new Display(mock(DisplayManagerGlobal.class), 0, displayInfo, (Resources) null));
+ }
- when(wallpaperManager.getBitmap(false)).thenReturn(mWallpaperBitmap);
- when(mWallpaperBitmap.getColorSpace()).thenReturn(ColorSpace.get(ColorSpace.Named.SRGB));
- when(mWallpaperBitmap.getConfig()).thenReturn(Bitmap.Config.ARGB_8888);
+ private void setBitmapDimensions(int bitmapWidth, int bitmapHeight) {
+ mBitmapWidth = bitmapWidth;
+ mBitmapHeight = bitmapHeight;
}
private ImageWallpaper createImageWallpaper() {
- return new ImageWallpaper(mFeatureFlags) {
+ return new ImageWallpaper(mFeatureFlags, mFakeBackgroundExecutor, mFakeMainExecutor) {
@Override
public Engine onCreateEngine() {
return new GLEngine(mHandler) {
@@ -130,6 +188,7 @@
}
@Test
+ @Ignore
public void testBitmapWallpaper_normal() {
// Will use a image wallpaper with dimensions DISPLAY_WIDTH x DISPLAY_WIDTH.
// Then we expect the surface size will be also DISPLAY_WIDTH x DISPLAY_WIDTH.
@@ -140,6 +199,7 @@
}
@Test
+ @Ignore
public void testBitmapWallpaper_low_resolution() {
// Will use a image wallpaper with dimensions BMP_WIDTH x BMP_HEIGHT.
// Then we expect the surface size will be also BMP_WIDTH x BMP_HEIGHT.
@@ -150,6 +210,7 @@
}
@Test
+ @Ignore
public void testBitmapWallpaper_too_small() {
// Will use a image wallpaper with dimensions INVALID_BMP_WIDTH x INVALID_BMP_HEIGHT.
// Then we expect the surface size will be also MIN_SURFACE_WIDTH x MIN_SURFACE_HEIGHT.
@@ -166,8 +227,7 @@
ImageWallpaper.GLEngine engineSpy = spy(wallpaperEngine);
- when(mWallpaperBitmap.getWidth()).thenReturn(bmpWidth);
- when(mWallpaperBitmap.getHeight()).thenReturn(bmpHeight);
+ setBitmapDimensions(bmpWidth, bmpHeight);
ImageWallpaperRenderer renderer = new ImageWallpaperRenderer(mMockContext);
doReturn(renderer).when(engineSpy).getRendererInstance();
@@ -177,4 +237,116 @@
assertWithMessage("setFixedSizeAllowed should have been called.").that(
mEventCountdown.getCount()).isEqualTo(0);
}
+
+
+ private ImageWallpaper createImageWallpaperCanvas() {
+ return new ImageWallpaper(mFeatureFlags, mFakeBackgroundExecutor, mFakeMainExecutor) {
+ @Override
+ public Engine onCreateEngine() {
+ return new CanvasEngine() {
+ @Override
+ public Context getDisplayContext() {
+ return mMockContext;
+ }
+
+ @Override
+ public SurfaceHolder getSurfaceHolder() {
+ return mSurfaceHolder;
+ }
+
+ @Override
+ public void setFixedSizeAllowed(boolean allowed) {
+ super.setFixedSizeAllowed(allowed);
+ assertWithMessage("mFixedSizeAllowed should be true").that(
+ allowed).isTrue();
+ }
+ };
+ }
+ };
+ }
+
+ private ImageWallpaper.CanvasEngine getSpyEngine() {
+ ImageWallpaper imageWallpaper = createImageWallpaperCanvas();
+ ImageWallpaper.CanvasEngine engine =
+ (ImageWallpaper.CanvasEngine) imageWallpaper.onCreateEngine();
+ ImageWallpaper.CanvasEngine spyEngine = spy(engine);
+ doNothing().when(spyEngine).drawFrameOnCanvas(any(Bitmap.class));
+ doNothing().when(spyEngine).reportEngineShown(anyBoolean());
+ doAnswer(invocation -> {
+ ((ImageWallpaper.CanvasEngine) invocation.getMock()).onMiniBitmapUpdated();
+ return null;
+ }).when(spyEngine).recomputeColorExtractorMiniBitmap();
+ return spyEngine;
+ }
+
+ @Test
+ public void testMinSurface() {
+
+ // test that the surface is always at least MIN_SURFACE_WIDTH x MIN_SURFACE_HEIGHT
+ testMinSurfaceHelper(8, 8);
+ testMinSurfaceHelper(100, 2000);
+ testMinSurfaceHelper(200, 1);
+ testMinSurfaceHelper(0, 1);
+ testMinSurfaceHelper(1, 0);
+ testMinSurfaceHelper(0, 0);
+ }
+
+ private void testMinSurfaceHelper(int bitmapWidth, int bitmapHeight) {
+
+ clearInvocations(mSurfaceHolder);
+ setBitmapDimensions(bitmapWidth, bitmapHeight);
+
+ ImageWallpaper imageWallpaper = createImageWallpaperCanvas();
+ ImageWallpaper.CanvasEngine engine =
+ (ImageWallpaper.CanvasEngine) imageWallpaper.onCreateEngine();
+ engine.onCreate(mSurfaceHolder);
+
+ verify(mSurfaceHolder, times(1)).setFixedSize(
+ intThat(greaterThanOrEqualTo(ImageWallpaper.CanvasEngine.MIN_SURFACE_WIDTH)),
+ intThat(greaterThanOrEqualTo(ImageWallpaper.CanvasEngine.MIN_SURFACE_HEIGHT)));
+ }
+
+ @Test
+ public void testZeroBitmap() {
+ // test that a frame is never drawn with a 0 bitmap
+ testZeroBitmapHelper(0, 1);
+ testZeroBitmapHelper(1, 0);
+ testZeroBitmapHelper(0, 0);
+ }
+
+ private void testZeroBitmapHelper(int bitmapWidth, int bitmapHeight) {
+
+ clearInvocations(mSurfaceHolder);
+ setBitmapDimensions(bitmapWidth, bitmapHeight);
+
+ ImageWallpaper imageWallpaper = createImageWallpaperCanvas();
+ ImageWallpaper.CanvasEngine engine =
+ (ImageWallpaper.CanvasEngine) imageWallpaper.onCreateEngine();
+ ImageWallpaper.CanvasEngine spyEngine = spy(engine);
+ spyEngine.onCreate(mSurfaceHolder);
+ spyEngine.onSurfaceRedrawNeeded(mSurfaceHolder);
+ verify(spyEngine, never()).drawFrameOnCanvas(any());
+ }
+
+ @Test
+ public void testLoadDrawAndUnloadBitmap() {
+ setBitmapDimensions(LOW_BMP_WIDTH, LOW_BMP_HEIGHT);
+
+ ImageWallpaper.CanvasEngine spyEngine = getSpyEngine();
+ spyEngine.onCreate(mSurfaceHolder);
+ spyEngine.onSurfaceRedrawNeeded(mSurfaceHolder);
+ assertThat(mFakeBackgroundExecutor.numPending()).isAtLeast(1);
+
+ int n = 0;
+ while (mFakeBackgroundExecutor.numPending() + mFakeMainExecutor.numPending() >= 1) {
+ n++;
+ assertThat(n).isAtMost(10);
+ mFakeBackgroundExecutor.runNextReady();
+ mFakeMainExecutor.runNextReady();
+ mFakeSystemClock.advanceTime(1000);
+ }
+
+ verify(spyEngine, times(1)).drawFrameOnCanvas(mWallpaperBitmap);
+ assertThat(spyEngine.isBitmapLoaded()).isFalse();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRendererTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRendererTest.java
deleted file mode 100644
index 93f4f82..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRendererTest.java
+++ /dev/null
@@ -1,133 +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.systemui.wallpapers.canvas;
-
-import static org.hamcrest.Matchers.greaterThanOrEqualTo;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.hamcrest.MockitoHamcrest.intThat;
-
-import android.graphics.Bitmap;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.view.DisplayInfo;
-import android.view.SurfaceHolder;
-
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@SmallTest
-@RunWith(AndroidTestingRunner.class)
-@TestableLooper.RunWithLooper
-public class ImageCanvasWallpaperRendererTest extends SysuiTestCase {
-
- private static final int MOBILE_DISPLAY_WIDTH = 720;
- private static final int MOBILE_DISPLAY_HEIGHT = 1600;
-
- @Mock
- private SurfaceHolder mMockSurfaceHolder;
-
- @Mock
- private DisplayInfo mMockDisplayInfo;
-
- @Mock
- private Bitmap mMockBitmap;
-
- @Before
- public void setUp() throws Exception {
- allowTestableLooperAsMainThread();
- MockitoAnnotations.initMocks(this);
- }
-
- private void setDimensions(
- int bitmapWidth, int bitmapHeight,
- int displayWidth, int displayHeight) {
- when(mMockBitmap.getWidth()).thenReturn(bitmapWidth);
- when(mMockBitmap.getHeight()).thenReturn(bitmapHeight);
- mMockDisplayInfo.logicalWidth = displayWidth;
- mMockDisplayInfo.logicalHeight = displayHeight;
- }
-
- private void testMinDimensions(
- int bitmapWidth, int bitmapHeight) {
-
- clearInvocations(mMockSurfaceHolder);
- setDimensions(bitmapWidth, bitmapHeight,
- ImageCanvasWallpaperRendererTest.MOBILE_DISPLAY_WIDTH,
- ImageCanvasWallpaperRendererTest.MOBILE_DISPLAY_HEIGHT);
-
- ImageCanvasWallpaperRenderer renderer =
- new ImageCanvasWallpaperRenderer(mMockSurfaceHolder);
- renderer.drawFrame(mMockBitmap, true);
-
- verify(mMockSurfaceHolder, times(1)).setFixedSize(
- intThat(greaterThanOrEqualTo(ImageCanvasWallpaperRenderer.MIN_SURFACE_WIDTH)),
- intThat(greaterThanOrEqualTo(ImageCanvasWallpaperRenderer.MIN_SURFACE_HEIGHT)));
- }
-
- @Test
- public void testMinSurface() {
- // test that the surface is always at least MIN_SURFACE_WIDTH x MIN_SURFACE_HEIGHT
- testMinDimensions(8, 8);
-
- testMinDimensions(100, 2000);
-
- testMinDimensions(200, 1);
- }
-
- private void testZeroDimensions(int bitmapWidth, int bitmapHeight) {
-
- clearInvocations(mMockSurfaceHolder);
- setDimensions(bitmapWidth, bitmapHeight,
- ImageCanvasWallpaperRendererTest.MOBILE_DISPLAY_WIDTH,
- ImageCanvasWallpaperRendererTest.MOBILE_DISPLAY_HEIGHT);
-
- ImageCanvasWallpaperRenderer renderer =
- new ImageCanvasWallpaperRenderer(mMockSurfaceHolder);
- ImageCanvasWallpaperRenderer spyRenderer = spy(renderer);
- spyRenderer.drawFrame(mMockBitmap, true);
-
- verify(mMockSurfaceHolder, never()).setFixedSize(anyInt(), anyInt());
- verify(spyRenderer, never()).drawWallpaperWithCanvas(any());
- }
-
- @Test
- public void testZeroBitmap() {
- // test that updateSurfaceSize is not called with a bitmap of width 0 or height 0
- testZeroDimensions(
- 0, 1
- );
-
- testZeroDimensions(1, 0
- );
-
- testZeroDimensions(0, 0
- );
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/canvas/WallpaperColorExtractorTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/canvas/WallpaperColorExtractorTest.java
new file mode 100644
index 0000000..76bff1d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/canvas/WallpaperColorExtractorTest.java
@@ -0,0 +1,350 @@
+/*
+ * 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.systemui.wallpapers.canvas;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.WallpaperColors;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class WallpaperColorExtractorTest extends SysuiTestCase {
+ private static final int LOW_BMP_WIDTH = 128;
+ private static final int LOW_BMP_HEIGHT = 128;
+ private static final int HIGH_BMP_WIDTH = 3000;
+ private static final int HIGH_BMP_HEIGHT = 4000;
+ private static final int VERY_LOW_BMP_WIDTH = 1;
+ private static final int VERY_LOW_BMP_HEIGHT = 1;
+ private static final int DISPLAY_WIDTH = 1920;
+ private static final int DISPLAY_HEIGHT = 1080;
+
+ private static final int PAGES_LOW = 4;
+ private static final int PAGES_HIGH = 7;
+
+ private static final int MIN_AREAS = 4;
+ private static final int MAX_AREAS = 10;
+
+ private int mMiniBitmapWidth;
+ private int mMiniBitmapHeight;
+
+ @Mock
+ private Executor mBackgroundExecutor;
+
+ private int mColorsProcessed;
+ private int mMiniBitmapUpdatedCount;
+ private int mActivatedCount;
+ private int mDeactivatedCount;
+
+ @Before
+ public void setUp() throws Exception {
+ allowTestableLooperAsMainThread();
+ MockitoAnnotations.initMocks(this);
+ doAnswer(invocation -> {
+ ((Runnable) invocation.getArgument(0)).run();
+ return null;
+ }).when(mBackgroundExecutor).execute(any(Runnable.class));
+ }
+
+ private void resetCounters() {
+ mColorsProcessed = 0;
+ mMiniBitmapUpdatedCount = 0;
+ mActivatedCount = 0;
+ mDeactivatedCount = 0;
+ }
+
+ private Bitmap getMockBitmap(int width, int height) {
+ Bitmap bitmap = mock(Bitmap.class);
+ when(bitmap.getWidth()).thenReturn(width);
+ when(bitmap.getHeight()).thenReturn(height);
+ return bitmap;
+ }
+
+ private WallpaperColorExtractor getSpyWallpaperColorExtractor() {
+
+ WallpaperColorExtractor wallpaperColorExtractor = new WallpaperColorExtractor(
+ mBackgroundExecutor,
+ new WallpaperColorExtractor.WallpaperColorExtractorCallback() {
+ @Override
+ public void onColorsProcessed(List<RectF> regions,
+ List<WallpaperColors> colors) {
+ assertThat(regions.size()).isEqualTo(colors.size());
+ mColorsProcessed += regions.size();
+ }
+
+ @Override
+ public void onMiniBitmapUpdated() {
+ mMiniBitmapUpdatedCount++;
+ }
+
+ @Override
+ public void onActivated() {
+ mActivatedCount++;
+ }
+
+ @Override
+ public void onDeactivated() {
+ mDeactivatedCount++;
+ }
+ });
+ WallpaperColorExtractor spyWallpaperColorExtractor = spy(wallpaperColorExtractor);
+
+ doAnswer(invocation -> {
+ mMiniBitmapWidth = invocation.getArgument(1);
+ mMiniBitmapHeight = invocation.getArgument(2);
+ return getMockBitmap(mMiniBitmapWidth, mMiniBitmapHeight);
+ }).when(spyWallpaperColorExtractor).createMiniBitmap(any(Bitmap.class), anyInt(), anyInt());
+
+
+ doAnswer(invocation -> getMockBitmap(
+ invocation.getArgument(1),
+ invocation.getArgument(2)))
+ .when(spyWallpaperColorExtractor)
+ .createMiniBitmap(any(Bitmap.class), anyInt(), anyInt());
+
+ doReturn(new WallpaperColors(Color.valueOf(0), Color.valueOf(0), Color.valueOf(0)))
+ .when(spyWallpaperColorExtractor).getLocalWallpaperColors(any(Rect.class));
+
+ return spyWallpaperColorExtractor;
+ }
+
+ private RectF randomArea() {
+ float width = (float) Math.random();
+ float startX = (float) (Math.random() * (1 - width));
+ float height = (float) Math.random();
+ float startY = (float) (Math.random() * (1 - height));
+ return new RectF(startX, startY, startX + width, startY + height);
+ }
+
+ private List<RectF> listOfRandomAreas(int min, int max) {
+ int nAreas = randomBetween(min, max);
+ List<RectF> result = new ArrayList<>();
+ for (int i = 0; i < nAreas; i++) {
+ result.add(randomArea());
+ }
+ return result;
+ }
+
+ private int randomBetween(int minIncluded, int maxIncluded) {
+ return (int) (Math.random() * ((maxIncluded - minIncluded) + 1)) + minIncluded;
+ }
+
+ /**
+ * Test that for bitmaps of random dimensions, the mini bitmap is always created
+ * with either a width <= SMALL_SIDE or a height <= SMALL_SIDE
+ */
+ @Test
+ public void testMiniBitmapCreation() {
+ WallpaperColorExtractor spyWallpaperColorExtractor = getSpyWallpaperColorExtractor();
+ int nSimulations = 10;
+ for (int i = 0; i < nSimulations; i++) {
+ resetCounters();
+ int width = randomBetween(LOW_BMP_WIDTH, HIGH_BMP_WIDTH);
+ int height = randomBetween(LOW_BMP_HEIGHT, HIGH_BMP_HEIGHT);
+ Bitmap bitmap = getMockBitmap(width, height);
+ spyWallpaperColorExtractor.onBitmapChanged(bitmap);
+
+ assertThat(mMiniBitmapUpdatedCount).isEqualTo(1);
+ assertThat(Math.min(mMiniBitmapWidth, mMiniBitmapHeight))
+ .isAtMost(WallpaperColorExtractor.SMALL_SIDE);
+ }
+ }
+
+ /**
+ * Test that for bitmaps with both width and height <= SMALL_SIDE,
+ * the mini bitmap is always created with both width and height <= SMALL_SIDE
+ */
+ @Test
+ public void testSmallMiniBitmapCreation() {
+ WallpaperColorExtractor spyWallpaperColorExtractor = getSpyWallpaperColorExtractor();
+ int nSimulations = 10;
+ for (int i = 0; i < nSimulations; i++) {
+ resetCounters();
+ int width = randomBetween(VERY_LOW_BMP_WIDTH, LOW_BMP_WIDTH);
+ int height = randomBetween(VERY_LOW_BMP_HEIGHT, LOW_BMP_HEIGHT);
+ Bitmap bitmap = getMockBitmap(width, height);
+ spyWallpaperColorExtractor.onBitmapChanged(bitmap);
+
+ assertThat(mMiniBitmapUpdatedCount).isEqualTo(1);
+ assertThat(Math.max(mMiniBitmapWidth, mMiniBitmapHeight))
+ .isAtMost(WallpaperColorExtractor.SMALL_SIDE);
+ }
+ }
+
+ /**
+ * Test that for a new color extractor with information
+ * (number of pages, display dimensions, wallpaper bitmap) given in random order,
+ * the colors are processed and all the callbacks are properly executed.
+ */
+ @Test
+ public void testNewColorExtraction() {
+ Bitmap bitmap = getMockBitmap(HIGH_BMP_WIDTH, HIGH_BMP_HEIGHT);
+
+ int nSimulations = 10;
+ for (int i = 0; i < nSimulations; i++) {
+ resetCounters();
+ WallpaperColorExtractor spyWallpaperColorExtractor = getSpyWallpaperColorExtractor();
+ List<RectF> regions = listOfRandomAreas(MIN_AREAS, MAX_AREAS);
+ int nPages = randomBetween(PAGES_LOW, PAGES_HIGH);
+ List<Runnable> tasks = Arrays.asList(
+ () -> spyWallpaperColorExtractor.onPageChanged(nPages),
+ () -> spyWallpaperColorExtractor.onBitmapChanged(bitmap),
+ () -> spyWallpaperColorExtractor.setDisplayDimensions(
+ DISPLAY_WIDTH, DISPLAY_HEIGHT),
+ () -> spyWallpaperColorExtractor.addLocalColorsAreas(
+ regions));
+ Collections.shuffle(tasks);
+ tasks.forEach(Runnable::run);
+
+ assertThat(mActivatedCount).isEqualTo(1);
+ assertThat(mMiniBitmapUpdatedCount).isEqualTo(1);
+ assertThat(mColorsProcessed).isEqualTo(regions.size());
+
+ spyWallpaperColorExtractor.removeLocalColorAreas(regions);
+ assertThat(mDeactivatedCount).isEqualTo(1);
+ }
+ }
+
+ /**
+ * Test that the method removeLocalColorAreas behaves properly and does not call
+ * the onDeactivated callback unless all color areas are removed.
+ */
+ @Test
+ public void testRemoveColors() {
+ Bitmap bitmap = getMockBitmap(HIGH_BMP_WIDTH, HIGH_BMP_HEIGHT);
+ int nSimulations = 10;
+ for (int i = 0; i < nSimulations; i++) {
+ resetCounters();
+ WallpaperColorExtractor spyWallpaperColorExtractor = getSpyWallpaperColorExtractor();
+ List<RectF> regions1 = listOfRandomAreas(MIN_AREAS / 2, MAX_AREAS / 2);
+ List<RectF> regions2 = listOfRandomAreas(MIN_AREAS / 2, MAX_AREAS / 2);
+ List<RectF> regions = new ArrayList<>();
+ regions.addAll(regions1);
+ regions.addAll(regions2);
+ int nPages = randomBetween(PAGES_LOW, PAGES_HIGH);
+ List<Runnable> tasks = Arrays.asList(
+ () -> spyWallpaperColorExtractor.onPageChanged(nPages),
+ () -> spyWallpaperColorExtractor.onBitmapChanged(bitmap),
+ () -> spyWallpaperColorExtractor.setDisplayDimensions(
+ DISPLAY_WIDTH, DISPLAY_HEIGHT),
+ () -> spyWallpaperColorExtractor.removeLocalColorAreas(regions1));
+
+ spyWallpaperColorExtractor.addLocalColorsAreas(regions);
+ assertThat(mActivatedCount).isEqualTo(1);
+ Collections.shuffle(tasks);
+ tasks.forEach(Runnable::run);
+
+ assertThat(mMiniBitmapUpdatedCount).isEqualTo(1);
+ assertThat(mDeactivatedCount).isEqualTo(0);
+ spyWallpaperColorExtractor.removeLocalColorAreas(regions2);
+ assertThat(mDeactivatedCount).isEqualTo(1);
+ }
+ }
+
+ /**
+ * Test that if we change some information (wallpaper bitmap, number of pages),
+ * the colors are correctly recomputed.
+ * Test that if we remove some color areas in the middle of the process,
+ * only the remaining areas are recomputed.
+ */
+ @Test
+ public void testRecomputeColorExtraction() {
+ Bitmap bitmap = getMockBitmap(HIGH_BMP_WIDTH, HIGH_BMP_HEIGHT);
+ WallpaperColorExtractor spyWallpaperColorExtractor = getSpyWallpaperColorExtractor();
+ List<RectF> regions1 = listOfRandomAreas(MIN_AREAS / 2, MAX_AREAS / 2);
+ List<RectF> regions2 = listOfRandomAreas(MIN_AREAS / 2, MAX_AREAS / 2);
+ List<RectF> regions = new ArrayList<>();
+ regions.addAll(regions1);
+ regions.addAll(regions2);
+ spyWallpaperColorExtractor.addLocalColorsAreas(regions);
+ assertThat(mActivatedCount).isEqualTo(1);
+ int nPages = PAGES_LOW;
+ spyWallpaperColorExtractor.onBitmapChanged(bitmap);
+ spyWallpaperColorExtractor.onPageChanged(nPages);
+ spyWallpaperColorExtractor.setDisplayDimensions(DISPLAY_WIDTH, DISPLAY_HEIGHT);
+
+ int nSimulations = 20;
+ for (int i = 0; i < nSimulations; i++) {
+ resetCounters();
+
+ // verify that if we remove some regions, they are not recomputed after other changes
+ if (i == nSimulations / 2) {
+ regions.removeAll(regions2);
+ spyWallpaperColorExtractor.removeLocalColorAreas(regions2);
+ }
+
+ if (Math.random() >= 0.5) {
+ int nPagesNew = randomBetween(PAGES_LOW, PAGES_HIGH);
+ if (nPagesNew == nPages) continue;
+ nPages = nPagesNew;
+ spyWallpaperColorExtractor.onPageChanged(nPagesNew);
+ } else {
+ Bitmap newBitmap = getMockBitmap(HIGH_BMP_WIDTH, HIGH_BMP_HEIGHT);
+ spyWallpaperColorExtractor.onBitmapChanged(newBitmap);
+ assertThat(mMiniBitmapUpdatedCount).isEqualTo(1);
+ }
+ assertThat(mColorsProcessed).isEqualTo(regions.size());
+ }
+ spyWallpaperColorExtractor.removeLocalColorAreas(regions);
+ assertThat(mDeactivatedCount).isEqualTo(1);
+ }
+
+ @Test
+ public void testCleanUp() {
+ resetCounters();
+ Bitmap bitmap = getMockBitmap(HIGH_BMP_WIDTH, HIGH_BMP_HEIGHT);
+ doNothing().when(bitmap).recycle();
+ WallpaperColorExtractor spyWallpaperColorExtractor = getSpyWallpaperColorExtractor();
+ spyWallpaperColorExtractor.onPageChanged(PAGES_LOW);
+ spyWallpaperColorExtractor.onBitmapChanged(bitmap);
+ assertThat(mMiniBitmapUpdatedCount).isEqualTo(1);
+ spyWallpaperColorExtractor.cleanUp();
+ spyWallpaperColorExtractor.addLocalColorsAreas(listOfRandomAreas(MIN_AREAS, MAX_AREAS));
+ assertThat(mColorsProcessed).isEqualTo(0);
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 5e7d814..16fe121 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -735,6 +735,9 @@
// initialized in the constructor.
public int CUR_MAX_EMPTY_PROCESSES;
+ /** @see mEnforceReceiverExportedFlagRequirement */
+ private static final String KEY_ENFORCE_RECEIVER_EXPORTED_FLAG_REQUIREMENT =
+ "enforce_exported_flag_requirement";
/** @see #mNoKillCachedProcessesUntilBootCompleted */
private static final String KEY_NO_KILL_CACHED_PROCESSES_UNTIL_BOOT_COMPLETED =
@@ -744,6 +747,9 @@
private static final String KEY_NO_KILL_CACHED_PROCESSES_POST_BOOT_COMPLETED_DURATION_MILLIS =
"no_kill_cached_processes_post_boot_completed_duration_millis";
+ /** @see mEnforceReceiverExportedFlagRequirement */
+ private static final boolean DEFAULT_ENFORCE_RECEIVER_EXPORTED_FLAG_REQUIREMENT = false;
+
/** @see #mNoKillCachedProcessesUntilBootCompleted */
private static final boolean DEFAULT_NO_KILL_CACHED_PROCESSES_UNTIL_BOOT_COMPLETED = true;
@@ -752,6 +758,15 @@
DEFAULT_NO_KILL_CACHED_PROCESSES_POST_BOOT_COMPLETED_DURATION_MILLIS = 600_000;
/**
+ * If true, enforce the requirement that dynamically registered receivers specify one of
+ * {@link android.content.Context#RECEIVER_EXPORTED} or
+ * {@link android.content.Context#RECEIVER_NOT_EXPORTED} if registering for any non-system
+ * broadcasts.
+ */
+ volatile boolean mEnforceReceiverExportedFlagRequirement =
+ DEFAULT_ENFORCE_RECEIVER_EXPORTED_FLAG_REQUIREMENT;
+
+ /**
* If true, do not kill excessive cached processes proactively, until user-0 is unlocked.
* @see #mNoKillCachedProcessesPostBootCompletedDurationMillis
*/
@@ -1010,6 +1025,9 @@
case KEY_NO_KILL_CACHED_PROCESSES_UNTIL_BOOT_COMPLETED:
updateNoKillCachedProcessesUntilBootCompleted();
break;
+ case KEY_ENFORCE_RECEIVER_EXPORTED_FLAG_REQUIREMENT:
+ updateEnforceReceiverExportedFlagRequirement();
+ break;
case KEY_NO_KILL_CACHED_PROCESSES_POST_BOOT_COMPLETED_DURATION_MILLIS:
updateNoKillCachedProcessesPostBootCompletedDurationMillis();
break;
@@ -1482,6 +1500,13 @@
DEFAULT_DEFER_BOOT_COMPLETED_BROADCAST);
}
+ private void updateEnforceReceiverExportedFlagRequirement() {
+ mEnforceReceiverExportedFlagRequirement = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_ENFORCE_RECEIVER_EXPORTED_FLAG_REQUIREMENT,
+ DEFAULT_ENFORCE_RECEIVER_EXPORTED_FLAG_REQUIREMENT);
+ }
+
private void updateNoKillCachedProcessesUntilBootCompleted() {
mNoKillCachedProcessesUntilBootCompleted = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -1819,6 +1844,8 @@
pw.print("="); pw.println(mPrioritizeAlarmBroadcasts);
pw.print(" "); pw.print(KEY_NO_KILL_CACHED_PROCESSES_UNTIL_BOOT_COMPLETED);
pw.print("="); pw.println(mNoKillCachedProcessesUntilBootCompleted);
+ pw.print(" "); pw.print(KEY_ENFORCE_RECEIVER_EXPORTED_FLAG_REQUIREMENT);
+ pw.print("="); pw.println(mEnforceReceiverExportedFlagRequirement);
pw.print(" "); pw.print(KEY_NO_KILL_CACHED_PROCESSES_POST_BOOT_COMPLETED_DURATION_MILLIS);
pw.print("="); pw.println(mNoKillCachedProcessesPostBootCompletedDurationMillis);
pw.print(" "); pw.print(KEY_MAX_EMPTY_TIME_MILLIS);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 34e3eec..5c1031a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -215,8 +215,8 @@
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetManagerInternal;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.EnabledSince;
import android.content.AttributionSource;
import android.content.AutofillOptions;
import android.content.BroadcastReceiver;
@@ -588,7 +588,7 @@
* unprotected broadcast in code.
*/
@ChangeId
- @Disabled
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
private static final long DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED = 161145287L;
/**
@@ -2935,10 +2935,13 @@
|| event == Event.ACTIVITY_DESTROYED)) {
contentCaptureService.notifyActivityEvent(userId, activity, event);
}
- // TODO(b/201234353): Move the logic to client side.
- if (mVoiceInteractionManagerProvider != null && (event == Event.ACTIVITY_PAUSED
- || event == Event.ACTIVITY_RESUMED || event == Event.ACTIVITY_STOPPED)) {
- mVoiceInteractionManagerProvider.notifyActivityEventChanged();
+ // Currently we have move most of logic to the client side. When the activity lifecycle
+ // event changed, the client side will notify the VoiceInteractionManagerService. But
+ // when the application process died, the VoiceInteractionManagerService will miss the
+ // activity lifecycle event changed, so we still need ACTIVITY_DESTROYED event here to
+ // know if the activity has been destroyed.
+ if (mVoiceInteractionManagerProvider != null && event == Event.ACTIVITY_DESTROYED) {
+ mVoiceInteractionManagerProvider.notifyActivityDestroyed(appToken);
}
}
@@ -13440,7 +13443,8 @@
// broadcasts, or the receiver is null (a sticky broadcast). Sticky broadcasts should
// not be used generally, so we will be marking them as exported by default
final boolean requireExplicitFlagForDynamicReceivers = CompatChanges.isChangeEnabled(
- DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED, callingUid);
+ DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED, callingUid)
+ && mConstants.mEnforceReceiverExportedFlagRequirement;
if (!onlyProtectedBroadcasts) {
if (receiver == null && !explicitExportStateDefined) {
// sticky broadcast, no flag specified (flag isn't required)
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 542f381..067c35f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -68,7 +68,6 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
-import android.app.AppGlobals;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -81,7 +80,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
@@ -177,6 +175,7 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.TransferPipe;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.view.IInputMethodManager;
@@ -195,8 +194,6 @@
import com.android.server.utils.PriorityDump;
import com.android.server.wm.WindowManagerInternal;
-import com.google.android.collect.Sets;
-
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
@@ -211,7 +208,6 @@
import java.util.Locale;
import java.util.Objects;
import java.util.OptionalInt;
-import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Future;
@@ -283,7 +279,7 @@
* {@link #mPreventImeStartupUnlessTextEditor}.
*/
@NonNull
- private final Set<String> mNonPreemptibleInputMethods;
+ private final String[] mNonPreemptibleInputMethods;
@UserIdInt
private int mLastSwitchUserId;
@@ -297,12 +293,12 @@
private final SparseBooleanArray mLoggedDeniedGetInputMethodWindowVisibleHeightForUid =
new SparseBooleanArray(0);
final WindowManagerInternal mWindowManagerInternal;
+ private final ActivityManagerInternal mActivityManagerInternal;
final PackageManagerInternal mPackageManagerInternal;
final InputManagerInternal mInputManagerInternal;
final ImePlatformCompatUtils mImePlatformCompatUtils;
final InputMethodDeviceConfigs mInputMethodDeviceConfigs;
private final DisplayManagerInternal mDisplayManagerInternal;
- final boolean mHasFeature;
private final ArrayMap<String, List<InputMethodSubtype>> mAdditionalSubtypeMap =
new ArrayMap<>();
private final UserManager mUserManager;
@@ -800,7 +796,6 @@
private LocaleList mLastSystemLocales;
private boolean mAccessibilityRequestingNoSoftKeyboard;
private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
- private final IPackageManager mIPackageManager;
private final String mSlotIme;
/**
@@ -1546,11 +1541,13 @@
int change = isPackageDisappearing(curIm.getPackageName());
if (change == PACKAGE_TEMPORARY_CHANGE
|| change == PACKAGE_PERMANENT_CHANGE) {
+ final PackageManager userAwarePackageManager =
+ getPackageManagerForUser(mContext, mSettings.getCurrentUserId());
ServiceInfo si = null;
try {
- si = mIPackageManager.getServiceInfo(
- curIm.getComponent(), 0, mSettings.getCurrentUserId());
- } catch (RemoteException ex) {
+ si = userAwarePackageManager.getServiceInfo(curIm.getComponent(),
+ PackageManager.ComponentInfoFlags.of(0));
+ } catch (PackageManager.NameNotFoundException ignored) {
}
if (si == null) {
// Uh oh, current input method is no longer around!
@@ -1708,7 +1705,6 @@
}
public InputMethodManagerService(Context context) {
- mIPackageManager = AppGlobals.getPackageManager();
mContext = context;
mRes = context.getResources();
// TODO(b/196206770): Disallow I/O on this thread. Currently it's needed for loading
@@ -1722,6 +1718,7 @@
mIWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService(Context.WINDOW_SERVICE));
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
+ mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
mImePlatformCompatUtils = new ImePlatformCompatUtils();
@@ -1730,8 +1727,6 @@
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mUserManager = mContext.getSystemService(UserManager.class);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
- mHasFeature = context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_INPUT_METHODS);
mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);
@@ -1756,12 +1751,7 @@
mShowOngoingImeSwitcherForPhones = false;
mNotificationShown = false;
- int userId = 0;
- try {
- userId = ActivityManager.getService().getCurrentUser().id;
- } catch (RemoteException e) {
- Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);
- }
+ final int userId = mActivityManagerInternal.getCurrentUserId();
mLastSwitchUserId = userId;
@@ -1777,8 +1767,8 @@
mAutofillController = new AutofillSuggestionsController(this);
mPreventImeStartupUnlessTextEditor = mRes.getBoolean(
com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor);
- mNonPreemptibleInputMethods = Sets.newHashSet(mRes.getStringArray(
- com.android.internal.R.array.config_nonPreemptibleInputMethods));
+ mNonPreemptibleInputMethods = mRes.getStringArray(
+ com.android.internal.R.array.config_nonPreemptibleInputMethods);
mHwController = new HandwritingModeController(thread.getLooper(),
new InkWindowInitializer());
registerDeviceListenerAndCheckStylusSupport();
@@ -2605,23 +2595,20 @@
if (!mPreventImeStartupUnlessTextEditor) {
return false;
}
-
- final boolean imeVisibleAllowed =
- isSoftInputModeStateVisibleAllowed(unverifiedTargetSdkVersion, startInputFlags);
-
- return !(imeVisibleAllowed
- || mShowRequested
- || isNonPreemptibleImeLocked(selectedMethodId));
- }
-
- /** Return {@code true} if the given IME is non-preemptible like the tv remote service. */
- @GuardedBy("ImfLock.class")
- private boolean isNonPreemptibleImeLocked(@NonNull String selectedMethodId) {
- final InputMethodInfo imi = mMethodMap.get(selectedMethodId);
- if (imi != null) {
- return mNonPreemptibleInputMethods.contains(imi.getPackageName());
+ if (mShowRequested) {
+ return false;
}
- return false;
+ if (isSoftInputModeStateVisibleAllowed(unverifiedTargetSdkVersion, startInputFlags)) {
+ return false;
+ }
+ final InputMethodInfo imi = mMethodMap.get(selectedMethodId);
+ if (imi == null) {
+ return false;
+ }
+ if (ArrayUtils.contains(mNonPreemptibleInputMethods, imi.getPackageName())) {
+ return false;
+ }
+ return true;
}
@GuardedBy("ImfLock.class")
@@ -3211,27 +3198,30 @@
@GuardedBy("ImfLock.class")
void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) {
if (enabledMayChange) {
+ final PackageManager userAwarePackageManager = getPackageManagerForUser(mContext,
+ mSettings.getCurrentUserId());
+
List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodListLocked();
for (int i = 0; i < enabled.size(); i++) {
// We allow the user to select "disabled until used" apps, so if they
// are enabling one of those here we now need to make it enabled.
InputMethodInfo imm = enabled.get(i);
+ ApplicationInfo ai = null;
try {
- ApplicationInfo ai = mIPackageManager.getApplicationInfo(imm.getPackageName(),
- PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
- mSettings.getCurrentUserId());
- if (ai != null && ai.enabledSetting
- == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
- if (DEBUG) {
- Slog.d(TAG, "Update state(" + imm.getId()
- + "): DISABLED_UNTIL_USED -> DEFAULT");
- }
- mIPackageManager.setApplicationEnabledSetting(imm.getPackageName(),
- PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
- PackageManager.DONT_KILL_APP, mSettings.getCurrentUserId(),
- mContext.getBasePackageName());
+ ai = userAwarePackageManager.getApplicationInfo(imm.getPackageName(),
+ PackageManager.ApplicationInfoFlags.of(
+ PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS));
+ } catch (PackageManager.NameNotFoundException ignored) {
+ }
+ if (ai != null && ai.enabledSetting
+ == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+ if (DEBUG) {
+ Slog.d(TAG, "Update state(" + imm.getId()
+ + "): DISABLED_UNTIL_USED -> DEFAULT");
}
- } catch (RemoteException e) {
+ userAwarePackageManager.setApplicationEnabledSetting(imm.getPackageName(),
+ PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+ PackageManager.DONT_KILL_APP);
}
}
}
@@ -3313,7 +3303,7 @@
// setSelectedInputMethodAndSubtypeLocked().
setSelectedMethodIdLocked(id);
- if (LocalServices.getService(ActivityManagerInternal.class).isSystemReady()) {
+ if (mActivityManagerInternal.isSystemReady()) {
Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra("input_method_id", id);
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
index 2a6ae44..cb6e43c 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
@@ -17,7 +17,6 @@
package com.android.server.locksettings;
import android.security.AndroidKeyStoreMaintenance;
-import android.security.keymaster.KeymasterDefs;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import android.security.keystore2.AndroidKeyStoreLoadStoreParameter;
@@ -223,13 +222,6 @@
keyStore.setEntry(protectorKeyAlias, entry, protRollbackResistant);
Slog.i(TAG, "Using rollback-resistant key");
} catch (KeyStoreException e) {
- if (!(e.getCause() instanceof android.security.KeyStoreException)) {
- throw e;
- }
- int errorCode = ((android.security.KeyStoreException) e.getCause()).getErrorCode();
- if (errorCode != KeymasterDefs.KM_ERROR_ROLLBACK_RESISTANCE_UNAVAILABLE) {
- throw e;
- }
Slog.w(TAG, "Rollback-resistant keys unavailable. Falling back to "
+ "non-rollback-resistant key");
keyStore.setEntry(protectorKeyAlias, entry, protNonRollbackResistant);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4166364..a0748b4 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -5518,6 +5518,7 @@
switch (effectId) {
case HapticFeedbackConstants.CONTEXT_CLICK:
case HapticFeedbackConstants.GESTURE_END:
+ case HapticFeedbackConstants.ROTARY_SCROLL_TICK:
return VibrationEffect.get(VibrationEffect.EFFECT_TICK);
case HapticFeedbackConstants.TEXT_HANDLE_MOVE:
if (!mHapticTextHandleEnabled) {
@@ -5536,6 +5537,8 @@
case HapticFeedbackConstants.EDGE_RELEASE:
case HapticFeedbackConstants.CONFIRM:
case HapticFeedbackConstants.GESTURE_START:
+ case HapticFeedbackConstants.ROTARY_SCROLL_ITEM_FOCUS:
+ case HapticFeedbackConstants.ROTARY_SCROLL_LIMIT:
return VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
case HapticFeedbackConstants.LONG_PRESS:
case HapticFeedbackConstants.LONG_PRESS_POWER_BUTTON:
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 8a84860..6e93171 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -270,6 +270,8 @@
private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats
= new KernelMemoryBandwidthStats();
private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>();
+ private int[] mCpuPowerBracketMap;
+ private final CpuUsageDetails mCpuUsageDetails = new CpuUsageDetails();
public LongSparseArray<SamplingTimer> getKernelMemoryStats() {
return mKernelMemoryStats;
@@ -478,7 +480,7 @@
@GuardedBy("this")
@SuppressWarnings("GuardedBy") // errorprone false positive on getProcStateTimeCounter
@VisibleForTesting
- public void updateProcStateCpuTimesLocked(int uid, long timestampMs) {
+ public void updateProcStateCpuTimesLocked(int uid, long elapsedRealtimeMs, long uptimeMs) {
if (!initKernelSingleUidTimeReaderLocked()) {
return;
}
@@ -488,12 +490,20 @@
mNumSingleUidCpuTimeReads++;
LongArrayMultiStateCounter onBatteryCounter =
- u.getProcStateTimeCounter(timestampMs).getCounter();
+ u.getProcStateTimeCounter(elapsedRealtimeMs).getCounter();
LongArrayMultiStateCounter onBatteryScreenOffCounter =
- u.getProcStateScreenOffTimeCounter(timestampMs).getCounter();
+ u.getProcStateScreenOffTimeCounter(elapsedRealtimeMs).getCounter();
- mKernelSingleUidTimeReader.addDelta(uid, onBatteryCounter, timestampMs);
- mKernelSingleUidTimeReader.addDelta(uid, onBatteryScreenOffCounter, timestampMs);
+ if (isUsageHistoryEnabled()) {
+ LongArrayMultiStateCounter.LongArrayContainer deltaContainer =
+ getCpuTimeInFreqContainer();
+ mKernelSingleUidTimeReader.addDelta(uid, onBatteryCounter, elapsedRealtimeMs,
+ deltaContainer);
+ recordCpuUsage(uid, deltaContainer, elapsedRealtimeMs, uptimeMs);
+ } else {
+ mKernelSingleUidTimeReader.addDelta(uid, onBatteryCounter, elapsedRealtimeMs);
+ }
+ mKernelSingleUidTimeReader.addDelta(uid, onBatteryScreenOffCounter, elapsedRealtimeMs);
if (u.mChildUids != null) {
LongArrayMultiStateCounter.LongArrayContainer deltaContainer =
@@ -504,14 +514,27 @@
u.mChildUids.valueAt(j).cpuTimeInFreqCounter;
if (cpuTimeInFreqCounter != null) {
mKernelSingleUidTimeReader.addDelta(u.mChildUids.keyAt(j),
- cpuTimeInFreqCounter, timestampMs, deltaContainer);
+ cpuTimeInFreqCounter, elapsedRealtimeMs, deltaContainer);
onBatteryCounter.addCounts(deltaContainer);
+ if (isUsageHistoryEnabled()) {
+ recordCpuUsage(uid, deltaContainer, elapsedRealtimeMs, uptimeMs);
+ }
onBatteryScreenOffCounter.addCounts(deltaContainer);
}
}
}
}
+ private void recordCpuUsage(int uid, LongArrayMultiStateCounter.LongArrayContainer cpuUsage,
+ long elapsedRealtimeMs, long uptimeMs) {
+ if (!cpuUsage.combineValues(mCpuUsageDetails.cpuUsageMs, mCpuPowerBracketMap)) {
+ return;
+ }
+
+ mCpuUsageDetails.uid = uid;
+ mHistory.recordCpuUsage(elapsedRealtimeMs, uptimeMs, mCpuUsageDetails);
+ }
+
/**
* Removes kernel CPU stats for removed UIDs, in the order they were added to the
* mPendingRemovedUids queue.
@@ -557,16 +580,26 @@
continue;
}
- final long timestampMs = mClock.elapsedRealtime();
+ final long elapsedRealtimeMs = mClock.elapsedRealtime();
+ final long uptimeMs = mClock.uptimeMillis();
final LongArrayMultiStateCounter onBatteryCounter =
- u.getProcStateTimeCounter(timestampMs).getCounter();
+ u.getProcStateTimeCounter(elapsedRealtimeMs).getCounter();
final LongArrayMultiStateCounter onBatteryScreenOffCounter =
- u.getProcStateScreenOffTimeCounter(timestampMs).getCounter();
+ u.getProcStateScreenOffTimeCounter(elapsedRealtimeMs).getCounter();
if (uid == parentUid || Process.isSdkSandboxUid(uid)) {
- mKernelSingleUidTimeReader.addDelta(parentUid, onBatteryCounter, timestampMs);
+ if (isUsageHistoryEnabled()) {
+ LongArrayMultiStateCounter.LongArrayContainer deltaContainer =
+ getCpuTimeInFreqContainer();
+ mKernelSingleUidTimeReader.addDelta(parentUid, onBatteryCounter,
+ elapsedRealtimeMs, deltaContainer);
+ recordCpuUsage(parentUid, deltaContainer, elapsedRealtimeMs, uptimeMs);
+ } else {
+ mKernelSingleUidTimeReader.addDelta(parentUid, onBatteryCounter,
+ elapsedRealtimeMs);
+ }
mKernelSingleUidTimeReader.addDelta(parentUid, onBatteryScreenOffCounter,
- timestampMs);
+ elapsedRealtimeMs);
} else {
Uid.ChildUid childUid = u.getChildUid(uid);
if (childUid != null) {
@@ -574,9 +607,12 @@
if (counter != null) {
final LongArrayMultiStateCounter.LongArrayContainer deltaContainer =
getCpuTimeInFreqContainer();
- mKernelSingleUidTimeReader.addDelta(uid, counter, timestampMs,
+ mKernelSingleUidTimeReader.addDelta(uid, counter, elapsedRealtimeMs,
deltaContainer);
onBatteryCounter.addCounts(deltaContainer);
+ if (isUsageHistoryEnabled()) {
+ recordCpuUsage(uid, deltaContainer, elapsedRealtimeMs, uptimeMs);
+ }
onBatteryScreenOffCounter.addCounts(deltaContainer);
}
}
@@ -585,17 +621,6 @@
}
}
- @VisibleForTesting
- public static long[] addCpuTimes(long[] timesA, long[] timesB) {
- if (timesA != null && timesB != null) {
- for (int i = timesA.length - 1; i >= 0; --i) {
- timesA[i] += timesB[i];
- }
- return timesA;
- }
- return timesA == null ? (timesB == null ? null : timesB) : timesA;
- }
-
@GuardedBy("this")
private boolean initKernelSingleUidTimeReaderLocked() {
if (mKernelSingleUidTimeReader == null) {
@@ -7422,8 +7447,10 @@
@GuardedBy("this")
public void recordMeasuredEnergyDetailsLocked(long elapsedRealtimeMs,
long uptimeMs, MeasuredEnergyDetails measuredEnergyDetails) {
- mHistory.recordMeasuredEnergyDetails(elapsedRealtimeMs, uptimeMs,
- measuredEnergyDetails);
+ if (isUsageHistoryEnabled()) {
+ mHistory.recordMeasuredEnergyDetails(elapsedRealtimeMs, uptimeMs,
+ measuredEnergyDetails);
+ }
}
@GuardedBy("this")
@@ -10285,7 +10312,7 @@
}
if (mBsi.trackPerProcStateCpuTimes()) {
- mBsi.updateProcStateCpuTimesLocked(mUid, elapsedRealtimeMs);
+ mBsi.updateProcStateCpuTimesLocked(mUid, elapsedRealtimeMs, uptimeMs);
LongArrayMultiStateCounter onBatteryCounter =
getProcStateTimeCounter(elapsedRealtimeMs).getCounter();
@@ -10784,6 +10811,10 @@
mBatteryLevel = 0;
}
+ /**
+ * Injects a power profile.
+ */
+ @GuardedBy("this")
public void setPowerProfileLocked(PowerProfile profile) {
mPowerProfile = profile;
@@ -10800,6 +10831,27 @@
firstCpuOfCluster += mPowerProfile.getNumCoresInCpuCluster(i);
}
+ // Initialize CPU power bracket map, which combines CPU states (cluster/freq pairs)
+ // into a small number of brackets
+ mCpuPowerBracketMap = new int[getCpuFreqCount()];
+ int index = 0;
+ int numCpuClusters = mPowerProfile.getNumCpuClusters();
+ for (int cluster = 0; cluster < numCpuClusters; cluster++) {
+ int steps = mPowerProfile.getNumSpeedStepsInCpuCluster(cluster);
+ for (int step = 0; step < steps; step++) {
+ mCpuPowerBracketMap[index++] =
+ mPowerProfile.getPowerBracketForCpuCore(cluster, step);
+ }
+ }
+
+ int cpuPowerBracketCount = mPowerProfile.getCpuPowerBracketCount();
+ mCpuUsageDetails.cpuBracketDescriptions = new String[cpuPowerBracketCount];
+ mCpuUsageDetails.cpuUsageMs = new long[cpuPowerBracketCount];
+ for (int i = 0; i < cpuPowerBracketCount; i++) {
+ mCpuUsageDetails.cpuBracketDescriptions[i] =
+ mPowerProfile.getCpuPowerBracketDescription(i);
+ }
+
if (mEstimatedBatteryCapacityMah == -1) {
// Initialize the estimated battery capacity to a known preset one.
mEstimatedBatteryCapacityMah = (int) mPowerProfile.getBatteryCapacity();
@@ -14630,11 +14682,16 @@
}
@GuardedBy("this")
- public boolean trackPerProcStateCpuTimes() {
+ private boolean trackPerProcStateCpuTimes() {
return mCpuUidFreqTimeReader.isFastCpuTimesReader();
}
@GuardedBy("this")
+ private boolean isUsageHistoryEnabled() {
+ return false;
+ }
+
+ @GuardedBy("this")
public void systemServicesReady(Context context) {
mConstants.startObserving(context.getContentResolver());
registerUsbStateReceiver(context);
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorService.java b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
index 4186330..39672b8 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorService.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorService.java
@@ -88,8 +88,10 @@
new TimeDetectorInternalImpl(context, handler, timeDetectorStrategy);
publishLocalService(TimeDetectorInternal.class, internal);
+ CallerIdentityInjector callerIdentityInjector = CallerIdentityInjector.REAL;
TimeDetectorService service = new TimeDetectorService(
- context, handler, serviceConfigAccessor, timeDetectorStrategy);
+ context, handler, callerIdentityInjector, serviceConfigAccessor,
+ timeDetectorStrategy, NtpTrustedTime.getInstance(context));
// Publish the binder service so it can be accessed from other (appropriately
// permissioned) processes.
@@ -114,23 +116,15 @@
@VisibleForTesting
public TimeDetectorService(@NonNull Context context, @NonNull Handler handler,
- @NonNull ServiceConfigAccessor serviceConfigAccessor,
- @NonNull TimeDetectorStrategy timeDetectorStrategy) {
- this(context, handler, serviceConfigAccessor, timeDetectorStrategy,
- CallerIdentityInjector.REAL, NtpTrustedTime.getInstance(context));
- }
-
- @VisibleForTesting
- public TimeDetectorService(@NonNull Context context, @NonNull Handler handler,
+ @NonNull CallerIdentityInjector callerIdentityInjector,
@NonNull ServiceConfigAccessor serviceConfigAccessor,
@NonNull TimeDetectorStrategy timeDetectorStrategy,
- @NonNull CallerIdentityInjector callerIdentityInjector,
@NonNull NtpTrustedTime ntpTrustedTime) {
mContext = Objects.requireNonNull(context);
mHandler = Objects.requireNonNull(handler);
+ mCallerIdentityInjector = Objects.requireNonNull(callerIdentityInjector);
mServiceConfigAccessor = Objects.requireNonNull(serviceConfigAccessor);
mTimeDetectorStrategy = Objects.requireNonNull(timeDetectorStrategy);
- mCallerIdentityInjector = Objects.requireNonNull(callerIdentityInjector);
mNtpTrustedTime = Objects.requireNonNull(ntpTrustedTime);
// Wire up a change listener so that ITimeZoneDetectorListeners can be notified when
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
index 510896d..c3f05cc 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategyImpl.java
@@ -300,6 +300,7 @@
}
@Override
+ @NonNull
public synchronized TimeState getTimeState() {
boolean userShouldConfirmTime = mEnvironment.systemClockConfidence() < TIME_CONFIDENCE_HIGH;
UnixEpochTime unixEpochTime = new UnixEpochTime(
@@ -330,37 +331,32 @@
public synchronized boolean confirmTime(@NonNull UnixEpochTime confirmationTime) {
Objects.requireNonNull(confirmationTime);
- @TimeConfidence int newTimeConfidence = TIME_CONFIDENCE_HIGH;
- @TimeConfidence int currentTimeConfidence = mEnvironment.systemClockConfidence();
- boolean timeNeedsConfirmation = currentTimeConfidence < newTimeConfidence;
-
// All system clock calculation take place under a wake lock.
mEnvironment.acquireWakeLock();
try {
// Check if the specified time matches the current system clock time (closely
// enough) to raise the confidence.
- long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
- long actualSystemClockMillis = mEnvironment.systemClockMillis();
- long adjustedAutoDetectedUnixEpochMillis =
- confirmationTime.at(elapsedRealtimeMillis).getUnixEpochTimeMillis();
- long absTimeDifferenceMillis =
- Math.abs(adjustedAutoDetectedUnixEpochMillis - actualSystemClockMillis);
- int confidenceUpgradeThresholdMillis =
- mCurrentConfigurationInternal.getSystemClockConfidenceThresholdMillis();
- boolean timeConfirmed = absTimeDifferenceMillis <= confidenceUpgradeThresholdMillis;
- boolean updateConfidenceRequired = timeNeedsConfirmation && timeConfirmed;
- if (updateConfidenceRequired) {
- String logMsg = "Confirm system clock time."
- + " confirmationTime=" + confirmationTime
- + " newTimeConfidence=" + newTimeConfidence
- + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
- + " (old) actualSystemClockMillis=" + actualSystemClockMillis
- + " currentTimeConfidence=" + currentTimeConfidence;
- if (DBG) {
- Slog.d(LOG_TAG, logMsg);
- }
+ long currentElapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
+ long currentSystemClockMillis = mEnvironment.systemClockMillis();
+ boolean timeConfirmed = isTimeWithinConfidenceThreshold(
+ confirmationTime, currentElapsedRealtimeMillis, currentSystemClockMillis);
+ if (timeConfirmed) {
+ @TimeConfidence int newTimeConfidence = TIME_CONFIDENCE_HIGH;
+ @TimeConfidence int currentTimeConfidence = mEnvironment.systemClockConfidence();
+ boolean confidenceUpgradeRequired = currentTimeConfidence < newTimeConfidence;
+ if (confidenceUpgradeRequired) {
+ String logMsg = "Confirm system clock time."
+ + " confirmationTime=" + confirmationTime
+ + " newTimeConfidence=" + newTimeConfidence
+ + " currentElapsedRealtimeMillis=" + currentElapsedRealtimeMillis
+ + " currentSystemClockMillis=" + currentSystemClockMillis
+ + " (old) currentTimeConfidence=" + currentTimeConfidence;
+ if (DBG) {
+ Slog.d(LOG_TAG, logMsg);
+ }
- mEnvironment.setSystemClockConfidence(newTimeConfidence, logMsg);
+ mEnvironment.setSystemClockConfidence(newTimeConfidence, logMsg);
+ }
}
return timeConfirmed;
} finally {
@@ -470,8 +466,7 @@
}
@GuardedBy("this")
- private boolean storeTelephonySuggestion(
- @NonNull TelephonyTimeSuggestion suggestion) {
+ private boolean storeTelephonySuggestion(@NonNull TelephonyTimeSuggestion suggestion) {
UnixEpochTime newUnixEpochTime = suggestion.getUnixEpochTime();
int slotIndex = suggestion.getSlotIndex();
@@ -712,7 +707,8 @@
}
private static int scoreTelephonySuggestion(
- long elapsedRealtimeMillis, @NonNull TelephonyTimeSuggestion timeSuggestion) {
+ @ElapsedRealtimeLong long elapsedRealtimeMillis,
+ @NonNull TelephonyTimeSuggestion timeSuggestion) {
// Validate first.
UnixEpochTime unixEpochTime = timeSuggestion.getUnixEpochTime();
@@ -845,10 +841,12 @@
@GuardedBy("this")
private void upgradeSystemClockConfidenceIfRequired(
@NonNull UnixEpochTime autoDetectedUnixEpochTime, @NonNull String cause) {
+
+ // Fast path: No need to upgrade confidence if confidence is already high.
@TimeConfidence int newTimeConfidence = TIME_CONFIDENCE_HIGH;
@TimeConfidence int currentTimeConfidence = mEnvironment.systemClockConfidence();
- boolean timeNeedsConfirmation = currentTimeConfidence < newTimeConfidence;
- if (!timeNeedsConfirmation) {
+ boolean confidenceUpgradeRequired = currentTimeConfidence < newTimeConfidence;
+ if (!confidenceUpgradeRequired) {
return;
}
@@ -857,23 +855,18 @@
try {
// Check if the specified time matches the current system clock time (closely
// enough) to raise the confidence.
- long elapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
- long actualSystemClockMillis = mEnvironment.systemClockMillis();
- long adjustedAutoDetectedUnixEpochMillis =
- autoDetectedUnixEpochTime.at(elapsedRealtimeMillis).getUnixEpochTimeMillis();
- long absTimeDifferenceMillis =
- Math.abs(adjustedAutoDetectedUnixEpochMillis - actualSystemClockMillis);
- int confidenceUpgradeThresholdMillis =
- mCurrentConfigurationInternal.getSystemClockConfidenceThresholdMillis();
- boolean updateConfidenceRequired =
- absTimeDifferenceMillis <= confidenceUpgradeThresholdMillis;
+ long currentElapsedRealtimeMillis = mEnvironment.elapsedRealtimeMillis();
+ long currentSystemClockMillis = mEnvironment.systemClockMillis();
+ boolean updateConfidenceRequired = isTimeWithinConfidenceThreshold(
+ autoDetectedUnixEpochTime, currentElapsedRealtimeMillis,
+ currentSystemClockMillis);
if (updateConfidenceRequired) {
String logMsg = "Upgrade system clock confidence."
+ " autoDetectedUnixEpochTime=" + autoDetectedUnixEpochTime
+ " newTimeConfidence=" + newTimeConfidence
+ " cause=" + cause
- + " elapsedRealtimeMillis=" + elapsedRealtimeMillis
- + " (old) actualSystemClockMillis=" + actualSystemClockMillis
+ + " currentElapsedRealtimeMillis=" + currentElapsedRealtimeMillis
+ + " currentSystemClockMillis=" + currentSystemClockMillis
+ " currentTimeConfidence=" + currentTimeConfidence;
if (DBG) {
Slog.d(LOG_TAG, logMsg);
@@ -891,6 +884,19 @@
}
@GuardedBy("this")
+ private boolean isTimeWithinConfidenceThreshold(@NonNull UnixEpochTime timeToCheck,
+ @ElapsedRealtimeLong long currentElapsedRealtimeMillis,
+ @CurrentTimeMillisLong long currentSystemClockMillis) {
+ long adjustedAutoDetectedUnixEpochMillis =
+ timeToCheck.at(currentElapsedRealtimeMillis).getUnixEpochTimeMillis();
+ long absTimeDifferenceMillis =
+ Math.abs(adjustedAutoDetectedUnixEpochMillis - currentSystemClockMillis);
+ int confidenceUpgradeThresholdMillis =
+ mCurrentConfigurationInternal.getSystemClockConfidenceThresholdMillis();
+ return absTimeDifferenceMillis <= confidenceUpgradeThresholdMillis;
+ }
+
+ @GuardedBy("this")
private boolean setSystemClockAndConfidenceUnderWakeLock(
@Origin int origin, @NonNull UnixEpochTime newTime,
@TimeConfidence int newTimeConfidence, @NonNull String cause) {
@@ -1066,7 +1072,8 @@
}
private static boolean validateSuggestionUnixEpochTime(
- long currentElapsedRealtimeMillis, UnixEpochTime unixEpochTime) {
+ @ElapsedRealtimeLong long currentElapsedRealtimeMillis,
+ @NonNull UnixEpochTime unixEpochTime) {
long suggestionElapsedRealtimeMillis = unixEpochTime.getElapsedRealtimeMillis();
if (suggestionElapsedRealtimeMillis > currentElapsedRealtimeMillis) {
// Future elapsed realtimes are ignored. They imply the elapsed realtime was wrong, or
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index 0e58d91..822cd41 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -102,8 +102,10 @@
// Publish the binder service so it can be accessed from other (appropriately
// permissioned) processes.
- TimeZoneDetectorService service = TimeZoneDetectorService.create(
- context, handler, serviceConfigAccessor, timeZoneDetectorStrategy);
+ CallerIdentityInjector callerIdentityInjector = CallerIdentityInjector.REAL;
+ TimeZoneDetectorService service = new TimeZoneDetectorService(
+ context, handler, callerIdentityInjector, serviceConfigAccessor,
+ timeZoneDetectorStrategy);
// Dump the device activity monitor when the service is dumped.
service.addDumpable(deviceActivityMonitor);
@@ -142,16 +144,6 @@
@GuardedBy("mDumpables")
private final List<Dumpable> mDumpables = new ArrayList<>();
- private static TimeZoneDetectorService create(
- @NonNull Context context, @NonNull Handler handler,
- @NonNull ServiceConfigAccessor serviceConfigAccessor,
- @NonNull TimeZoneDetectorStrategy timeZoneDetectorStrategy) {
-
- CallerIdentityInjector callerIdentityInjector = CallerIdentityInjector.REAL;
- return new TimeZoneDetectorService(context, handler, callerIdentityInjector,
- serviceConfigAccessor, timeZoneDetectorStrategy);
- }
-
@VisibleForTesting
public TimeZoneDetectorService(@NonNull Context context, @NonNull Handler handler,
@NonNull CallerIdentityInjector callerIdentityInjector,
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 886e8e6..99950a0 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -687,7 +687,7 @@
*/
public void lockUser(int userId) {
mLockPatternUtils.requireStrongAuth(
- StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED, userId);
+ StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, userId);
try {
WindowManagerGlobal.getWindowManagerService().lockNow(null);
} catch (RemoteException e) {
@@ -2083,7 +2083,7 @@
if (mStrongAuthTracker.isTrustAllowedForUser(mUserId)) {
if (DEBUG) Slog.d(TAG, "Revoking all trust because of trust timeout");
mLockPatternUtils.requireStrongAuth(
- mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED, mUserId);
+ mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, mUserId);
}
maybeLockScreen(mUserId);
}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 7bc62ec..59f37c2 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -27,6 +27,8 @@
import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE;
@@ -707,7 +709,26 @@
try {
synchronized (mGlobalLock) {
final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
- return r != null && r.setOccludesParent(true);
+ // Create a transition if the activity is playing in case the below activity didn't
+ // commit invisible. That's because if any activity below this one has changed its
+ // visibility while playing transition, there won't able to commit visibility until
+ // the running transition finish.
+ final Transition transition = r != null
+ && r.mTransitionController.inPlayingTransition(r)
+ ? r.mTransitionController.createTransition(TRANSIT_TO_BACK) : null;
+ if (transition != null) {
+ r.mTransitionController.requestStartTransition(transition, null /*startTask */,
+ null /* remoteTransition */, null /* displayChange */);
+ }
+ final boolean changed = r != null && r.setOccludesParent(true);
+ if (transition != null) {
+ if (changed) {
+ r.mTransitionController.setReady(r.getDisplayContent());
+ } else {
+ transition.abort();
+ }
+ }
+ return changed;
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -728,7 +749,25 @@
if (under != null) {
under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
}
- return r.setOccludesParent(false);
+ // Create a transition if the activity is playing in case the current activity
+ // didn't commit invisible. That's because if this activity has changed its
+ // visibility while playing transition, there won't able to commit visibility until
+ // the running transition finish.
+ final Transition transition = r.mTransitionController.inPlayingTransition(r)
+ ? r.mTransitionController.createTransition(TRANSIT_TO_FRONT) : null;
+ if (transition != null) {
+ r.mTransitionController.requestStartTransition(transition, null /*startTask */,
+ null /* remoteTransition */, null /* displayChange */);
+ }
+ final boolean changed = r.setOccludesParent(false);
+ if (transition != null) {
+ if (changed) {
+ r.mTransitionController.setReady(r.getDisplayContent());
+ } else {
+ transition.abort();
+ }
+ }
+ return changed;
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9ed77fc..c317be7 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -120,6 +120,8 @@
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_OLD_UNSET;
+import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
+import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
@@ -657,7 +659,7 @@
private final WindowState.UpdateReportedVisibilityResults mReportedVisibilityResults =
new WindowState.UpdateReportedVisibilityResults();
- boolean mUseTransferredAnimation;
+ int mTransitionChangeFlags;
/** Whether we need to setup the animation to animate only within the letterbox. */
private boolean mNeedsLetterboxedAnimation;
@@ -4384,10 +4386,10 @@
// When transferring an animation, we no longer need to apply an animation to
// the token we transfer the animation over. Thus, set this flag to indicate
// we've transferred the animation.
- mUseTransferredAnimation = true;
+ mTransitionChangeFlags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
} else if (mTransitionController.getTransitionPlayer() != null) {
// In the new transit system, just set this every time we transfer the window
- mUseTransferredAnimation = true;
+ mTransitionChangeFlags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
}
// Post cleanup after the visibility and animation are transferred.
fromActivity.postWindowRemoveStartingWindowCleanup(tStartingWindow);
@@ -5236,6 +5238,10 @@
// If in a transition, defer commits for activities that are going invisible
if (!visible && inTransition()) {
+ if (mTransitionController.inPlayingTransition(this)
+ && mTransitionController.isCollecting(this)) {
+ mTransitionChangeFlags |= FLAG_IS_OCCLUDED;
+ }
return;
}
// If we are preparing an app transition, then delay changing
@@ -5286,7 +5292,7 @@
@Override
boolean applyAnimation(LayoutParams lp, @TransitionOldType int transit, boolean enter,
boolean isVoiceInteraction, @Nullable ArrayList<WindowContainer> sources) {
- if (mUseTransferredAnimation) {
+ if ((mTransitionChangeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
return false;
}
// If it was set to true, reset the last request to force the transition.
@@ -5359,7 +5365,7 @@
mWmService.mWindowPlacerLocked.performSurfacePlacement();
}
displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
- mUseTransferredAnimation = false;
+ mTransitionChangeFlags = 0;
postApplyAnimation(visible, fromTransition);
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 93d9b0e..cb29e3f 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -49,7 +49,6 @@
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
-import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static android.window.TransitionInfo.FLAG_WILL_IME_SHOWN;
@@ -1297,7 +1296,7 @@
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" sibling is a participant with mode %s",
TransitionInfo.modeToString(siblingMode));
- if (mode != siblingMode) {
+ if (reduceMode(mode) != reduceMode(siblingMode)) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
" SKIP: common mode mismatch. was %s",
TransitionInfo.modeToString(mode));
@@ -1307,6 +1306,16 @@
return true;
}
+ /** "reduces" a mode into a smaller set of modes that uniquely represents visibility change. */
+ @TransitionInfo.TransitionMode
+ private static int reduceMode(@TransitionInfo.TransitionMode int mode) {
+ switch (mode) {
+ case TRANSIT_TO_BACK: return TRANSIT_CLOSE;
+ case TRANSIT_TO_FRONT: return TRANSIT_OPEN;
+ default: return mode;
+ }
+ }
+
/**
* Go through topTargets and try to promote (see {@link #canPromote}) one of them.
*
@@ -1808,7 +1817,7 @@
@TransitionInfo.TransitionMode
int getTransitMode(@NonNull WindowContainer wc) {
if ((mFlags & ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH) != 0) {
- return TRANSIT_CLOSE;
+ return mExistenceChanged ? TRANSIT_CLOSE : TRANSIT_TO_BACK;
}
final boolean nowVisible = wc.isVisibleRequested();
if (nowVisible == mVisible) {
@@ -1845,12 +1854,10 @@
final ActivityRecord record = wc.asActivityRecord();
if (record != null) {
parentTask = record.getTask();
- if (record.mUseTransferredAnimation) {
- flags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
- }
if (record.mVoiceInteraction) {
flags |= FLAG_IS_VOICE_INTERACTION;
}
+ flags |= record.mTransitionChangeFlags;
}
final TaskFragment taskFragment = wc.asTaskFragment();
if (taskFragment != null && task == null) {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 19df5a2..57f5777 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -306,6 +306,18 @@
}
@Test
+ public void setServiceInfo_ChangeAccessibilityTool_updateFails() {
+ assertFalse(mSpyServiceInfo.isAccessibilityTool());
+
+ final AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo();
+ serviceInfo.setAccessibilityTool(true);
+ mServiceConnection.setServiceInfo(serviceInfo);
+
+ // isAccessibilityTool should not be dynamically updatable
+ assertFalse(mSpyServiceInfo.isAccessibilityTool());
+ }
+
+ @Test
public void canReceiveEvents_hasEventType_returnTrue() {
final AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo();
updateServiceInfo(serviceInfo,
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index 4100ff1..39b0770 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.os.BatteryManager;
import android.os.BatteryStats;
+import android.os.BatteryStats.CpuUsageDetails;
import android.os.BatteryStats.HistoryItem;
import android.os.BatteryStats.MeasuredEnergyDetails;
import android.os.Parcel;
@@ -315,8 +316,7 @@
String dump = toString(item, /* checkin */ false);
assertThat(dump).contains("+200ms");
- assertThat(dump).contains("ext=E");
- assertThat(dump).contains("Energy: A=0 B/0=100 B/1=200");
+ assertThat(dump).contains("ext=energy:A=0 B/0=100 B/1=200");
assertThat(dump).doesNotContain("C=");
String checkin = toString(item, /* checkin */ true);
@@ -325,6 +325,55 @@
assertThat(checkin).doesNotContain("C=");
}
+ @Test
+ public void cpuUsageDetails() {
+ mHistory.forceRecordAllHistory();
+ mHistory.startRecordingHistory(0, 0, /* reset */ true);
+ mHistory.setBatteryState(true /* charging */, BatteryManager.BATTERY_STATUS_CHARGING, 80,
+ 1234);
+
+ CpuUsageDetails details = new CpuUsageDetails();
+ details.cpuBracketDescriptions = new String[] {"low", "Med", "HIGH"};
+ details.uid = 10123;
+ details.cpuUsageMs = new long[] { 100, 200, 300};
+ mHistory.recordCpuUsage(200, 200, details);
+
+ details.uid = 10321;
+ details.cpuUsageMs = new long[] { 400, 500, 600};
+ mHistory.recordCpuUsage(300, 300, details);
+
+ BatteryStatsHistoryIterator iterator = mHistory.iterate();
+ BatteryStats.HistoryItem item = new BatteryStats.HistoryItem();
+ assertThat(iterator.next(item)).isTrue(); // First item contains current time only
+
+ assertThat(iterator.next(item)).isTrue();
+
+ String dump = toString(item, /* checkin */ false);
+ assertThat(dump).contains("+200ms");
+ assertThat(dump).contains("ext=cpu:u0a123: 100, 200, 300");
+ assertThat(dump).contains("ext=cpu-bracket:0:low");
+ assertThat(dump).contains("ext=cpu-bracket:1:Med");
+ assertThat(dump).contains("ext=cpu-bracket:2:HIGH");
+
+ String checkin = toString(item, /* checkin */ true);
+ assertThat(checkin).contains("XB,3,0,low");
+ assertThat(checkin).contains("XB,3,1,Med");
+ assertThat(checkin).contains("XB,3,2,HIGH");
+ assertThat(checkin).contains("XC,10123,100,200,300");
+
+ assertThat(iterator.next(item)).isTrue();
+
+ dump = toString(item, /* checkin */ false);
+ assertThat(dump).contains("+300ms");
+ assertThat(dump).contains("ext=cpu:u0a321: 400, 500, 600");
+ // Power bracket descriptions are written only once
+ assertThat(dump).doesNotContain("ext=cpu-bracket");
+
+ checkin = toString(item, /* checkin */ true);
+ assertThat(checkin).doesNotContain("XB");
+ assertThat(checkin).contains("XC,10321,400,500,600");
+ }
+
private String toString(BatteryStats.HistoryItem item, boolean checkin) {
BatteryStats.HistoryPrinter printer = new BatteryStats.HistoryPrinter();
StringWriter writer = new StringWriter();
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java
index 5403269..c6a7fbc 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsImplTest.java
@@ -28,7 +28,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -117,6 +116,7 @@
// Initialize time-in-freq counters
mMockClock.realtime = 1000;
+ mMockClock.uptime = 1000;
for (int i = 0; i < testUids.length; ++i) {
mockKernelSingleUidTimeReader(testUids[i], new long[5]);
mBatteryStatsImpl.noteUidProcessStateLocked(testUids[i], activityManagerProcStates[i]);
@@ -142,11 +142,12 @@
};
mMockClock.realtime += 1000;
+ mMockClock.uptime += 1000;
for (int i = 0; i < testUids.length; ++i) {
mockKernelSingleUidTimeReader(testUids[i], cpuTimes[i]);
mBatteryStatsImpl.updateProcStateCpuTimesLocked(testUids[i],
- mMockClock.realtime);
+ mMockClock.realtime, mMockClock.uptime);
}
for (int i = 0; i < testUids.length; ++i) {
@@ -171,6 +172,7 @@
};
mMockClock.realtime += 1000;
+ mMockClock.uptime += 1000;
for (int i = 0; i < testUids.length; ++i) {
long[] newCpuTimes = new long[cpuTimes[i].length];
@@ -179,7 +181,7 @@
}
mockKernelSingleUidTimeReader(testUids[i], newCpuTimes);
mBatteryStatsImpl.updateProcStateCpuTimesLocked(testUids[i],
- mMockClock.realtime);
+ mMockClock.realtime, mMockClock.uptime);
}
for (int i = 0; i < testUids.length; ++i) {
@@ -200,7 +202,7 @@
}
// Validate the on-battery-screen-off counter
- mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, 0,
+ mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_OFF, mMockClock.uptime * 1000,
mMockClock.realtime * 1000);
final long[][] delta2 = {
@@ -211,6 +213,7 @@
};
mMockClock.realtime += 1000;
+ mMockClock.uptime += 1000;
for (int i = 0; i < testUids.length; ++i) {
long[] newCpuTimes = new long[cpuTimes[i].length];
@@ -219,7 +222,7 @@
}
mockKernelSingleUidTimeReader(testUids[i], newCpuTimes);
mBatteryStatsImpl.updateProcStateCpuTimesLocked(testUids[i],
- mMockClock.realtime);
+ mMockClock.realtime, mMockClock.uptime);
}
for (int i = 0; i < testUids.length; ++i) {
@@ -253,6 +256,7 @@
};
mMockClock.realtime += 1000;
+ mMockClock.uptime += 1000;
final int parentUid = testUids[1];
final int childUid = 99099;
@@ -267,7 +271,7 @@
}
mockKernelSingleUidTimeReader(testUids[i], newCpuTimes);
mBatteryStatsImpl.updateProcStateCpuTimesLocked(testUids[i],
- mMockClock.realtime);
+ mMockClock.realtime, mMockClock.uptime);
}
for (int i = 0; i < testUids.length; ++i) {
@@ -302,6 +306,7 @@
mBatteryStatsImpl.updateTimeBasesLocked(true, Display.STATE_ON, 0, 0);
mMockClock.realtime = 1000;
+ mMockClock.uptime = 1000;
final int[] testUids = {10032, 10048, 10145, 10139};
final int[] testProcStates = {
@@ -316,7 +321,7 @@
uid.setProcessStateForTest(testProcStates[i], mMockClock.elapsedRealtime());
mockKernelSingleUidTimeReader(testUids[i], new long[NUM_CPU_FREQS]);
mBatteryStatsImpl.updateProcStateCpuTimesLocked(testUids[i],
- mMockClock.elapsedRealtime());
+ mMockClock.elapsedRealtime(), mMockClock.uptime);
}
final SparseArray<long[]> allUidCpuTimes = new SparseArray<>();
@@ -341,6 +346,7 @@
}
mMockClock.realtime += 1000;
+ mMockClock.uptime += 1000;
mBatteryStatsImpl.updateCpuTimesForAllUids();
@@ -394,27 +400,6 @@
}
@Test
- public void testAddCpuTimes() {
- long[] timesA = null;
- long[] timesB = null;
- assertNull(mBatteryStatsImpl.addCpuTimes(timesA, timesB));
-
- timesA = new long[] {34, 23, 45, 24};
- assertArrayEquals(timesA, mBatteryStatsImpl.addCpuTimes(timesA, timesB));
-
- timesB = timesA;
- timesA = null;
- assertArrayEquals(timesB, mBatteryStatsImpl.addCpuTimes(timesA, timesB));
-
- final long[] expected = {434, 6784, 34987, 9984};
- timesA = new long[timesB.length];
- for (int i = 0; i < timesA.length; ++i) {
- timesA[i] = expected[i] - timesB[i];
- }
- assertArrayEquals(expected, mBatteryStatsImpl.addCpuTimes(timesA, timesB));
- }
-
- @Test
public void testMulticastWakelockAcqRel() {
final int testUid = 10032;
final int acquireTimeMs = 1000;
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index ba5f66c..f776d3d 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -22,7 +22,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -104,8 +103,8 @@
mMockNtpTrustedTime = mock(NtpTrustedTime.class);
mTimeDetectorService = new TimeDetectorService(
- mMockContext, mTestHandler, mFakeServiceConfigAccessor,
- mFakeTimeDetectorStrategy, mTestCallerIdentityInjector, mMockNtpTrustedTime);
+ mMockContext, mTestHandler, mTestCallerIdentityInjector, mFakeServiceConfigAccessor,
+ mFakeTimeDetectorStrategy, mMockNtpTrustedTime);
}
@After
@@ -114,19 +113,14 @@
mHandlerThread.join();
}
- @Test(expected = SecurityException.class)
+ @Test
public void testGetCapabilitiesAndConfig_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
- try {
- mTimeDetectorService.getCapabilitiesAndConfig();
- fail("Expected SecurityException");
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
- }
+ assertThrows(SecurityException.class, mTimeDetectorService::getCapabilitiesAndConfig);
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
@Test
@@ -141,40 +135,30 @@
mTimeDetectorService.getCapabilitiesAndConfig());
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
- @Test(expected = SecurityException.class)
+ @Test
public void testAddListener_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
ITimeDetectorListener mockListener = mock(ITimeDetectorListener.class);
- try {
- mTimeDetectorService.addListener(mockListener);
- fail("Expected SecurityException");
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
- }
+ assertThrows(SecurityException.class, () -> mTimeDetectorService.addListener(mockListener));
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
- @Test(expected = SecurityException.class)
+ @Test
public void testRemoveListener_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
ITimeDetectorListener mockListener = mock(ITimeDetectorListener.class);
- try {
- mTimeDetectorService.removeListener(mockListener);
- fail("Expected a SecurityException");
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
- }
+ assertThrows(SecurityException.class,
+ () -> mTimeDetectorService.removeListener(mockListener));
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
@Test
@@ -193,8 +177,7 @@
mTimeDetectorService.addListener(mockListener);
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
verify(mockListener).asBinder();
verify(mockListenerBinder).linkToDeath(any(), anyInt());
verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
@@ -213,8 +196,7 @@
mTestHandler.waitForMessagesToBeProcessed();
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
verify(mockListener).onChange();
verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
reset(mockListenerBinder, mockListener, mMockContext);
@@ -230,8 +212,7 @@
mTimeDetectorService.removeListener(mockListener);
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
verify(mockListener).asBinder();
verify(mockListenerBinder).unlinkToDeath(any(), eq(0));
verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
@@ -246,28 +227,23 @@
mTimeDetectorService.updateConfiguration(autoDetectDisabledConfiguration);
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
verify(mockListener, never()).onChange();
verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
reset(mockListenerBinder, mockListener, mMockContext);
}
}
- @Test(expected = SecurityException.class)
+ @Test
public void testSuggestTelephonyTime_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
TelephonyTimeSuggestion timeSuggestion = createTelephonyTimeSuggestion();
- try {
- mTimeDetectorService.suggestTelephonyTime(timeSuggestion);
- fail();
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
- anyString());
- }
+ assertThrows(SecurityException.class,
+ () -> mTimeDetectorService.suggestTelephonyTime(timeSuggestion));
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE), anyString());
}
@Test
@@ -279,27 +255,22 @@
mTestHandler.assertTotalMessagesEnqueued(1);
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
- anyString());
+ eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE), anyString());
mTestHandler.waitForMessagesToBeProcessed();
mFakeTimeDetectorStrategy.verifySuggestTelephonyTimeCalled(timeSuggestion);
}
- @Test(expected = SecurityException.class)
+ @Test
public void testSuggestManualTime_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
ManualTimeSuggestion manualTimeSuggestion = createManualTimeSuggestion();
- try {
- mTimeDetectorService.suggestManualTime(manualTimeSuggestion);
- fail();
- } finally {
- verify(mMockContext).enforceCallingOrSelfPermission(
- eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
- anyString());
- }
+ assertThrows(SecurityException.class,
+ () -> mTimeDetectorService.suggestManualTime(manualTimeSuggestion));
+ verify(mMockContext).enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE), anyString());
}
@Test
@@ -313,24 +284,20 @@
mTestCallerIdentityInjector.getCallingUserId(), manualTimeSuggestion);
verify(mMockContext).enforceCallingOrSelfPermission(
- eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
- anyString());
+ eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE), anyString());
}
- @Test(expected = SecurityException.class)
+ @Test
public void testSuggestNetworkTime_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
NetworkTimeSuggestion networkTimeSuggestion = createNetworkTimeSuggestion();
- try {
- mTimeDetectorService.suggestNetworkTime(networkTimeSuggestion);
- fail();
- } finally {
- verify(mMockContext).enforceCallingOrSelfPermission(
- eq(android.Manifest.permission.SET_TIME), anyString());
- }
+ assertThrows(SecurityException.class,
+ () -> mTimeDetectorService.suggestNetworkTime(networkTimeSuggestion));
+ verify(mMockContext).enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.SET_TIME), anyString());
}
@Test
@@ -348,19 +315,16 @@
mFakeTimeDetectorStrategy.verifySuggestNetworkTimeCalled(networkTimeSuggestion);
}
- @Test(expected = SecurityException.class)
+ @Test
public void testSuggestGnssTime_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
GnssTimeSuggestion gnssTimeSuggestion = createGnssTimeSuggestion();
- try {
- mTimeDetectorService.suggestGnssTime(gnssTimeSuggestion);
- fail();
- } finally {
- verify(mMockContext).enforceCallingOrSelfPermission(
- eq(android.Manifest.permission.SET_TIME), anyString());
- }
+ assertThrows(SecurityException.class,
+ () -> mTimeDetectorService.suggestGnssTime(gnssTimeSuggestion));
+ verify(mMockContext).enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.SET_TIME), anyString());
}
@Test
@@ -378,19 +342,16 @@
mFakeTimeDetectorStrategy.verifySuggestGnssTimeCalled(gnssTimeSuggestion);
}
- @Test(expected = SecurityException.class)
+ @Test
public void testSuggestExternalTime_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
ExternalTimeSuggestion externalTimeSuggestion = createExternalTimeSuggestion();
- try {
- mTimeDetectorService.suggestExternalTime(externalTimeSuggestion);
- fail();
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.SUGGEST_EXTERNAL_TIME), anyString());
- }
+ assertThrows(SecurityException.class,
+ () -> mTimeDetectorService.suggestExternalTime(externalTimeSuggestion));
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.SUGGEST_EXTERNAL_TIME), anyString());
}
@Test
@@ -434,23 +395,18 @@
TimeState actualState = mTimeDetectorService.getTimeState();
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
assertEquals(actualState, fakeState);
}
- @Test(expected = SecurityException.class)
+ @Test
public void testGetTimeState_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
- try {
- mTimeDetectorService.getTimeState();
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
- }
+ assertThrows(SecurityException.class, mTimeDetectorService::getTimeState);
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
@Test
@@ -461,24 +417,19 @@
mTimeDetectorService.setTimeState(state);
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
assertEquals(mFakeTimeDetectorStrategy.getTimeState(), state);
}
- @Test(expected = SecurityException.class)
+ @Test
public void testSetTimeState_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
TimeState state = new TimeState(new UnixEpochTime(12345L, 98765L), true);
- try {
- mTimeDetectorService.setTimeState(state);
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
- }
+ assertThrows(SecurityException.class, () -> mTimeDetectorService.setTimeState(state));
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
@Test
@@ -490,23 +441,19 @@
assertFalse(mTimeDetectorService.confirmTime(confirmationTime));
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
mFakeTimeDetectorStrategy.verifyConfirmTimeCalled(confirmationTime);
}
- @Test(expected = SecurityException.class)
+ @Test
public void testConfirmTime_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
- try {
- mTimeDetectorService.confirmTime(new UnixEpochTime(12345L, 98765L));
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
- }
+ assertThrows(SecurityException.class,
+ () -> mTimeDetectorService.confirmTime(new UnixEpochTime(12345L, 98765L)));
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
@Test
@@ -524,24 +471,19 @@
mTestCallerIdentityInjector.getCallingUserId(), timeSuggestion);
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
- @Test(expected = SecurityException.class)
+ @Test
public void testSetManualTime_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
ManualTimeSuggestion timeSuggestion = createManualTimeSuggestion();
- try {
- mTimeDetectorService.setManualTime(timeSuggestion);
- fail("Expected SecurityException");
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
- }
+ assertThrows(SecurityException.class,
+ () -> mTimeDetectorService.setManualTime(timeSuggestion));
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
@Test
@@ -562,7 +504,7 @@
.build();
}
- private static ConfigurationInternal createConfigurationInternal(boolean autoDetectionEnabled) {
+ static ConfigurationInternal createConfigurationInternal(boolean autoDetectionEnabled) {
return new ConfigurationInternal.Builder(ARBITRARY_USER_ID)
.setUserConfigAllowed(true)
.setAutoDetectionSupported(true)
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
index b8c3ea7..fc64597 100644
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
@@ -18,7 +18,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -97,19 +97,14 @@
mHandlerThread.join();
}
- @Test(expected = SecurityException.class)
+ @Test
public void testGetCapabilitiesAndConfig_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
- try {
- mTimeZoneDetectorService.getCapabilitiesAndConfig();
- fail("Expected SecurityException");
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
- }
+ assertThrows(SecurityException.class, mTimeZoneDetectorService::getCapabilitiesAndConfig);
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
@Test
@@ -124,40 +119,31 @@
mTimeZoneDetectorService.getCapabilitiesAndConfig());
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
- @Test(expected = SecurityException.class)
+ @Test
public void testAddListener_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
ITimeZoneDetectorListener mockListener = mock(ITimeZoneDetectorListener.class);
- try {
- mTimeZoneDetectorService.addListener(mockListener);
- fail("Expected SecurityException");
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
- }
+ assertThrows(SecurityException.class,
+ () -> mTimeZoneDetectorService.addListener(mockListener));
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
- @Test(expected = SecurityException.class)
+ @Test
public void testRemoveListener_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
ITimeZoneDetectorListener mockListener = mock(ITimeZoneDetectorListener.class);
- try {
- mTimeZoneDetectorService.removeListener(mockListener);
- fail("Expected a SecurityException");
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
- }
+ assertThrows(SecurityException.class,
+ () -> mTimeZoneDetectorService.removeListener(mockListener));
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
@Test
@@ -176,8 +162,7 @@
mTimeZoneDetectorService.addListener(mockListener);
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
verify(mockListener).asBinder();
verify(mockListenerBinder).linkToDeath(any(), anyInt());
verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
@@ -196,8 +181,7 @@
mTestHandler.waitForMessagesToBeProcessed();
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
verify(mockListener).onChange();
verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
reset(mockListenerBinder, mockListener, mMockContext);
@@ -213,8 +197,7 @@
mTimeZoneDetectorService.removeListener(mockListener);
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
verify(mockListener).asBinder();
verify(mockListenerBinder).unlinkToDeath(any(), eq(0));
verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
@@ -229,28 +212,23 @@
mTimeZoneDetectorService.updateConfiguration(autoDetectDisabledConfiguration);
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
verify(mockListener, never()).onChange();
verifyNoMoreInteractions(mockListenerBinder, mockListener, mMockContext);
reset(mockListenerBinder, mockListener, mMockContext);
}
}
- @Test(expected = SecurityException.class)
+ @Test
public void testSuggestGeolocationTimeZone_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
GeolocationTimeZoneSuggestion timeZoneSuggestion = createGeolocationTimeZoneSuggestion();
- try {
- mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion);
- fail("Expected SecurityException");
- } finally {
- verify(mMockContext).enforceCallingOrSelfPermission(
- eq(android.Manifest.permission.SET_TIME_ZONE),
- anyString());
- }
+ assertThrows(SecurityException.class,
+ () -> mTimeZoneDetectorService.suggestGeolocationTimeZone(timeZoneSuggestion));
+ verify(mMockContext).enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.SET_TIME_ZONE), anyString());
}
@Test
@@ -263,27 +241,22 @@
mTestHandler.assertTotalMessagesEnqueued(1);
verify(mMockContext).enforceCallingOrSelfPermission(
- eq(android.Manifest.permission.SET_TIME_ZONE),
- anyString());
+ eq(android.Manifest.permission.SET_TIME_ZONE), anyString());
mTestHandler.waitForMessagesToBeProcessed();
mFakeTimeZoneDetectorStrategy.verifySuggestGeolocationTimeZoneCalled(timeZoneSuggestion);
}
- @Test(expected = SecurityException.class)
+ @Test
public void testSuggestManualTimeZone_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingOrSelfPermission(anyString(), any());
ManualTimeZoneSuggestion timeZoneSuggestion = createManualTimeZoneSuggestion();
- try {
- mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion);
- fail("Expected SecurityException");
- } finally {
- verify(mMockContext).enforceCallingOrSelfPermission(
- eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
- anyString());
- }
+ assertThrows(SecurityException.class,
+ () -> mTimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion));
+ verify(mMockContext).enforceCallingOrSelfPermission(
+ eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE), anyString());
}
@Test
@@ -300,40 +273,31 @@
mTestCallerIdentityInjector.getCallingUserId(), timeZoneSuggestion);
verify(mMockContext).enforceCallingOrSelfPermission(
- eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE),
- anyString());
+ eq(android.Manifest.permission.SUGGEST_MANUAL_TIME_AND_ZONE), anyString());
}
- @Test(expected = SecurityException.class)
+ @Test
public void testSuggestTelephonyTime_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
TelephonyTimeZoneSuggestion timeZoneSuggestion = createTelephonyTimeZoneSuggestion();
- try {
- mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
- fail("Expected SecurityException");
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
- anyString());
- }
+ assertThrows(SecurityException.class,
+ () -> mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion));
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE), anyString());
}
- @Test(expected = SecurityException.class)
+ @Test
public void testSuggestTelephonyTimeZone_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
TelephonyTimeZoneSuggestion timeZoneSuggestion = createTelephonyTimeZoneSuggestion();
- try {
- mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion);
- fail("Expected SecurityException");
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
- anyString());
- }
+ assertThrows(SecurityException.class,
+ () -> mTimeZoneDetectorService.suggestTelephonyTimeZone(timeZoneSuggestion));
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE), anyString());
}
@Test
@@ -345,8 +309,7 @@
mTestHandler.assertTotalMessagesEnqueued(1);
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE),
- anyString());
+ eq(android.Manifest.permission.SUGGEST_TELEPHONY_TIME_AND_ZONE), anyString());
mTestHandler.waitForMessagesToBeProcessed();
mFakeTimeZoneDetectorStrategy.verifySuggestTelephonyTimeZoneCalled(timeZoneSuggestion);
@@ -361,23 +324,18 @@
TimeZoneState actualState = mTimeZoneDetectorService.getTimeZoneState();
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
assertEquals(actualState, fakeState);
}
- @Test(expected = SecurityException.class)
+ @Test
public void testGetTimeZoneState_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
- try {
- mTimeZoneDetectorService.getTimeZoneState();
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
- }
+ assertThrows(SecurityException.class, mTimeZoneDetectorService::getTimeZoneState);
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
@Test
@@ -388,24 +346,20 @@
mTimeZoneDetectorService.setTimeZoneState(state);
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
assertEquals(mFakeTimeZoneDetectorStrategy.getTimeZoneState(), state);
}
- @Test(expected = SecurityException.class)
+ @Test
public void testSetTimeZoneState_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
TimeZoneState state = new TimeZoneState("Europe/Narnia", true);
- try {
- mTimeZoneDetectorService.setTimeZoneState(state);
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
- }
+ assertThrows(SecurityException.class,
+ () -> mTimeZoneDetectorService.setTimeZoneState(state));
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
@Test
@@ -416,23 +370,19 @@
assertFalse(mTimeZoneDetectorService.confirmTimeZone("Europe/Narnia"));
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
mFakeTimeZoneDetectorStrategy.verifyConfirmTimeZoneCalled("Europe/Narnia");
}
- @Test(expected = SecurityException.class)
+ @Test
public void testConfirmTimeZone_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
- try {
- mTimeZoneDetectorService.confirmTimeZone("Europe/Narnia");
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
- }
+ assertThrows(SecurityException.class,
+ () -> mTimeZoneDetectorService.confirmTimeZone("Europe/Narnia"));
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
@Test
@@ -450,24 +400,19 @@
mTestCallerIdentityInjector.getCallingUserId(), timeZoneSuggestion);
verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
- @Test(expected = SecurityException.class)
+ @Test
public void testSetManualTimeZone_withoutPermission() {
doThrow(new SecurityException("Mock"))
.when(mMockContext).enforceCallingPermission(anyString(), any());
ManualTimeZoneSuggestion timeZoneSuggestion = createManualTimeZoneSuggestion();
- try {
- mTimeZoneDetectorService.setManualTimeZone(timeZoneSuggestion);
- fail("Expected SecurityException");
- } finally {
- verify(mMockContext).enforceCallingPermission(
- eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION),
- anyString());
- }
+ assertThrows(SecurityException.class,
+ () -> mTimeZoneDetectorService.setManualTimeZone(timeZoneSuggestion));
+ verify(mMockContext).enforceCallingPermission(
+ eq(android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION), anyString());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index bc2c57e..3adee0f 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -24,7 +24,6 @@
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
-import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
@@ -32,6 +31,7 @@
import android.content.res.Configuration;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.AtomicFile;
+import android.util.TimeSparseArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 51894f3..52280d2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -396,6 +396,70 @@
}
@Test
+ public void testCreateInfo_PromoteSimilarClose() {
+ final Transition transition = createTestTransition(TRANSIT_CLOSE);
+ ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+ ArraySet<WindowContainer> participants = transition.mParticipants;
+
+ final Task topTask = createTask(mDisplayContent);
+ final Task belowTask = createTask(mDisplayContent);
+ final ActivityRecord showing = createActivityRecord(belowTask);
+ final ActivityRecord hiding = createActivityRecord(topTask);
+ final ActivityRecord closing = createActivityRecord(topTask);
+ // Start states.
+ changes.put(topTask, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+ changes.put(belowTask, new Transition.ChangeInfo(false /* vis */, false /* exChg */));
+ changes.put(showing, new Transition.ChangeInfo(false /* vis */, false /* exChg */));
+ changes.put(hiding, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+ changes.put(closing, new Transition.ChangeInfo(true /* vis */, true /* exChg */));
+ fillChangeMap(changes, topTask);
+ // End states.
+ showing.mVisibleRequested = true;
+ closing.mVisibleRequested = false;
+ hiding.mVisibleRequested = false;
+
+ participants.add(belowTask);
+ participants.add(hiding);
+ participants.add(closing);
+ ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
+ assertEquals(2, targets.size());
+ assertTrue(targets.contains(belowTask));
+ assertTrue(targets.contains(topTask));
+ }
+
+ @Test
+ public void testCreateInfo_PromoteSimilarOpen() {
+ final Transition transition = createTestTransition(TRANSIT_OPEN);
+ ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+ ArraySet<WindowContainer> participants = transition.mParticipants;
+
+ final Task topTask = createTask(mDisplayContent);
+ final Task belowTask = createTask(mDisplayContent);
+ final ActivityRecord showing = createActivityRecord(topTask);
+ final ActivityRecord opening = createActivityRecord(topTask);
+ final ActivityRecord closing = createActivityRecord(belowTask);
+ // Start states.
+ changes.put(topTask, new Transition.ChangeInfo(false /* vis */, false /* exChg */));
+ changes.put(belowTask, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+ changes.put(showing, new Transition.ChangeInfo(false /* vis */, false /* exChg */));
+ changes.put(opening, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+ changes.put(closing, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+ fillChangeMap(changes, topTask);
+ // End states.
+ showing.mVisibleRequested = true;
+ opening.mVisibleRequested = true;
+ closing.mVisibleRequested = false;
+
+ participants.add(belowTask);
+ participants.add(showing);
+ participants.add(opening);
+ ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes);
+ assertEquals(2, targets.size());
+ assertTrue(targets.contains(belowTask));
+ assertTrue(targets.contains(topTask));
+ }
+
+ @Test
public void testTargets_noIntermediatesToWallpaper() {
final Transition transition = createTestTransition(TRANSIT_OPEN);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 26a1e9d..361b1c0 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
@@ -29,6 +28,7 @@
import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.TimeSparseArray;
import android.util.TimeUtils;
import com.android.internal.annotations.VisibleForTesting;
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 34c6c16..9203208 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -34,7 +34,6 @@
import android.app.usage.ConfigurationStats;
import android.app.usage.EventList;
import android.app.usage.EventStats;
-import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStats;
@@ -50,6 +49,7 @@
import android.util.Slog;
import android.util.SparseArrayMap;
import android.util.SparseIntArray;
+import android.util.TimeSparseArray;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index be7c112..79546b8 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -172,11 +172,11 @@
mAmInternal.setVoiceInteractionManagerProvider(
new ActivityManagerInternal.VoiceInteractionManagerProvider() {
@Override
- public void notifyActivityEventChanged() {
+ public void notifyActivityDestroyed(IBinder activityToken) {
if (DEBUG) {
- Slog.d(TAG, "call notifyActivityEventChanged");
+ Slog.d(TAG, "notifyActivityDestroyed activityToken=" + activityToken);
}
- mServiceStub.notifyActivityEventChanged();
+ mServiceStub.notifyActivityDestroyed(activityToken);
}
});
}
@@ -449,11 +449,12 @@
return mImpl.supportsLocalVoiceInteraction();
}
- void notifyActivityEventChanged() {
+ void notifyActivityDestroyed(@NonNull IBinder activityToken) {
synchronized (this) {
- if (mImpl == null) return;
+ if (mImpl == null || activityToken == null) return;
- Binder.withCleanCallingIdentity(() -> mImpl.notifyActivityEventChangedLocked());
+ Binder.withCleanCallingIdentity(
+ () -> mImpl.notifyActivityDestroyedLocked(activityToken));
}
}
@@ -1226,6 +1227,16 @@
}
}
+ @Override
+ public void notifyActivityEventChanged(@NonNull IBinder activityToken, int type) {
+ synchronized (this) {
+ if (mImpl == null || activityToken == null) {
+ return;
+ }
+ Binder.withCleanCallingIdentity(
+ () -> mImpl.notifyActivityEventChangedLocked(activityToken, type));
+ }
+ }
//----------------- Hotword Detection/Validation APIs --------------------------------//
@android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_HOTWORD_DETECTION)
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 9f66059..dcf7b78 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -522,9 +522,23 @@
mActiveSession.stopListeningVisibleActivityChangedLocked();
}
- public void notifyActivityEventChangedLocked() {
+ public void notifyActivityDestroyedLocked(@NonNull IBinder activityToken) {
if (DEBUG) {
- Slog.d(TAG, "notifyActivityEventChangedLocked");
+ Slog.d(TAG, "notifyActivityDestroyedLocked activityToken=" + activityToken);
+ }
+ if (mActiveSession == null || !mActiveSession.mShown) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyActivityDestroyedLocked not allowed on no session or"
+ + " hidden session");
+ }
+ return;
+ }
+ mActiveSession.notifyActivityDestroyedLocked(activityToken);
+ }
+
+ public void notifyActivityEventChangedLocked(@NonNull IBinder activityToken, int type) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyActivityEventChangedLocked type=" + type);
}
if (mActiveSession == null || !mActiveSession.mShown) {
if (DEBUG) {
@@ -533,7 +547,7 @@
}
return;
}
- mActiveSession.notifyActivityEventChangedLocked();
+ mActiveSession.notifyActivityEventChangedLocked(activityToken, type);
}
public void updateStateLocked(
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 980b3b5..f8bc499 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -61,6 +61,7 @@
import android.service.voice.VisibleActivityInfo;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionSession;
+import android.util.ArrayMap;
import android.util.Slog;
import android.view.IWindowManager;
@@ -130,7 +131,11 @@
private boolean mListeningVisibleActivity;
private final ScheduledExecutorService mScheduledExecutorService =
Executors.newSingleThreadScheduledExecutor();
- private final List<VisibleActivityInfo> mVisibleActivityInfos = new ArrayList<>();
+ // Records the visible activity information the system has already called onVisible, without
+ // confirming the result of callback. When activity visible state is changed, we use this to
+ // determine to call onVisible or onInvisible to assistant application.
+ private final ArrayMap<IBinder, VisibleActivityInfo> mVisibleActivityInfoForToken =
+ new ArrayMap<>();
private final PowerManagerInternal mPowerManagerInternal;
private final LowPowerStandbyControllerInternal mLowPowerStandbyControllerInternal;
private final Runnable mRemoveFromLowPowerStandbyAllowlistRunnable =
@@ -535,7 +540,7 @@
public void cancelLocked(boolean finishTask) {
mListeningVisibleActivity = false;
- mVisibleActivityInfos.clear();
+ mVisibleActivityInfoForToken.clear();
hideLocked();
mCanceled = true;
if (mBound) {
@@ -613,17 +618,24 @@
if (DEBUG) {
Slog.d(TAG, "startListeningVisibleActivityChangedLocked");
}
- mListeningVisibleActivity = true;
- mVisibleActivityInfos.clear();
- mScheduledExecutorService.execute(() -> {
- if (DEBUG) {
- Slog.d(TAG, "call handleVisibleActivitiesLocked from enable listening");
- }
- synchronized (mLock) {
- handleVisibleActivitiesLocked();
- }
- });
+ if (!mShown || mCanceled || mSession == null) {
+ return;
+ }
+
+ mListeningVisibleActivity = true;
+ mVisibleActivityInfoForToken.clear();
+
+ // It should only need to report which activities are visible
+ final ArrayMap<IBinder, VisibleActivityInfo> newVisibleActivityInfos =
+ getTopVisibleActivityInfosLocked();
+
+ if (newVisibleActivityInfos == null || newVisibleActivityInfos.isEmpty()) {
+ return;
+ }
+ notifyVisibleActivitiesChangedLocked(newVisibleActivityInfos,
+ VisibleActivityInfo.TYPE_ACTIVITY_ADDED);
+ mVisibleActivityInfoForToken.putAll(newVisibleActivityInfos);
}
void stopListeningVisibleActivityChangedLocked() {
@@ -631,12 +643,13 @@
Slog.d(TAG, "stopListeningVisibleActivityChangedLocked");
}
mListeningVisibleActivity = false;
- mVisibleActivityInfos.clear();
+ mVisibleActivityInfoForToken.clear();
}
- void notifyActivityEventChangedLocked() {
+ void notifyActivityEventChangedLocked(@NonNull IBinder activityToken, int type) {
if (DEBUG) {
- Slog.d(TAG, "notifyActivityEventChangedLocked");
+ Slog.d(TAG, "notifyActivityEventChangedLocked activityToken=" + activityToken
+ + ", type=" + type);
}
if (!mListeningVisibleActivity) {
if (DEBUG) {
@@ -645,99 +658,139 @@
return;
}
mScheduledExecutorService.execute(() -> {
- if (DEBUG) {
- Slog.d(TAG, "call handleVisibleActivitiesLocked from activity event");
- }
synchronized (mLock) {
- handleVisibleActivitiesLocked();
+ handleVisibleActivitiesLocked(activityToken, type);
}
});
}
- private List<VisibleActivityInfo> getVisibleActivityInfosLocked() {
+ private ArrayMap<IBinder, VisibleActivityInfo> getTopVisibleActivityInfosLocked() {
if (DEBUG) {
- Slog.d(TAG, "getVisibleActivityInfosLocked");
+ Slog.d(TAG, "getTopVisibleActivityInfosLocked");
}
List<ActivityAssistInfo> allVisibleActivities =
LocalServices.getService(ActivityTaskManagerInternal.class)
.getTopVisibleActivities();
if (DEBUG) {
- Slog.d(TAG,
- "getVisibleActivityInfosLocked: allVisibleActivities=" + allVisibleActivities);
+ Slog.d(TAG, "getTopVisibleActivityInfosLocked: allVisibleActivities="
+ + allVisibleActivities);
}
- if (allVisibleActivities == null || allVisibleActivities.isEmpty()) {
+ if (allVisibleActivities.isEmpty()) {
Slog.w(TAG, "no visible activity");
return null;
}
final int count = allVisibleActivities.size();
- final List<VisibleActivityInfo> visibleActivityInfos = new ArrayList<>(count);
+ final ArrayMap<IBinder, VisibleActivityInfo> visibleActivityInfoArrayMap =
+ new ArrayMap<>(count);
for (int i = 0; i < count; i++) {
ActivityAssistInfo info = allVisibleActivities.get(i);
if (DEBUG) {
- Slog.d(TAG, " : activityToken=" + info.getActivityToken()
+ Slog.d(TAG, "ActivityAssistInfo : activityToken=" + info.getActivityToken()
+ ", assistToken=" + info.getAssistToken()
+ ", taskId=" + info.getTaskId());
}
- visibleActivityInfos.add(
+ visibleActivityInfoArrayMap.put(info.getActivityToken(),
new VisibleActivityInfo(info.getTaskId(), info.getAssistToken()));
}
- return visibleActivityInfos;
+ return visibleActivityInfoArrayMap;
}
- private void handleVisibleActivitiesLocked() {
+ // TODO(b/242359988): Split this method up
+ private void handleVisibleActivitiesLocked(@NonNull IBinder activityToken, int type) {
if (DEBUG) {
- Slog.d(TAG, "handleVisibleActivitiesLocked");
+ Slog.d(TAG, "handleVisibleActivitiesLocked activityToken=" + activityToken
+ + ", type=" + type);
}
- if (mSession == null) {
- return;
- }
- if (!mShown || !mListeningVisibleActivity || mCanceled) {
- return;
- }
- final List<VisibleActivityInfo> newVisibleActivityInfos = getVisibleActivityInfosLocked();
- if (newVisibleActivityInfos == null || newVisibleActivityInfos.isEmpty()) {
- notifyVisibleActivitiesChangedLocked(mVisibleActivityInfos,
- VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
- mVisibleActivityInfos.clear();
+ if (!mListeningVisibleActivity) {
+ if (DEBUG) {
+ Slog.d(TAG, "not enable listening visible activity");
+ }
return;
}
- if (mVisibleActivityInfos.isEmpty()) {
- notifyVisibleActivitiesChangedLocked(newVisibleActivityInfos,
- VisibleActivityInfo.TYPE_ACTIVITY_ADDED);
- mVisibleActivityInfos.addAll(newVisibleActivityInfos);
+ if (!mShown || mCanceled || mSession == null) {
return;
}
- final List<VisibleActivityInfo> addedActivities = new ArrayList<>();
- final List<VisibleActivityInfo> removedActivities = new ArrayList<>();
+ // We use this local variable to determine to call onVisible or onInvisible.
+ boolean notifyOnVisible = false;
+ VisibleActivityInfo notifyVisibleActivityInfo = null;
- removedActivities.addAll(mVisibleActivityInfos);
- for (int i = 0; i < newVisibleActivityInfos.size(); i++) {
- final VisibleActivityInfo candidateVisibleActivityInfo = newVisibleActivityInfos.get(i);
- if (!removedActivities.isEmpty() && removedActivities.contains(
- candidateVisibleActivityInfo)) {
- removedActivities.remove(candidateVisibleActivityInfo);
- } else {
- addedActivities.add(candidateVisibleActivityInfo);
+ if (type == VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_START
+ || type == VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_RESUME) {
+ // It seems that the onStart is unnecessary. But if we have it, the assistant
+ // application can request the directActions early. Even if we have the onStart,
+ // we still need the onResume because it is possible that the activity goes to
+ // onResume from onPause with invisible before the activity goes to onStop from
+ // onPause.
+
+ // Check if we have reported this activity as visible. If we have reported it as
+ // visible, do nothing.
+ if (mVisibleActivityInfoForToken.containsKey(activityToken)) {
+ return;
+ }
+
+ // Before reporting this activity as visible, we need to make sure the activity
+ // is really visible.
+ notifyVisibleActivityInfo = getVisibleActivityInfoFromTopVisibleActivity(
+ activityToken);
+ if (notifyVisibleActivityInfo == null) {
+ return;
+ }
+ notifyOnVisible = true;
+ } else if (type == VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_PAUSE) {
+ // For the onPause stage, the Activity is not necessarily invisible now, so we need
+ // to check its state.
+ // Note: After syncing with Activity owner, before the onPause is called, the
+ // visibility state has been updated.
+ notifyVisibleActivityInfo = getVisibleActivityInfoFromTopVisibleActivity(
+ activityToken);
+ if (notifyVisibleActivityInfo != null) {
+ return;
+ }
+
+ // Also make sure we previously reported this Activity as visible.
+ notifyVisibleActivityInfo = mVisibleActivityInfoForToken.get(activityToken);
+ if (notifyVisibleActivityInfo == null) {
+ return;
+ }
+ } else if (type == VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_STOP) {
+ // For the onStop stage, the activity is in invisible state. We only need to consider if
+ // we have reported this activity as visible. If we have reported it as visible, we
+ // need to report it as invisible.
+ // Why we still need onStop? Because it is possible that the activity is in a visible
+ // state during onPause stage, when the activity enters onStop from onPause, we may
+ // need to notify onInvisible.
+ // Note: After syncing with Activity owner, before the onStop is called, the
+ // visibility state has been updated.
+ notifyVisibleActivityInfo = mVisibleActivityInfoForToken.get(activityToken);
+ if (notifyVisibleActivityInfo == null) {
+ return;
+ }
+ } else {
+ Slog.w(TAG, "notifyActivityEventChangedLocked unexpected type=" + type);
+ return;
+ }
+
+ try {
+ mSession.notifyVisibleActivityInfoChanged(notifyVisibleActivityInfo,
+ notifyOnVisible ? VisibleActivityInfo.TYPE_ACTIVITY_ADDED
+ : VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
+ } catch (RemoteException e) {
+ if (DEBUG) {
+ Slog.w(TAG, "handleVisibleActivitiesLocked RemoteException : " + e);
}
}
- if (!addedActivities.isEmpty()) {
- notifyVisibleActivitiesChangedLocked(addedActivities,
- VisibleActivityInfo.TYPE_ACTIVITY_ADDED);
+ if (notifyOnVisible) {
+ mVisibleActivityInfoForToken.put(activityToken, notifyVisibleActivityInfo);
+ } else {
+ mVisibleActivityInfoForToken.remove(activityToken);
}
- if (!removedActivities.isEmpty()) {
- notifyVisibleActivitiesChangedLocked(removedActivities,
- VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
- }
-
- mVisibleActivityInfos.clear();
- mVisibleActivityInfos.addAll(newVisibleActivityInfos);
}
private void notifyVisibleActivitiesChangedLocked(
- List<VisibleActivityInfo> visibleActivityInfos, int type) {
+ ArrayMap<IBinder, VisibleActivityInfo> visibleActivityInfos, int type) {
if (visibleActivityInfos == null || visibleActivityInfos.isEmpty()) {
return;
}
@@ -746,7 +799,7 @@
}
try {
for (int i = 0; i < visibleActivityInfos.size(); i++) {
- mSession.notifyVisibleActivityInfoChanged(visibleActivityInfos.get(i), type);
+ mSession.notifyVisibleActivityInfoChanged(visibleActivityInfos.valueAt(i), type);
}
} catch (RemoteException e) {
if (DEBUG) {
@@ -759,6 +812,51 @@
}
}
+ private VisibleActivityInfo getVisibleActivityInfoFromTopVisibleActivity(
+ @NonNull IBinder activityToken) {
+ final ArrayMap<IBinder, VisibleActivityInfo> visibleActivityInfos =
+ getTopVisibleActivityInfosLocked();
+ if (visibleActivityInfos == null) {
+ return null;
+ }
+ return visibleActivityInfos.get(activityToken);
+ }
+
+ void notifyActivityDestroyedLocked(@NonNull IBinder activityToken) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyActivityDestroyedLocked activityToken=" + activityToken);
+ }
+ if (!mListeningVisibleActivity) {
+ if (DEBUG) {
+ Slog.d(TAG, "not enable listening visible activity");
+ }
+ return;
+ }
+ mScheduledExecutorService.execute(() -> {
+ synchronized (mLock) {
+ if (!mListeningVisibleActivity) {
+ return;
+ }
+ if (!mShown || mCanceled || mSession == null) {
+ return;
+ }
+
+ VisibleActivityInfo visibleActivityInfo = mVisibleActivityInfoForToken.remove(
+ activityToken);
+ if (visibleActivityInfo != null) {
+ try {
+ mSession.notifyVisibleActivityInfoChanged(visibleActivityInfo,
+ VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
+ } catch (RemoteException e) {
+ if (DEBUG) {
+ Slog.w(TAG, "notifyVisibleActivityInfoChanged RemoteException : " + e);
+ }
+ }
+ }
+ }
+ });
+ }
+
private void removeFromLowPowerStandbyAllowlist() {
synchronized (mLock) {
if (mLowPowerStandbyAllowlisted) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index 4c47684..a27b2da 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker.ime
-import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.FlakyTest
import android.view.Surface
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
@@ -61,7 +61,7 @@
}
/** {@inheritDoc} */
- @Presubmit
+ @FlakyTest(bugId = 251214932)
@Test
override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
// depends on how much of the animation transactions are sent to SF at once
@@ -79,7 +79,7 @@
}
/** {@inheritDoc} */
- @Presubmit
+ @FlakyTest(bugId = 251214932)
@Test
override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
val component = ComponentNameMatcher("", "RecentTaskScreenshotSurface")
@@ -95,7 +95,7 @@
}
}
- @Presubmit
+ @FlakyTest(bugId = 251214932)
@Test
fun launcherWindowBecomesInvisible() {
testSpec.assertWm {
@@ -105,9 +105,11 @@
}
}
- @Presubmit @Test fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible()
+ @FlakyTest(bugId = 251214932)
+ @Test
+ fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible()
- @Presubmit
+ @FlakyTest(bugId = 251214932)
@Test
fun imeAppWindowIsAlwaysVisible() {
// the app starts visible in live tile, and stays visible for the duration of entering
@@ -117,13 +119,13 @@
testSpec.assertWm { this.isAppWindowVisible(testApp) }
}
- @Presubmit
+ @FlakyTest(bugId = 251214932)
@Test
fun imeLayerBecomesVisible() {
testSpec.assertLayers { this.isVisible(ComponentNameMatcher.IME) }
}
- @Presubmit
+ @FlakyTest(bugId = 251214932)
@Test
fun appLayerReplacesLauncher() {
testSpec.assertLayers {
@@ -135,6 +137,60 @@
}
}
+ @FlakyTest(bugId = 251214932)
+ @Test
+ override fun navBarLayerPositionAtStartAndEnd() {
+ super.navBarLayerPositionAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 251214932)
+ @Test
+ override fun navBarWindowIsAlwaysVisible() {
+ super.navBarWindowIsAlwaysVisible()
+ }
+
+ @FlakyTest(bugId = 251214932)
+ @Test
+ override fun statusBarLayerIsVisibleAtStartAndEnd() {
+ super.statusBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 251214932)
+ @Test
+ override fun entireScreenCovered() {
+ super.entireScreenCovered()
+ }
+
+ @FlakyTest(bugId = 251214932)
+ @Test
+ override fun navBarLayerIsVisibleAtStartAndEnd() {
+ super.navBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 251214932)
+ @Test
+ override fun statusBarLayerPositionAtStartAndEnd() {
+ super.statusBarLayerPositionAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 251214932)
+ @Test
+ override fun statusBarWindowIsAlwaysVisible() {
+ super.statusBarWindowIsAlwaysVisible()
+ }
+
+ @FlakyTest(bugId = 251214932)
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() {
+ super.taskBarLayerIsVisibleAtStartAndEnd()
+ }
+
+ @FlakyTest(bugId = 251214932)
+ @Test
+ override fun taskBarWindowIsAlwaysVisible() {
+ super.taskBarWindowIsAlwaysVisible()
+ }
+
companion object {
@Parameterized.Parameters(name = "{0}")
@JvmStatic
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 5597287..89e3b5f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -197,6 +197,12 @@
@Test
override fun appWindowBecomesTopWindow() = super.appWindowBecomesTopWindow()
+ @FlakyTest(bugId = 251217585)
+ @Test
+ override fun focusChanges() {
+ super.focusChanges()
+ }
+
companion object {
/**
* Creates the test configurations.