Merge "Enables the rotation suggestion button during setup wizard"
diff --git a/Android.bp b/Android.bp
index 54983d6..3b8ef61 100644
--- a/Android.bp
+++ b/Android.bp
@@ -156,7 +156,6 @@
"framework-scheduling.stubs.module_lib",
"framework-sdkextensions.stubs.module_lib",
"framework-statsd.stubs.module_lib",
- "framework-supplementalapi.stubs.module_lib",
"framework-supplementalprocess.stubs.module_lib",
"framework-tethering.stubs.module_lib",
"framework-uwb.stubs.module_lib",
@@ -181,7 +180,6 @@
"framework-scheduling.impl",
"framework-sdkextensions.impl",
"framework-statsd.impl",
- "framework-supplementalapi.impl",
"framework-supplementalprocess.impl",
"framework-tethering.impl",
"framework-uwb.impl",
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 92c63c9..3b11036 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -250,7 +250,6 @@
"framework-scheduling.stubs",
"framework-sdkextensions.stubs",
"framework-statsd.stubs",
- "framework-supplementalapi.stubs",
"framework-supplementalprocess.stubs",
"framework-tethering.stubs",
"framework-uwb.stubs",
@@ -273,7 +272,6 @@
"framework-scheduling.stubs.system",
"framework-sdkextensions.stubs.system",
"framework-statsd.stubs.system",
- "framework-supplementalapi.stubs",
"framework-supplementalprocess.stubs",
"framework-tethering.stubs.system",
"framework-uwb.stubs.system",
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
index 005c447..a6a007f 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Agent.java
@@ -46,6 +46,7 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArrayMap;
+import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -681,8 +682,8 @@
final long minBalance = mIrs.getMinBalanceLocked(userId, pkgName);
final double perc = batteryLevel / 100d;
// TODO: maybe don't give credits to bankrupt apps until battery level >= 50%
- if (ledger.getCurrentBalance() < minBalance) {
- final long shortfall = minBalance - getBalanceLocked(userId, pkgName);
+ final long shortfall = minBalance - ledger.getCurrentBalance();
+ if (shortfall > 0) {
recordTransactionLocked(userId, pkgName, ledger,
new Ledger.Transaction(now, now, REGULATION_BASIC_INCOME,
null, (long) (perc * shortfall)), true);
@@ -1170,5 +1171,57 @@
void dumpLocked(IndentingPrintWriter pw) {
pw.println();
mBalanceThresholdAlarmQueue.dump(pw);
+
+ pw.println();
+ pw.println("Ongoing events:");
+ pw.increaseIndent();
+ boolean printedEvents = false;
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ for (int u = mCurrentOngoingEvents.numMaps() - 1; u >= 0; --u) {
+ final int userId = mCurrentOngoingEvents.keyAt(u);
+ for (int p = mCurrentOngoingEvents.numElementsForKey(userId) - 1; p >= 0; --p) {
+ final String pkgName = mCurrentOngoingEvents.keyAt(u, p);
+ final SparseArrayMap<String, OngoingEvent> ongoingEvents =
+ mCurrentOngoingEvents.get(userId, pkgName);
+
+ boolean printedApp = false;
+
+ for (int e = ongoingEvents.numMaps() - 1; e >= 0; --e) {
+ final int eventId = ongoingEvents.keyAt(e);
+ for (int t = ongoingEvents.numElementsForKey(eventId) - 1; t >= 0; --t) {
+ if (!printedApp) {
+ printedApp = true;
+ pw.println(appToString(userId, pkgName));
+ pw.increaseIndent();
+ }
+ printedEvents = true;
+
+ OngoingEvent ongoingEvent = ongoingEvents.valueAt(e, t);
+
+ pw.print(EconomicPolicy.eventToString(ongoingEvent.eventId));
+ if (ongoingEvent.tag != null) {
+ pw.print("(");
+ pw.print(ongoingEvent.tag);
+ pw.print(")");
+ }
+ pw.print(" runtime=");
+ TimeUtils.formatDuration(nowElapsed - ongoingEvent.startTimeElapsed, pw);
+ pw.print(" delta/sec=");
+ pw.print(ongoingEvent.deltaPerSec);
+ pw.print(" refCount=");
+ pw.print(ongoingEvent.refCount);
+ pw.println();
+ }
+ }
+
+ if (printedApp) {
+ pw.decreaseIndent();
+ }
+ }
+ }
+ if (!printedEvents) {
+ pw.print("N/A");
+ }
+ pw.decreaseIndent();
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 20a300a..36895a5 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -567,20 +567,23 @@
final String pkgName = event.getPackageName();
if (DEBUG) {
Slog.d(TAG, "Processing event " + event.getEventType()
+ + " (" + event.mInstanceId + ")"
+ " for " + appToString(userId, pkgName));
}
final long nowElapsed = SystemClock.elapsedRealtime();
switch (event.getEventType()) {
case UsageEvents.Event.ACTIVITY_RESUMED:
mAgent.noteOngoingEventLocked(userId, pkgName,
- EconomicPolicy.REWARD_TOP_ACTIVITY, null, nowElapsed);
+ EconomicPolicy.REWARD_TOP_ACTIVITY, String.valueOf(event.mInstanceId),
+ nowElapsed);
break;
case UsageEvents.Event.ACTIVITY_PAUSED:
case UsageEvents.Event.ACTIVITY_STOPPED:
case UsageEvents.Event.ACTIVITY_DESTROYED:
final long now = getCurrentTimeMillis();
mAgent.stopOngoingActionLocked(userId, pkgName,
- EconomicPolicy.REWARD_TOP_ACTIVITY, null, nowElapsed, now);
+ EconomicPolicy.REWARD_TOP_ACTIVITY, String.valueOf(event.mInstanceId),
+ nowElapsed, now);
break;
case UsageEvents.Event.USER_INTERACTION:
case UsageEvents.Event.CHOOSER_ACTION:
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
index a234ae6..f4917ad 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
@@ -138,6 +138,11 @@
dumpTime(pw, transaction.endTimeMs);
pw.print(": ");
pw.print(EconomicPolicy.eventToString(transaction.eventId));
+ if (transaction.tag != null) {
+ pw.print("(");
+ pw.print(transaction.tag);
+ pw.print(")");
+ }
pw.print(" --> ");
pw.println(narcToString(transaction.delta));
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 154c42f..ee44198 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17888,6 +17888,7 @@
field public static final int RGB_565 = 4; // 0x4
field public static final int RGB_888 = 3; // 0x3
field public static final int S_UI8 = 53; // 0x35
+ field public static final long USAGE_COMPOSER_OVERLAY = 2048L; // 0x800L
field public static final long USAGE_CPU_READ_OFTEN = 3L; // 0x3L
field public static final long USAGE_CPU_READ_RARELY = 2L; // 0x2L
field public static final long USAGE_CPU_WRITE_OFTEN = 48L; // 0x30L
@@ -49046,6 +49047,7 @@
method @NonNull public android.view.SurfaceControl build();
method @NonNull public android.view.SurfaceControl.Builder setBufferSize(@IntRange(from=0) int, @IntRange(from=0) int);
method @NonNull public android.view.SurfaceControl.Builder setFormat(int);
+ method @NonNull public android.view.SurfaceControl.Builder setHidden(boolean);
method @NonNull public android.view.SurfaceControl.Builder setName(@NonNull String);
method @NonNull public android.view.SurfaceControl.Builder setOpaque(boolean);
method @NonNull public android.view.SurfaceControl.Builder setParent(@Nullable android.view.SurfaceControl);
@@ -49060,11 +49062,18 @@
method @NonNull public android.view.SurfaceControl.Transaction merge(@NonNull android.view.SurfaceControl.Transaction);
method @NonNull public android.view.SurfaceControl.Transaction reparent(@NonNull android.view.SurfaceControl, @Nullable android.view.SurfaceControl);
method @NonNull public android.view.SurfaceControl.Transaction setAlpha(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0, to=1.0) float);
+ method @NonNull public android.view.SurfaceControl.Transaction setBuffer(@NonNull android.view.SurfaceControl, @Nullable android.hardware.HardwareBuffer);
method @NonNull public android.view.SurfaceControl.Transaction setBufferSize(@NonNull android.view.SurfaceControl, @IntRange(from=0) int, @IntRange(from=0) int);
+ method @NonNull public android.view.SurfaceControl.Transaction setBufferTransform(@NonNull android.view.SurfaceControl, int);
+ method @NonNull public android.view.SurfaceControl.Transaction setCrop(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect);
+ method @NonNull public android.view.SurfaceControl.Transaction setDamageRegion(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Region);
method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int);
method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int, int);
method @NonNull public android.view.SurfaceControl.Transaction setGeometry(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect, @Nullable android.graphics.Rect, int);
method @NonNull public android.view.SurfaceControl.Transaction setLayer(@NonNull android.view.SurfaceControl, @IntRange(from=java.lang.Integer.MIN_VALUE, to=java.lang.Integer.MAX_VALUE) int);
+ method @NonNull public android.view.SurfaceControl.Transaction setOpaque(@NonNull android.view.SurfaceControl, boolean);
+ method @NonNull public android.view.SurfaceControl.Transaction setPosition(@NonNull android.view.SurfaceControl, float, float);
+ method @NonNull public android.view.SurfaceControl.Transaction setScale(@NonNull android.view.SurfaceControl, float, float);
method @NonNull public android.view.SurfaceControl.Transaction setVisibility(@NonNull android.view.SurfaceControl, boolean);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.SurfaceControl.Transaction> CREATOR;
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index c9b8ba2..a33d0a2 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -70,6 +70,7 @@
field public static final String BIND_TRANSLATION_SERVICE = "android.permission.BIND_TRANSLATION_SERVICE";
field public static final String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT";
field public static final String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
+ field public static final String BLUETOOTH_MAP = "android.permission.BLUETOOTH_MAP";
field public static final String BRICK = "android.permission.BRICK";
field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
field public static final String BROADCAST_CLOSE_SYSTEM_DIALOGS = "android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS";
@@ -5875,9 +5876,11 @@
method public android.media.AudioTrack createAudioTrackSource(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException;
method public int detachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>);
method public int getFocusDuckingBehavior();
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<android.media.AudioFocusInfo> getFocusStack();
method public int getStatus();
method public boolean removeUidDeviceAffinity(int);
method public boolean removeUserIdDeviceAffinity(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean sendFocusLoss(@NonNull android.media.AudioFocusInfo) throws java.lang.IllegalStateException;
method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setRegistration(String);
method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>);
@@ -6447,6 +6450,7 @@
method public int flush();
method public long read(long);
method public long read(@NonNull byte[], long, long);
+ method public long seek(long);
method public void setFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
method public int start();
method public int stop();
@@ -6582,6 +6586,7 @@
public class DownloadEvent extends android.media.tv.tuner.filter.FilterEvent {
method public int getDataLength();
+ method public int getDownloadId();
method public int getItemFragmentIndex();
method public int getItemId();
method public int getLastItemFragmentIndex();
@@ -6591,11 +6596,13 @@
public class DownloadSettings extends android.media.tv.tuner.filter.Settings {
method @NonNull public static android.media.tv.tuner.filter.DownloadSettings.Builder builder(int);
method public int getDownloadId();
+ method public boolean useDownloadId();
}
public static class DownloadSettings.Builder {
method @NonNull public android.media.tv.tuner.filter.DownloadSettings build();
method @NonNull public android.media.tv.tuner.filter.DownloadSettings.Builder setDownloadId(int);
+ method @NonNull public android.media.tv.tuner.filter.DownloadSettings.Builder setUseDownloadId(boolean);
}
public class Filter implements java.lang.AutoCloseable {
@@ -6694,12 +6701,14 @@
method public long getAudioHandle();
method public long getAvDataId();
method public long getDataLength();
+ method public long getDts();
method @Nullable public android.media.tv.tuner.filter.AudioDescriptor getExtraMetaData();
method @Nullable public android.media.MediaCodec.LinearBlock getLinearBlock();
method @IntRange(from=0) public int getMpuSequenceNumber();
method public long getOffset();
method public long getPts();
method public int getStreamId();
+ method public boolean isDtsPresent();
method public boolean isPrivateData();
method public boolean isPtsPresent();
method public boolean isSecureMemory();
@@ -6809,7 +6818,8 @@
}
public class SectionEvent extends android.media.tv.tuner.filter.FilterEvent {
- method public int getDataLength();
+ method @Deprecated public int getDataLength();
+ method public long getDataLengthLong();
method public int getSectionNumber();
method public int getTableId();
method public int getVersion();
@@ -7541,6 +7551,7 @@
method public int getSignalStrength();
method public int getSnr();
method public int getSpectralInversion();
+ method @NonNull public int[] getStreamIdList();
method public int getSymbolRate();
method @IntRange(from=0, to=65535) public int getSystemId();
method public int getTransmissionMode();
@@ -7587,6 +7598,7 @@
field public static final int FRONTEND_STATUS_TYPE_SIGNAL_STRENGTH = 6; // 0x6
field public static final int FRONTEND_STATUS_TYPE_SNR = 1; // 0x1
field public static final int FRONTEND_STATUS_TYPE_SPECTRAL = 10; // 0xa
+ field public static final int FRONTEND_STATUS_TYPE_STREAM_ID_LIST = 39; // 0x27
field public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE = 7; // 0x7
field public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID = 29; // 0x1d
field public static final int FRONTEND_STATUS_TYPE_TRANSMISSION_MODE = 27; // 0x1b
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index 9cb9ddc..2c5acf1 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -60,14 +60,6 @@
}
-package android.bluetooth {
-
- public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
- method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean setPriority(android.bluetooth.BluetoothDevice, int);
- }
-
-}
-
package android.content {
public class Intent implements java.lang.Cloneable android.os.Parcelable {
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index a642696..3573a56 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -1394,6 +1394,12 @@
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will
* return a default value of {@code 1.0f}.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This legacy API gets the scale of full-screen
+ * magnification. To get the scale of the current controlling magnifier,
+ * use {@link #getMagnificationConfig} instead.
+ * </p>
*
* @return the current magnification scale
*/
@@ -1422,6 +1428,12 @@
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will
* return a default value of {@code 0.0f}.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This legacy API gets the center position of full-screen
+ * magnification. To get the magnification center of the current controlling magnifier,
+ * use {@link #getMagnificationConfig} instead.
+ * </p>
*
* @return the unscaled screen-relative X coordinate of the center of
* the magnified region
@@ -1451,6 +1463,12 @@
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will
* return a default value of {@code 0.0f}.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This legacy API gets the center position of full-screen
+ * magnification. To get the magnification center of the current controlling magnifier,
+ * use {@link #getMagnificationConfig} instead.
+ * </p>
*
* @return the unscaled screen-relative Y coordinate of the center of
* the magnified region
@@ -1571,6 +1589,11 @@
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will have
* no effect and return {@code false}.
+ * <p>
+ * <strong>Note:</strong> This legacy API sets the scale of full-screen
+ * magnification. To set the scale of the specified magnifier,
+ * use {@link #setMagnificationConfig} instead.
+ * </p>
*
* @param scale the magnification scale to set, must be >= 1 and <= 8
* @param animate {@code true} to animate from the current scale or
@@ -1602,6 +1625,12 @@
* {@link AccessibilityService#onServiceConnected()} has not yet been
* called) or the service has been disconnected, this method will have
* no effect and return {@code false}.
+ * </p>
+ * <p>
+ * <strong>Note:</strong> This legacy API sets the center of full-screen
+ * magnification. To set the center of the specified magnifier,
+ * use {@link #setMagnificationConfig} instead.
+ * </p>
*
* @param centerX the unscaled screen-relative X coordinate on which to
* center the viewport
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index af907af..779552f1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5808,6 +5808,7 @@
p, result);
buildCustomContentIntoTemplate(mContext, standard, customContent,
p, result);
+ makeHeaderExpanded(standard);
return standard;
}
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index c046324..8212eaa 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -601,49 +601,6 @@
}
/**
- * Set priority of the profile
- *
- * <p> The device should already be paired.
- * Priority can be one of {@link BluetoothProfile#PRIORITY_ON} or
- * {@link BluetoothProfile#PRIORITY_OFF}
- *
- * @param device Paired bluetooth device
- * @param priority
- * @return true if priority is set, false on error
- * @hide
- * @deprecated Replaced with {@link #setConnectionPolicy(BluetoothDevice, int)}
- * @removed
- */
- @Deprecated
- @SystemApi
- @RequiresLegacyBluetoothAdminPermission
- @RequiresBluetoothConnectPermission
- @RequiresPermission(allOf = {
- android.Manifest.permission.BLUETOOTH_CONNECT,
- android.Manifest.permission.MODIFY_PHONE_STATE,
- })
- public boolean setPriority(BluetoothDevice device, int priority) {
- if (DBG) log("setPriority(" + device + ", " + priority + ")");
- final IBluetoothHeadset service = mService;
- if (service != null && isEnabled() && isValidDevice(device)) {
- if (priority != BluetoothProfile.PRIORITY_OFF
- && priority != BluetoothProfile.PRIORITY_ON) {
- return false;
- }
- try {
- return service.setPriority(
- device, BluetoothAdapter.priorityToConnectionPolicy(priority),
- mAttributionSource);
- } catch (RemoteException e) {
- Log.e(TAG, Log.getStackTraceString(new Throwable()));
- return false;
- }
- }
- if (service == null) Log.w(TAG, "Proxy not attached to service");
- return false;
- }
-
- /**
* Set connection policy of the profile
*
* <p> The device should already be paired.
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 24c6a5a..bfd9fd0 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -792,6 +792,21 @@
}
/**
+ * To get the parent theme resource id according to the parameter theme resource id.
+ * @param resId theme resource id.
+ * @return the parent theme resource id.
+ * @hide
+ */
+ @StyleRes
+ int getParentThemeIdentifier(@StyleRes int resId) {
+ synchronized (this) {
+ ensureValidLocked();
+ // name is checked in JNI.
+ return nativeGetParentThemeIdentifier(mObject, resId);
+ }
+ }
+
+ /**
* Enable resource resolution logging to track the steps taken to resolve the last resource
* entry retrieved. Stores the configuration and package names for each step.
*
@@ -1600,6 +1615,8 @@
private static native void nativeThemeDump(long ptr, long themePtr, int priority, String tag,
String prefix);
static native @NativeConfig int nativeThemeGetChangingConfigurations(long themePtr);
+ @StyleRes
+ private static native int nativeGetParentThemeIdentifier(long ptr, @StyleRes int styleId);
// AssetInputStream related native methods.
private static native void nativeAssetDestroy(long assetPtr);
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index b5d4f09..cb53a2a 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1508,6 +1508,12 @@
* retrieve XML attributes with style and theme information applied.
*/
public final class Theme {
+ /**
+ * To trace parent themes needs to prevent a cycle situation.
+ * e.x. A's parent is B, B's parent is C, and C's parent is A.
+ */
+ private static final int MAX_NUMBER_OF_TRACING_PARENT_THEME = 100;
+
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -1801,6 +1807,13 @@
}
}
+ @StyleRes
+ /*package*/ int getParentThemeIdentifier(@StyleRes int resId) {
+ synchronized (mLock) {
+ return mThemeImpl.getParentThemeIdentifier(resId);
+ }
+ }
+
/**
* @hide
*/
@@ -1948,8 +1961,27 @@
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append('{');
- sb.append("id=0x").append(Integer.toHexString(getAppliedStyleResId())).append(", ");
- sb.append("themes=").append(Arrays.deepToString(getTheme()));
+ int themeResId = getAppliedStyleResId();
+ int i = 0;
+ sb.append("InheritanceMap=[");
+ while (themeResId > 0) {
+ if (i > MAX_NUMBER_OF_TRACING_PARENT_THEME) {
+ sb.append(",...");
+ break;
+ }
+
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append("id=0x").append(Integer.toHexString(themeResId));
+ sb.append(getResourcePackageName(themeResId))
+ .append(":").append(getResourceTypeName(themeResId))
+ .append("/").append(getResourceEntryName(themeResId));
+
+ i++;
+ themeResId = getParentThemeIdentifier(themeResId);
+ }
+ sb.append("], Themes=").append(Arrays.deepToString(getTheme()));
sb.append('}');
return sb.toString();
}
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index b9f93b8..4d850b0c 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -1310,6 +1310,14 @@
return mThemeResId;
}
+ @StyleRes
+ /*package*/ int getParentThemeIdentifier(@StyleRes int resId) {
+ if (resId > 0) {
+ return mAssets.getParentThemeIdentifier(resId);
+ }
+ return 0;
+ }
+
void applyStyle(int resId, boolean force) {
mAssets.applyStyleToTheme(mTheme, resId, force);
mThemeResId = resId;
diff --git a/core/java/android/hardware/HardwareBuffer.java b/core/java/android/hardware/HardwareBuffer.java
index cad30dd..a4a8f31 100644
--- a/core/java/android/hardware/HardwareBuffer.java
+++ b/core/java/android/hardware/HardwareBuffer.java
@@ -25,6 +25,7 @@
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
+import android.view.SurfaceControl;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
@@ -129,6 +130,15 @@
public static final long USAGE_GPU_SAMPLED_IMAGE = 1 << 8;
/** Usage: The buffer will be written to by the GPU */
public static final long USAGE_GPU_COLOR_OUTPUT = 1 << 9;
+ /**
+ * The buffer will be used as a composer HAL overlay layer.
+ *
+ * This flag is currently only needed when using
+ * {@link android.view.SurfaceControl.Transaction#setBuffer(SurfaceControl, HardwareBuffer)}
+ * to set a buffer. In all other cases, the framework adds this flag
+ * internally to buffers that could be presented in a composer overlay.
+ */
+ public static final long USAGE_COMPOSER_OVERLAY = 1 << 11;
/** Usage: The buffer must not be used outside of a protected hardware path */
public static final long USAGE_PROTECTED_CONTENT = 1 << 14;
/** Usage: The buffer will be read by a hardware video encoder */
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 8292f26..aa4b83a 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -192,14 +192,15 @@
// We only want to use ANGLE if the developer has explicitly chosen something other than
// default driver.
- final boolean requested = devOptIn.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE);
- if (requested) {
+ final boolean forceAngle = devOptIn.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE);
+ final boolean forceNative = devOptIn.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE);
+ if (forceAngle || forceNative) {
Log.v(TAG, "ANGLE developer option for " + packageName + ": " + devOptIn);
}
final boolean gameModeEnabledAngle = isAngleEnabledByGameMode(context, packageName);
- return requested || gameModeEnabledAngle;
+ return !forceNative && (forceAngle || gameModeEnabledAngle);
}
private int getVulkanVersion(PackageManager pm) {
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 83a6bc0..73ffd66 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -44,7 +44,6 @@
import android.graphics.BLASTBufferQueue;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
import android.graphics.PixelFormat;
import android.graphics.Point;
@@ -1930,9 +1929,7 @@
mScreenshotSize.set(mSurfaceSize.x, mSurfaceSize.y);
- GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(hardwareBuffer);
-
- t.setBuffer(mScreenshotSurfaceControl, graphicBuffer);
+ t.setBuffer(mScreenshotSurfaceControl, hardwareBuffer);
t.setColorSpace(mScreenshotSurfaceControl, screenshotBuffer.getColorSpace());
// Place on top everything else.
t.setLayer(mScreenshotSurfaceControl, Integer.MAX_VALUE);
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 6984e4d..4789231 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -874,6 +874,18 @@
? Math.max(fmDescent, Math.round(descents[breakIndex]))
: fmDescent;
+ // The fallback ascent/descent may be larger than top/bottom of the default font
+ // metrics. Adjust top/bottom with ascent/descent for avoiding unexpected
+ // clipping.
+ if (fallbackLineSpacing) {
+ if (ascent < fmTop) {
+ fmTop = ascent;
+ }
+ if (descent > fmBottom) {
+ fmBottom = descent;
+ }
+ }
+
v = out(source, here, endPos,
ascent, descent, fmTop, fmBottom,
v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 7e2792c..8124510 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -58,6 +58,10 @@
*/
public static final String SETTINGS_APP_LANGUAGE_SELECTION = "settings_app_language_selection";
+ /** @hide */
+ public static final String SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS =
+ "settings_enable_monitor_phantom_procs";
+
private static final Map<String, String> DEFAULT_FLAGS;
static {
@@ -81,6 +85,7 @@
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SECURITY_HUB, "true");
DEFAULT_FLAGS.put(SETTINGS_SUPPORT_LARGE_SCREEN, "true");
DEFAULT_FLAGS.put(SETTINGS_APP_LANGUAGE_SELECTION, "false");
+ DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
}
private static final Set<String> PERSISTENT_FLAGS;
@@ -89,6 +94,7 @@
PERSISTENT_FLAGS.add(SETTINGS_APP_LANGUAGE_SELECTION);
PERSISTENT_FLAGS.add(SETTINGS_PROVIDER_MODEL);
PERSISTENT_FLAGS.add(SETTINGS_SUPPORT_LARGE_SCREEN);
+ PERSISTENT_FLAGS.add(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
}
/**
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index af7d86c..3b52709 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -120,6 +120,8 @@
long relativeToObject, int zorder);
private static native void nativeSetPosition(long transactionObj, long nativeObject,
float x, float y);
+ private static native void nativeSetScale(long transactionObj, long nativeObject,
+ float x, float y);
private static native void nativeSetSize(long transactionObj, long nativeObject, int w, int h);
private static native void nativeSetTransparentRegionHint(long transactionObj,
long nativeObject, Region region);
@@ -202,9 +204,13 @@
private static native void nativeReparent(long transactionObj, long nativeObject,
long newParentNativeObject);
private static native void nativeSetBuffer(long transactionObj, long nativeObject,
- GraphicBuffer buffer);
+ HardwareBuffer buffer);
+ private static native void nativeSetBufferTransform(long transactionObj, long nativeObject,
+ int transform);
private static native void nativeSetColorSpace(long transactionObj, long nativeObject,
int colorSpace);
+ private static native void nativeSetDamageRegion(long transactionObj, long nativeObject,
+ Region region);
private static native void nativeOverrideHdrTypes(IBinder displayToken, int[] modes);
@@ -1333,7 +1339,6 @@
* Set the initial visibility for the SurfaceControl.
*
* @param hidden Whether the Surface is initially HIDDEN.
- * @hide
*/
@NonNull
public Builder setHidden(boolean hidden) {
@@ -2840,16 +2845,38 @@
}
/**
- * @hide
+ * Sets the SurfaceControl to the specified position relative to the parent
+ * SurfaceControl
+ *
+ * @param sc The SurfaceControl to change position
+ * @param x the X position
+ * @param y the Y position
+ * @return this transaction
*/
- @UnsupportedAppUsage
- public Transaction setPosition(SurfaceControl sc, float x, float y) {
+ @NonNull
+ public Transaction setPosition(@NonNull SurfaceControl sc, float x, float y) {
checkPreconditions(sc);
nativeSetPosition(mNativeObject, sc.mNativeObject, x, y);
return this;
}
/**
+ * Sets the SurfaceControl to the specified scale with (0, 0) as the center point
+ * of the scale.
+ *
+ * @param sc The SurfaceControl to change scale
+ * @param scaleX the X scale
+ * @param scaleY the Y scale
+ * @return this transaction
+ */
+ @NonNull
+ public Transaction setScale(@NonNull SurfaceControl sc, float scaleX, float scaleY) {
+ checkPreconditions(sc);
+ nativeSetScale(mNativeObject, sc.mNativeObject, scaleX, scaleY);
+ return this;
+ }
+
+ /**
* Set the default buffer size for the SurfaceControl, if there is a
* {@link Surface} associated with the control, then
* this will be the default size for buffers dequeued from it.
@@ -3056,7 +3083,9 @@
* @param sc SurfaceControl to set crop of.
* @param crop Bounds of the crop to apply.
* @hide
+ * @deprecated Use {@link #setCrop(SurfaceControl, Rect)} instead.
*/
+ @Deprecated
@UnsupportedAppUsage
public Transaction setWindowCrop(SurfaceControl sc, Rect crop) {
checkPreconditions(sc);
@@ -3071,6 +3100,28 @@
}
/**
+ * Bounds the surface and its children to the bounds specified. Size of the surface will be
+ * ignored and only the crop and buffer size will be used to determine the bounds of the
+ * surface. If no crop is specified and the surface has no buffer, the surface bounds is
+ * only constrained by the size of its parent bounds.
+ *
+ * @param sc SurfaceControl to set crop of.
+ * @param crop Bounds of the crop to apply.
+ * @return this This transaction for chaining
+ */
+ public @NonNull Transaction setCrop(@NonNull SurfaceControl sc, @Nullable Rect crop) {
+ checkPreconditions(sc);
+ if (crop != null) {
+ nativeSetWindowCrop(mNativeObject, sc.mNativeObject,
+ crop.left, crop.top, crop.right, crop.bottom);
+ } else {
+ nativeSetWindowCrop(mNativeObject, sc.mNativeObject, 0, 0, 0, 0);
+ }
+
+ return this;
+ }
+
+ /**
* Same as {@link Transaction#setWindowCrop(SurfaceControl, Rect)} but sets the crop rect
* top left at 0, 0.
*
@@ -3215,11 +3266,34 @@
}
/**
- * Sets the opacity of the surface. Setting the flag is equivalent to creating the
- * Surface with the {@link #OPAQUE} flag.
- * @hide
+ * Indicates whether the surface must be considered opaque, even if its pixel format is
+ * set to translucent. This can be useful if an application needs full RGBA 8888 support
+ * for instance but will still draw every pixel opaque.
+ * <p>
+ * This flag only determines whether opacity will be sampled from the alpha channel.
+ * Plane-alpha from calls to setAlpha() can still result in blended composition
+ * regardless of the opaque setting.
+ *
+ * Combined effects are (assuming a buffer format with an alpha channel):
+ * <ul>
+ * <li>OPAQUE + alpha(1.0) == opaque composition
+ * <li>OPAQUE + alpha(0.x) == blended composition
+ * <li>OPAQUE + alpha(0.0) == no composition
+ * <li>!OPAQUE + alpha(1.0) == blended composition
+ * <li>!OPAQUE + alpha(0.x) == blended composition
+ * <li>!OPAQUE + alpha(0.0) == no composition
+ * </ul>
+ * If the underlying buffer lacks an alpha channel, it is as if setOpaque(true)
+ * were set automatically.
+ *
+ * @see Builder#setOpaque(boolean)
+ *
+ * @param sc The SurfaceControl to update
+ * @param isOpaque true if the buffer's alpha should be ignored, false otherwise
+ * @return this
*/
- public Transaction setOpaque(SurfaceControl sc, boolean isOpaque) {
+ @NonNull
+ public Transaction setOpaque(@NonNull SurfaceControl sc, boolean isOpaque) {
checkPreconditions(sc);
if (isOpaque) {
nativeSetFlags(mNativeObject, sc.mNativeObject, SURFACE_OPAQUE, SURFACE_OPAQUE);
@@ -3498,14 +3572,57 @@
* created as type {@link #FX_SURFACE_BLAST}
*
* @hide
+ * @deprecated Use {@link #setBuffer(SurfaceControl, HardwareBuffer)} instead
*/
+ @Deprecated
public Transaction setBuffer(SurfaceControl sc, GraphicBuffer buffer) {
+ return setBuffer(sc, HardwareBuffer.createFromGraphicBuffer(buffer));
+ }
+
+ /**
+ * Updates the HardwareBuffer displayed for the SurfaceControl.
+ *
+ * Note that the buffer must be allocated with {@link HardwareBuffer#USAGE_COMPOSER_OVERLAY}
+ * as well as {@link HardwareBuffer#USAGE_GPU_SAMPLED_IMAGE} as the surface control might
+ * be composited using either an overlay or using the GPU.
+ *
+ * @param sc The SurfaceControl to update
+ * @param buffer The buffer to be displayed
+ * @return this
+ */
+ public @NonNull Transaction setBuffer(@NonNull SurfaceControl sc,
+ @Nullable HardwareBuffer buffer) {
checkPreconditions(sc);
nativeSetBuffer(mNativeObject, sc.mNativeObject, buffer);
return this;
}
/**
+ * Sets the buffer transform that should be applied to the current buffer.
+ *
+ * @param sc The SurfaceControl to update
+ * @param transform The transform to apply to the buffer.
+ * @return this
+ */
+ public @NonNull Transaction setBufferTransform(@NonNull SurfaceControl sc,
+ /* TODO: Mark the intdef */ int transform) {
+ checkPreconditions(sc);
+ nativeSetBufferTransform(mNativeObject, sc.mNativeObject, transform);
+ return this;
+ }
+
+ /**
+ * Updates the region for the content on this surface updated in this transaction.
+ *
+ * If unspecified, the complete surface is assumed to be damaged.
+ */
+ public @NonNull Transaction setDamageRegion(@NonNull SurfaceControl sc,
+ @Nullable Region region) {
+ nativeSetDamageRegion(mNativeObject, sc.mNativeObject, region);
+ return this;
+ }
+
+ /**
* Set the color space for the SurfaceControl. The supported color spaces are SRGB
* and Display P3, other color spaces will be treated as SRGB. This can only be used for
* SurfaceControls that were created as type {@link #FX_SURFACE_BLAST}
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 3b15db2..b85fe7c 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -836,20 +836,25 @@
boolean beginBatchEdit();
/**
- * Tell the editor that you are done with a batch edit previously
- * initiated with {@link #beginBatchEdit}. This ends the latest
- * batch only.
+ * Tell the editor that you are done with a batch edit previously initiated with
+ * {@link #beginBatchEdit()}. This ends the latest batch only.
*
- * <p><strong>IME authors:</strong> make sure you call this
- * exactly once for each call to {@link #beginBatchEdit}.</p>
+ * <p><strong>IME authors:</strong> make sure you call this exactly once for each call to
+ * {@link #beginBatchEdit()}.</p>
*
- * <p><strong>Editor authors:</strong> please be careful about
- * batch edit nesting. Updates still to be held back until the end
- * of the last batch edit.</p>
+ * <p><strong>Editor authors:</strong> please be careful about batch edit nesting. Updates still
+ * to be held back until the end of the last batch edit. In case you are delegating this API
+ * call to the one obtained from
+ * {@link android.widget.EditText#onCreateInputConnection(EditorInfo)}, there was an off-by-one
+ * that had returned {@code true} when its nested batch edit count becomes {@code 0} as a result
+ * of invoking this API. This bug is fixed in {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+ * </p>
*
- * @return true if there is still a batch edit in progress after closing
- * the latest one (in other words, if the nesting count is > 0), false
- * otherwise or if the input connection is no longer valid.
+ * @return For editor authors, you must return {@code true} if a batch edit is still in progress
+ * after closing the latest one (in other words, if the nesting count is still a
+ * positive number). Return {@code false} otherwise. For IME authors, you will
+ * always receive {@code true} as long as the request was sent to the editor, and
+ * receive {@code false} only if the input connection is no longer valid.
*/
boolean endBatchEdit();
diff --git a/core/java/com/android/internal/inputmethod/EditableInputConnection.java b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
index 410e68b..29bb311 100644
--- a/core/java/com/android/internal/inputmethod/EditableInputConnection.java
+++ b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
@@ -89,7 +89,7 @@
// contribution to mTextView's nested batch edit count is zero.
mTextView.endBatchEdit();
mBatchEditNesting--;
- return true;
+ return mBatchEditNesting > 0;
}
}
return false;
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 86d7810..8c23b21 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -881,6 +881,12 @@
return static_cast<jint>(bag->entry_count);
}
+static jint NativeGetParentThemeIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) {
+ ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ const auto parentThemeResId = assetmanager->GetParentThemeResourceId(resid);
+ return parentThemeResId.value_or(0);
+}
+
static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring name,
jstring def_type, jstring def_package) {
ScopedUtfChars name_utf8(env, name);
@@ -1464,6 +1470,8 @@
{"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray},
{"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize},
{"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray},
+ {"nativeGetParentThemeIdentifier", "(JI)I",
+ (void*)NativeGetParentThemeIdentifier},
// AssetManager resource name/ID methods.
{"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index fb5fded..67d0c52 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -597,6 +597,14 @@
transaction->setPosition(ctrl, x, y);
}
+static void nativeSetScale(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
+ jfloat xScale, jfloat yScale) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ transaction->setMatrix(ctrl, xScale, 0, 0, yScale);
+}
+
static void nativeSetGeometry(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
jobject sourceObj, jobject dstObj, jlong orientation) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -620,9 +628,19 @@
jobject bufferObject) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
- sp<GraphicBuffer> buffer(
- android_graphics_GraphicBuffer_getNativeGraphicsBuffer(env, bufferObject));
- transaction->setBuffer(ctrl, buffer);
+ sp<GraphicBuffer> graphicBuffer(GraphicBuffer::fromAHardwareBuffer(
+ android_hardware_HardwareBuffer_getNativeHardwareBuffer(env, bufferObject)));
+ transaction->setBuffer(ctrl, graphicBuffer);
+}
+
+static void nativeSetBufferTransform(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, jint transform) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ transaction->setTransform(ctrl, transform);
+ bool transformToInverseDisplay = (NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY & transform) ==
+ NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
+ transaction->setTransformToDisplayInverse(ctrl, transformToInverseDisplay);
}
static void nativeSetColorSpace(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject,
@@ -740,6 +758,37 @@
}
}
+static void nativeSetDamageRegion(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, jobject regionObj) {
+ SurfaceControl* const surfaceControl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+ if (regionObj == nullptr) {
+ transaction->setSurfaceDamageRegion(surfaceControl, Region::INVALID_REGION);
+ return;
+ }
+
+ graphics::RegionIterator iterator(env, regionObj);
+ if (!iterator.isValid()) {
+ transaction->setSurfaceDamageRegion(surfaceControl, Region::INVALID_REGION);
+ return;
+ }
+
+ Region region;
+ while (!iterator.isDone()) {
+ ARect rect = iterator.getRect();
+ region.orSelf(static_cast<const Rect&>(rect));
+ iterator.next();
+ }
+
+ if (region.getBounds().isEmpty()) {
+ transaction->setSurfaceDamageRegion(surfaceControl, Region::INVALID_REGION);
+ return;
+ }
+
+ transaction->setSurfaceDamageRegion(surfaceControl, region);
+}
+
static void nativeSetAlpha(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jfloat alpha) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -1905,10 +1954,14 @@
(void*)nativeSetRelativeLayer },
{"nativeSetPosition", "(JJFF)V",
(void*)nativeSetPosition },
+ {"nativeSetScale", "(JJFF)V",
+ (void*)nativeSetScale },
{"nativeSetSize", "(JJII)V",
(void*)nativeSetSize },
{"nativeSetTransparentRegionHint", "(JJLandroid/graphics/Region;)V",
(void*)nativeSetTransparentRegionHint },
+ { "nativeSetDamageRegion", "(JJLandroid/graphics/Region;)V",
+ (void*)nativeSetDamageRegion },
{"nativeSetAlpha", "(JJF)V",
(void*)nativeSetAlpha },
{"nativeSetColor", "(JJ[F)V",
@@ -2018,8 +2071,9 @@
(void*)nativeGetDisplayedContentSample },
{"nativeSetGeometry", "(JJLandroid/graphics/Rect;Landroid/graphics/Rect;J)V",
(void*)nativeSetGeometry },
- {"nativeSetBuffer", "(JJLandroid/graphics/GraphicBuffer;)V",
+ {"nativeSetBuffer", "(JJLandroid/hardware/HardwareBuffer;)V",
(void*)nativeSetBuffer },
+ {"nativeSetBufferTransform", "(JJI)V", (void*) nativeSetBufferTransform},
{"nativeSetColorSpace", "(JJI)V",
(void*)nativeSetColorSpace },
{"nativeSyncInputWindows", "(J)V",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4c36732..0894877 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2066,7 +2066,7 @@
<permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
android:protectionLevel="signature|privileged" />
- <!-- Control access to email providers exclusively for Bluetooth
+ <!-- @SystemApi Control access to email providers exclusively for Bluetooth
@hide
-->
<permission android:name="android.permission.BLUETOOTH_MAP"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
index d92e2cc..686fbbf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BadgedImageView.java
@@ -15,23 +15,22 @@
*/
package com.android.wm.shell.bubbles;
-import static android.graphics.Paint.ANTI_ALIAS_FLAG;
-import static android.graphics.Paint.DITHER_FLAG;
-import static android.graphics.Paint.FILTER_BITMAP_FLAG;
-
+import android.annotation.DrawableRes;
import android.annotation.Nullable;
import android.content.Context;
+import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.PathParser;
+import android.view.Gravity;
import android.view.View;
import android.view.ViewOutlineProvider;
+import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.launcher3.icons.DotRenderer;
@@ -47,7 +46,7 @@
* Badge = the icon associated with the app that created this bubble, this will show work profile
* badge if appropriate.
*/
-public class BadgedImageView extends ImageView {
+public class BadgedImageView extends FrameLayout {
/** Same value as Launcher3 dot code */
public static final float WHITE_SCRIM_ALPHA = 0.54f;
@@ -74,6 +73,9 @@
private final EnumSet<SuppressionFlag> mDotSuppressionFlags =
EnumSet.of(SuppressionFlag.FLYOUT_VISIBLE);
+ private final ImageView mBubbleIcon;
+ private final ImageView mAppIcon;
+
private float mDotScale = 0f;
private float mAnimatingToDotScale = 0f;
private boolean mDotIsAnimating = false;
@@ -86,7 +88,6 @@
private DotRenderer.DrawParams mDrawParams;
private int mDotColor;
- private Paint mPaint = new Paint(ANTI_ALIAS_FLAG);
private Rect mTempBounds = new Rect();
public BadgedImageView(Context context) {
@@ -104,6 +105,17 @@
public BadgedImageView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
+
+ mBubbleIcon = new ImageView(context);
+ addView(mBubbleIcon);
+ mAppIcon = new ImageView(context);
+ addView(mAppIcon);
+
+ final TypedArray ta = mContext.obtainStyledAttributes(attrs, new int[]{android.R.attr.src},
+ defStyleAttr, defStyleRes);
+ mBubbleIcon.setImageResource(ta.getResourceId(0, 0));
+ ta.recycle();
+
mDrawParams = new DotRenderer.DrawParams();
setFocusable(true);
@@ -135,7 +147,6 @@
public void showDotAndBadge(boolean onLeft) {
removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.BEHIND_STACK);
animateDotBadgePositions(onLeft);
-
}
public void hideDotAndBadge(boolean onLeft) {
@@ -149,6 +160,7 @@
*/
public void setRenderedBubble(BubbleViewProvider bubble) {
mBubble = bubble;
+ mBubbleIcon.setImageBitmap(bubble.getBubbleIcon());
if (mDotSuppressionFlags.contains(SuppressionFlag.BEHIND_STACK)) {
hideBadge();
} else {
@@ -176,6 +188,20 @@
mDotRenderer.draw(canvas, mDrawParams);
}
+ /**
+ * Set drawable resource shown as the icon
+ */
+ public void setIconImageResource(@DrawableRes int drawable) {
+ mBubbleIcon.setImageResource(drawable);
+ }
+
+ /**
+ * Get icon drawable
+ */
+ public Drawable getIconDrawable() {
+ return mBubbleIcon.getDrawable();
+ }
+
/** Adds a dot suppression flag, updating dot visibility if needed. */
void addDotSuppressionFlag(SuppressionFlag flag) {
if (mDotSuppressionFlags.add(flag)) {
@@ -279,7 +305,6 @@
showBadge();
}
-
/** Whether to draw the dot in onDraw(). */
private boolean shouldDrawDot() {
// Always render the dot if it's animating, since it could be animating out. Otherwise, show
@@ -325,29 +350,28 @@
void showBadge() {
Bitmap badge = mBubble.getAppBadge();
if (badge == null) {
- setImageBitmap(mBubble.getBubbleIcon());
+ mAppIcon.setVisibility(GONE);
return;
}
- Canvas bubbleCanvas = new Canvas();
- Bitmap noBadgeBubble = mBubble.getBubbleIcon();
- Bitmap bubble = noBadgeBubble.copy(noBadgeBubble.getConfig(), /* isMutable */ true);
- bubbleCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG));
- bubbleCanvas.setBitmap(bubble);
- final int bubbleSize = bubble.getWidth();
+ final int bubbleSize = mBubble.getBubbleIcon().getWidth();
final int badgeSize = (int) (ICON_BADGE_SCALE * bubbleSize);
- Rect dest = new Rect();
+
+ FrameLayout.LayoutParams appIconParams = (LayoutParams) mAppIcon.getLayoutParams();
+ appIconParams.height = badgeSize;
+ appIconParams.width = badgeSize;
if (mOnLeft) {
- dest.set(0, bubbleSize - badgeSize, badgeSize, bubbleSize);
+ appIconParams.gravity = Gravity.BOTTOM | Gravity.LEFT;
} else {
- dest.set(bubbleSize - badgeSize, bubbleSize - badgeSize, bubbleSize, bubbleSize);
+ appIconParams.gravity = Gravity.BOTTOM | Gravity.RIGHT;
}
- bubbleCanvas.drawBitmap(badge, null /* src */, dest, mPaint);
- bubbleCanvas.setBitmap(null);
- setImageBitmap(bubble);
+ mAppIcon.setLayoutParams(appIconParams);
+
+ mAppIcon.setImageBitmap(badge);
+ mAppIcon.setVisibility(VISIBLE);
}
void hideBadge() {
- setImageBitmap(mBubble.getBubbleIcon());
+ mAppIcon.setVisibility(GONE);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
index 519a856..cd635c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java
@@ -328,6 +328,7 @@
if (prevBubble == null) {
// Create a new bubble
bubble.setSuppressFlyout(suppressFlyout);
+ bubble.markUpdatedAt(mTimeSource.currentTimeMillis());
doAdd(bubble);
trim();
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index 5161092..a175929 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -66,7 +66,7 @@
updateResources()
getExpandedView()?.applyThemeAttrs()
// Apply inset and new style to fresh icon drawable.
- getIconView()?.setImageResource(R.drawable.bubble_ic_overflow_button)
+ getIconView()?.setIconImageResource(R.drawable.bubble_ic_overflow_button)
updateBtnTheme()
}
@@ -89,19 +89,19 @@
dotColor = colorAccent
val shapeColor = res.getColor(android.R.color.system_accent1_1000)
- overflowBtn?.drawable?.setTint(shapeColor)
+ overflowBtn?.iconDrawable?.setTint(shapeColor)
val iconFactory = BubbleIconFactory(context)
// Update bitmap
- val fg = InsetDrawable(overflowBtn?.drawable, overflowIconInset)
+ val fg = InsetDrawable(overflowBtn?.iconDrawable, overflowIconInset)
bitmap = iconFactory.createBadgedIconBitmap(
AdaptiveIconDrawable(ColorDrawable(colorAccent), fg)).icon
// Update dot path
dotPath = PathParser.createPathFromPathData(
res.getString(com.android.internal.R.string.config_icon_mask))
- val scale = iconFactory.normalizer.getScale(iconView!!.drawable,
+ val scale = iconFactory.normalizer.getScale(iconView!!.iconDrawable,
null /* outBounds */, null /* path */, null /* outMaskShape */)
val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f
val matrix = Matrix()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
index eea6e3c..c4bd73b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.common;
-import android.graphics.GraphicBuffer;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.view.SurfaceControl;
@@ -63,8 +62,6 @@
if (buffer == null || buffer.getHardwareBuffer() == null) {
return;
}
- final GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
- buffer.getHardwareBuffer());
mScreenshot = new SurfaceControl.Builder()
.setName("ScreenshotUtils screenshot")
.setFormat(PixelFormat.TRANSLUCENT)
@@ -73,7 +70,7 @@
.setBLASTLayer()
.build();
- mTransaction.setBuffer(mScreenshot, graphicBuffer);
+ mTransaction.setBuffer(mScreenshot, buffer.getHardwareBuffer());
mTransaction.setColorSpace(mScreenshot, buffer.getColorSpace());
mTransaction.reparent(mScreenshot, mSurfaceControl);
mTransaction.setLayer(mScreenshot, mLayer);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 040fffae..b8ac87f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -93,7 +93,7 @@
private final InsetsState mInsetsState = new InsetsState();
private Context mContext;
- private DividerSnapAlgorithm mDividerSnapAlgorithm;
+ @VisibleForTesting DividerSnapAlgorithm mDividerSnapAlgorithm;
private WindowContainerToken mWinToken1;
private WindowContainerToken mWinToken2;
private int mDividePosition;
@@ -294,20 +294,22 @@
mSplitLayoutHandler.onLayoutSizeChanging(this);
}
- void setDividePosition(int position) {
+ void setDividePosition(int position, boolean applyLayoutChange) {
mDividePosition = position;
updateBounds(mDividePosition);
- mSplitLayoutHandler.onLayoutSizeChanged(this);
+ if (applyLayoutChange) {
+ mSplitLayoutHandler.onLayoutSizeChanged(this);
+ }
}
- /** Sets divide position base on the ratio within root bounds. */
+ /** Updates divide position and split bounds base on the ratio within root bounds. */
public void setDivideRatio(float ratio) {
final int position = isLandscape()
? mRootBounds.left + (int) (mRootBounds.width() * ratio)
: mRootBounds.top + (int) (mRootBounds.height() * ratio);
- DividerSnapAlgorithm.SnapTarget snapTarget =
+ final DividerSnapAlgorithm.SnapTarget snapTarget =
mDividerSnapAlgorithm.calculateNonDismissingSnapTarget(position);
- setDividePosition(snapTarget.position);
+ setDividePosition(snapTarget.position, false /* applyLayoutChange */);
}
/** Resets divider position. */
@@ -336,7 +338,7 @@
break;
default:
flingDividePosition(currentPosition, snapTarget.position,
- () -> setDividePosition(snapTarget.position));
+ () -> setDividePosition(snapTarget.position, true /* applyLayoutChange */));
break;
}
}
@@ -389,7 +391,7 @@
@Override
public void onAnimationCancel(Animator animation) {
- setDividePosition(to);
+ setDividePosition(to, true /* applyLayoutChange */);
}
});
animator.start();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index ed5de0d..cdaa54c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -342,12 +342,12 @@
sideOptions = sideOptions != null ? sideOptions : new Bundle();
setSideStagePosition(sidePosition, wct);
+ mSplitLayout.setDivideRatio(splitRatio);
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
mSideStage.setBounds(getSideStageBounds(), wct);
- mSplitLayout.setDivideRatio(splitRatio);
// Make sure the launch options will put tasks in the corresponding split roots
addActivityOptions(mainOptions, mMainStage);
addActivityOptions(sideOptions, mSideStage);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index 6643ca1..4ecc0b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -380,9 +380,7 @@
}
private void drawSizeMatchSnapshot() {
- GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
- mSnapshot.getHardwareBuffer());
- mTransaction.setBuffer(mSurfaceControl, graphicBuffer)
+ mTransaction.setBuffer(mSurfaceControl, mSnapshot.getHardwareBuffer())
.setColorSpace(mSurfaceControl, mSnapshot.getColorSpace())
.apply();
}
@@ -428,20 +426,20 @@
// Scale the mismatch dimensions to fill the task bounds
mSnapshotMatrix.setRectToRect(mTmpSnapshotSize, mTmpDstFrame, Matrix.ScaleToFit.FILL);
mTransaction.setMatrix(childSurfaceControl, mSnapshotMatrix, mTmpFloat9);
- GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(
- mSnapshot.getHardwareBuffer());
mTransaction.setColorSpace(childSurfaceControl, mSnapshot.getColorSpace());
- mTransaction.setBuffer(childSurfaceControl, graphicBuffer);
+ mTransaction.setBuffer(childSurfaceControl, mSnapshot.getHardwareBuffer());
if (aspectRatioMismatch) {
GraphicBuffer background = GraphicBuffer.create(mFrame.width(), mFrame.height(),
PixelFormat.RGBA_8888,
GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
| GraphicBuffer.USAGE_SW_WRITE_RARELY);
+ // TODO: Support this on HardwareBuffer
final Canvas c = background.lockCanvas();
drawBackgroundAndBars(c, frame);
background.unlockCanvasAndPost(c);
- mTransaction.setBuffer(mSurfaceControl, background);
+ mTransaction.setBuffer(mSurfaceControl,
+ HardwareBuffer.createFromGraphicBuffer(background));
}
mTransaction.apply();
childSurfaceControl.release();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
index bc701d0..8bc1223 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java
@@ -39,6 +39,7 @@
import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.Log;
import android.util.Pair;
import android.view.WindowManager;
@@ -913,6 +914,31 @@
assertSelectionChangedTo(mBubbleA2);
}
+ /**
+ * - have a maxed out bubble stack & all of the bubbles have been recently accessed
+ * - bubble a notification that was posted before any of those bubbles were accessed
+ * => that bubble should be added
+ *
+ */
+ @Test
+ public void test_addOldNotifWithNewerBubbles() {
+ sendUpdatedEntryAtTime(mEntryA1, 2000);
+ sendUpdatedEntryAtTime(mEntryA2, 3000);
+ sendUpdatedEntryAtTime(mEntryA3, 4000);
+ sendUpdatedEntryAtTime(mEntryB1, 5000);
+ sendUpdatedEntryAtTime(mEntryB2, 6000);
+
+ mBubbleData.setListener(mListener);
+ sendUpdatedEntryAtTime(mEntryB3, 1000 /* postTime */, 7000 /* currentTime */);
+ verifyUpdateReceived();
+
+ // B3 is in the stack
+ assertThat(mBubbleData.getBubbleInStackWithKey(mBubbleB3.getKey())).isNotNull();
+ // A1 is the oldest so it's in the overflow
+ assertThat(mBubbleData.getOverflowBubbleWithKey(mEntryA1.getKey())).isNotNull();
+ assertOrderChangedTo(mBubbleB3, mBubbleB2, mBubbleB1, mBubbleA3, mBubbleA2);
+ }
+
private void verifyUpdateReceived() {
verify(mListener).applyUpdate(mUpdateCaptor.capture());
reset(mListener);
@@ -1014,6 +1040,12 @@
}
private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime) {
+ setCurrentTime(postTime);
+ sendUpdatedEntryAtTime(entry, postTime, true /* isTextChanged */);
+ }
+
+ private void sendUpdatedEntryAtTime(BubbleEntry entry, long postTime, long currentTime) {
+ setCurrentTime(currentTime);
sendUpdatedEntryAtTime(entry, postTime, true /* isTextChanged */);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
index 453050f..83d5f04 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java
@@ -24,6 +24,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -101,14 +102,21 @@
@Test
public void testSetDividePosition() {
- mSplitLayout.setDividePosition(anyInt());
+ mSplitLayout.setDividePosition(100, false /* applyLayoutChange */);
+ assertThat(mSplitLayout.getDividePosition()).isEqualTo(100);
+ verify(mSplitLayoutHandler, never()).onLayoutSizeChanged(any(SplitLayout.class));
+
+ mSplitLayout.setDividePosition(200, true /* applyLayoutChange */);
+ assertThat(mSplitLayout.getDividePosition()).isEqualTo(200);
verify(mSplitLayoutHandler).onLayoutSizeChanged(any(SplitLayout.class));
}
@Test
public void testSetDivideRatio() {
+ mSplitLayout.setDividePosition(200, false /* applyLayoutChange */);
mSplitLayout.setDivideRatio(0.5f);
- verify(mSplitLayoutHandler).onLayoutSizeChanged(any(SplitLayout.class));
+ assertThat(mSplitLayout.getDividePosition()).isEqualTo(
+ mSplitLayout.mDividerSnapAlgorithm.getMiddleTarget().position);
}
@Test
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 22904a0..136fc6c 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -902,6 +902,27 @@
return log_stream.str();
}
+base::expected<uint32_t, NullOrIOError> AssetManager2::GetParentThemeResourceId(uint32_t resid)
+const {
+ auto entry = FindEntry(resid, 0u /* density_override */,
+ false /* stop_at_first_match */,
+ false /* ignore_configuration */);
+ if (!entry.has_value()) {
+ return base::unexpected(entry.error());
+ }
+
+ auto entry_map = std::get_if<incfs::verified_map_ptr<ResTable_map_entry>>(&entry->entry);
+ if (entry_map == nullptr) {
+ // Not a bag, nothing to do.
+ return base::unexpected(std::nullopt);
+ }
+
+ auto map = *entry_map;
+ const uint32_t parent_resid = dtohl(map->parent.ident);
+
+ return parent_resid;
+}
+
base::expected<AssetManager2::ResourceName, NullOrIOError> AssetManager2::GetResourceName(
uint32_t resid) const {
auto result = FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */,
diff --git a/libs/androidfw/TEST_MAPPING b/libs/androidfw/TEST_MAPPING
index 9ebc996..8abe79d 100644
--- a/libs/androidfw/TEST_MAPPING
+++ b/libs/androidfw/TEST_MAPPING
@@ -2,6 +2,9 @@
"presubmit": [
{
"name": "CtsResourcesLoaderTests"
+ },
+ {
+ "name": "libandroidfw_tests"
}
]
}
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index a3b42df..1bde792 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -192,6 +192,12 @@
std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie,
Asset::AccessMode mode) const;
+ // Returns the resource id of parent style of the specified theme.
+ //
+ // Returns a null error if the name is missing/corrupt, or an I/O error if reading resource data
+ // failed.
+ base::expected<uint32_t, NullOrIOError> GetParentThemeResourceId(uint32_t resid) const;
+
// Returns the resource name of the specified resource ID.
//
// Utf8 strings are preferred, and only if they are unavailable are the Utf16 variants populated.
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 147e735..7f6fb90 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -460,4 +460,8 @@
boolean register);
void setTestDeviceConnectionState(in AudioDeviceAttributes device, boolean connected);
+
+ List<AudioFocusInfo> getFocusStack();
+
+ boolean sendFocusLoss(in AudioFocusInfo focusLoser, in IAudioPolicyCallback apcb);
}
diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java
index 0f08d79..3ba1d1f 100644
--- a/media/java/android/media/audiopolicy/AudioPolicy.java
+++ b/media/java/android/media/audiopolicy/AudioPolicy.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
@@ -230,7 +231,7 @@
* If set to {@code true}, it is mandatory to set an
* {@link AudioPolicy.AudioPolicyFocusListener} in order to successfully build
* an {@code AudioPolicy} instance.
- * @param enforce true if the policy will govern audio focus decisions.
+ * @param isFocusPolicy true if the policy will govern audio focus decisions.
* @return the same Builder instance.
*/
@NonNull
@@ -723,6 +724,45 @@
}
/**
+ * Returns the list of entries in the focus stack.
+ * The list is ordered with increasing rank of focus ownership, where the last entry is at the
+ * top of the focus stack and is the current focus owner.
+ * @return the ordered list of focus owners
+ * @see AudioManager#registerAudioPolicy(AudioPolicy)
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public @NonNull List<AudioFocusInfo> getFocusStack() {
+ try {
+ return getService().getFocusStack();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Send AUDIOFOCUS_LOSS to a specific stack entry, causing it to be notified of the focus
+ * loss, and for it to exit the focus stack (its focus listener will not be invoked after that).
+ * This operation is only valid for a registered policy (with
+ * {@link AudioManager#registerAudioPolicy(AudioPolicy)}) that is also set as the policy focus
+ * listener (with {@link Builder#setAudioPolicyFocusListener(AudioPolicyFocusListener)}.
+ * @param focusLoser the stack entry that is exiting the stack through a focus loss
+ * @return false if the focusLoser wasn't found in the stack, true otherwise
+ * @throws IllegalStateException if used on an unregistered policy, or a registered policy
+ * with no {@link AudioPolicyFocusListener} set
+ * @see AudioManager#registerAudioPolicy(AudioPolicy)
+ * @see Builder#setAudioPolicyStatusListener(AudioPolicyStatusListener)
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser) throws IllegalStateException {
+ Objects.requireNonNull(focusLoser);
+ try {
+ return getService().sendFocusLoss(focusLoser, cb());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Create an {@link AudioRecord} instance that is associated with the given {@link AudioMix}.
* Audio buffers recorded through the created instance will contain the mix of the audio
* streams that fed the given mixer.
diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
index cd87a09..9859a5f 100644
--- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
@@ -22,6 +22,7 @@
import android.media.tv.interactive.ITvIAppManagerCallback;
import android.media.tv.interactive.TvIAppInfo;
import android.net.Uri;
+import android.os.Bundle;
import android.view.Surface;
/**
@@ -31,6 +32,7 @@
interface ITvIAppManager {
List<TvIAppInfo> getTvIAppServiceList(int userId);
void prepare(String tiasId, int type, int userId);
+ void notifyAppLinkInfo(String tiasId, in Bundle info, int userId);
void startIApp(in IBinder sessionToken, int userId);
void createSession(
in ITvIAppClient client, in String iAppServiceId, int type, int seq, int userId);
diff --git a/media/java/android/media/tv/interactive/ITvIAppService.aidl b/media/java/android/media/tv/interactive/ITvIAppService.aidl
index af15dd8..72db3fa 100644
--- a/media/java/android/media/tv/interactive/ITvIAppService.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppService.aidl
@@ -18,6 +18,7 @@
import android.media.tv.interactive.ITvIAppServiceCallback;
import android.media.tv.interactive.ITvIAppSessionCallback;
+import android.os.Bundle;
import android.view.InputChannel;
/**
@@ -31,4 +32,5 @@
void createSession(in InputChannel channel, in ITvIAppSessionCallback callback,
in String iAppServiceId, int type);
void prepare(int type);
+ void notifyAppLinkInfo(in Bundle info);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java
index 2272084..8766055 100644
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvIAppManager.java
@@ -26,6 +26,7 @@
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvInputManager;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -87,6 +88,51 @@
*/
public static final int TV_IAPP_RTE_STATE_ERROR = 4;
+ /**
+ * Key for package name in app link.
+ * <p>Type: String
+ *
+ * @see #notifyAppLinkInfo(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_PACKAGE_NAME = "package_name";
+
+ /**
+ * Key for class name in app link.
+ * <p>Type: String
+ *
+ * @see #notifyAppLinkInfo(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_CLASS_NAME = "class_name";
+
+ /**
+ * Key for URI scheme in app link.
+ * <p>Type: String
+ *
+ * @see #notifyAppLinkInfo(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_URI_SCHEME = "uri_scheme";
+
+ /**
+ * Key for URI host in app link.
+ * <p>Type: String
+ *
+ * @see #notifyAppLinkInfo(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_URI_HOST = "uri_host";
+
+ /**
+ * Key for URI prefix in app link.
+ * <p>Type: String
+ *
+ * @see #notifyAppLinkInfo(String, Bundle)
+ * @hide
+ */
+ public static final String KEY_URI_PREFIX = "uri_prefix";
+
private final ITvIAppManager mService;
private final int mUserId;
@@ -418,6 +464,18 @@
}
/**
+ * Notifies app link info.
+ * @hide
+ */
+ public void notifyAppLinkInfo(String tvIAppServiceId, Bundle appLinkInfo) {
+ try {
+ mService.notifyAppLinkInfo(tvIAppServiceId, appLinkInfo, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Registers a {@link TvIAppManager.TvIAppCallback}.
*
* @param callback A callback used to monitor status of the TV IApp services.
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
index f93b597..6bf8028 100644
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -30,6 +30,7 @@
import android.media.tv.BroadcastInfoResponse;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -123,6 +124,11 @@
public void prepare(int type) {
onPrepare(type);
}
+
+ @Override
+ public void notifyAppLinkInfo(Bundle appLinkInfo) {
+ onAppLinkInfo(appLinkInfo);
+ }
};
return tvIAppServiceBinder;
}
@@ -135,6 +141,14 @@
// TODO: make it abstract when unhide
}
+ /**
+ * Registers App link info.
+ * @hide
+ */
+ public void onAppLinkInfo(Bundle appLinkInfo) {
+ // TODO: make it abstract when unhide
+ }
+
/**
* Returns a concrete implementation of {@link Session}.
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 94de7fa..255b391b 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -285,7 +285,7 @@
@Nullable
private FrontendInfo mFrontendInfo;
private Integer mFrontendHandle;
- private Boolean mIsSharedFrontend = false;
+ private Tuner mFeOwnerTuner = null;
private int mFrontendType = FrontendSettings.TYPE_UNDEFINED;
private int mUserId;
private Lnb mLnb;
@@ -442,11 +442,10 @@
mFrontendLock.lock();
try {
mTunerResourceManager.shareFrontend(mClientId, tuner.mClientId);
- synchronized (mIsSharedFrontend) {
- mFrontendHandle = tuner.mFrontendHandle;
- mFrontend = tuner.mFrontend;
- mIsSharedFrontend = true;
- }
+ mFeOwnerTuner = tuner;
+ mFeOwnerTuner.registerFrontendCallbackListener(this);
+ mFrontendHandle = mFeOwnerTuner.mFrontendHandle;
+ mFrontend = mFeOwnerTuner.mFrontend;
nativeShareFrontend(mFrontend.mId);
} finally {
releaseTRMSLock();
@@ -513,6 +512,27 @@
private long mNativeContext; // used by native jMediaTuner
/**
+ * Registers a tuner as a listener for frontend callbacks.
+ */
+ private void registerFrontendCallbackListener(Tuner tuner) {
+ nativeRegisterFeCbListener(tuner.getNativeContext());
+ }
+
+ /**
+ * Unregisters a tuner as a listener for frontend callbacks.
+ */
+ private void unregisterFrontendCallbackListener(Tuner tuner) {
+ nativeUnregisterFeCbListener(tuner.getNativeContext());
+ }
+
+ /**
+ * Returns the pointer to the associated JTuner.
+ */
+ long getNativeContext() {
+ return mNativeContext;
+ }
+
+ /**
* Releases the Tuner instance.
*/
@Override
@@ -526,19 +546,21 @@
}
}
- private void releaseAll() {
+ private void releaseFrontend() {
mFrontendLock.lock();
try {
if (mFrontendHandle != null) {
- synchronized (mIsSharedFrontend) {
- if (!mIsSharedFrontend) {
- int res = nativeCloseFrontend(mFrontendHandle);
- if (res != Tuner.RESULT_SUCCESS) {
- TunerUtils.throwExceptionForResult(res, "failed to close frontend");
- }
- mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId);
+ if (mFeOwnerTuner != null) {
+ // unregister self from the Frontend callback
+ mFeOwnerTuner.unregisterFrontendCallbackListener(this);
+ mFeOwnerTuner = null;
+ } else {
+ // close resource as owner
+ int res = nativeCloseFrontend(mFrontendHandle);
+ if (res != Tuner.RESULT_SUCCESS) {
+ TunerUtils.throwExceptionForResult(res, "failed to close frontend");
}
- mIsSharedFrontend = false;
+ mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId);
}
FrameworkStatsLog
.write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
@@ -549,9 +571,14 @@
} finally {
mFrontendLock.unlock();
}
+ }
+
+ private void releaseAll() {
+ releaseFrontend();
mLnbLock.lock();
try {
+ // mLnb will be non-null only for owner tuner
if (mLnb != null) {
mLnb.close();
}
@@ -641,6 +668,8 @@
*/
private native Frontend nativeOpenFrontendByHandle(int handle);
private native int nativeShareFrontend(int id);
+ private native void nativeRegisterFeCbListener(long nativeContext);
+ private native void nativeUnregisterFeCbListener(long nativeContext);
@Result
private native int nativeTune(int type, FrontendSettings settings);
private native int nativeStopTune();
@@ -1276,7 +1305,7 @@
}
private void onFrontendEvent(int eventType) {
- Log.d(TAG, "Got event from tuning. Event type: " + eventType);
+ Log.d(TAG, "Got event from tuning. Event type: " + eventType + " for " + this);
synchronized (mOnTuneEventLock) {
if (mOnTuneEventExecutor != null && mOnTuneEventListener != null) {
mOnTuneEventExecutor.execute(() -> {
diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
index cfd8583..6f3ab03 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java
@@ -98,6 +98,7 @@
private native void nativeSetFileDescriptor(int fd);
private native long nativeRead(long size);
private native long nativeRead(byte[] bytes, long offset, long size);
+ private native long nativeSeek(long pos);
private DvrPlayback() {
mUserId = Process.myUid();
@@ -243,7 +244,7 @@
*
* @param fd the file descriptor to read data.
* @see #read(long)
- * @see #read(byte[], long, long)
+ * @see #seek(long)
*/
public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) {
nativeSetFileDescriptor(fd.getFd());
@@ -261,19 +262,30 @@
}
/**
- * Reads data from the buffer for DVR playback and copies to the given byte array.
+ * Reads data from the buffer for DVR playback.
*
- * @param bytes the byte array to store the data.
- * @param offset the index of the first byte in {@code bytes} to copy to.
+ * @param buffer the byte array where DVR reads data from.
+ * @param offset the index of the first byte in {@code buffer} to read.
* @param size the maximum number of bytes to read.
* @return the number of bytes read.
*/
@BytesLong
- public long read(@NonNull byte[] bytes, @BytesLong long offset, @BytesLong long size) {
- if (size + offset > bytes.length) {
+ public long read(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) {
+ if (size + offset > buffer.length) {
throw new ArrayIndexOutOfBoundsException(
- "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size);
+ "Array length=" + buffer.length + ", offset=" + offset + ", size=" + size);
}
- return nativeRead(bytes, offset, size);
+ return nativeRead(buffer, offset, size);
+ }
+
+ /**
+ * Sets the file pointer offset of the file descriptor.
+ *
+ * @param pos the offset position, measured in bytes from the beginning of the file.
+ * @return the new offset position.
+ */
+ @BytesLong
+ public long seek(@BytesLong long pos) {
+ return nativeSeek(pos);
}
}
diff --git a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
index 212a713..e72026a 100644
--- a/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
+++ b/media/java/android/media/tv/tuner/dvr/DvrRecorder.java
@@ -216,7 +216,6 @@
*
* @param fd the file descriptor to write data.
* @see #write(long)
- * @see #write(byte[], long, long)
*/
public void setFileDescriptor(@NonNull ParcelFileDescriptor fd) {
nativeSetFileDescriptor(fd.getFd());
@@ -236,17 +235,17 @@
/**
* Writes recording data to buffer.
*
- * @param bytes the byte array stores the data to be written to DVR.
- * @param offset the index of the first byte in {@code bytes} to be written to DVR.
+ * @param buffer the byte array stores the data from DVR.
+ * @param offset the index of the first byte in {@code buffer} to write the data from DVR.
* @param size the maximum number of bytes to write.
* @return the number of bytes written.
*/
@BytesLong
- public long write(@NonNull byte[] bytes, @BytesLong long offset, @BytesLong long size) {
- if (size + offset > bytes.length) {
+ public long write(@NonNull byte[] buffer, @BytesLong long offset, @BytesLong long size) {
+ if (size + offset > buffer.length) {
throw new ArrayIndexOutOfBoundsException(
- "Array length=" + bytes.length + ", offset=" + offset + ", size=" + size);
+ "Array length=" + buffer.length + ", offset=" + offset + ", size=" + size);
}
- return nativeWrite(bytes, offset, size);
+ return nativeWrite(buffer, offset, size);
}
}
diff --git a/media/java/android/media/tv/tuner/filter/DownloadEvent.java b/media/java/android/media/tv/tuner/filter/DownloadEvent.java
index 394211be..25989db 100644
--- a/media/java/android/media/tv/tuner/filter/DownloadEvent.java
+++ b/media/java/android/media/tv/tuner/filter/DownloadEvent.java
@@ -27,15 +27,17 @@
@SystemApi
public class DownloadEvent extends FilterEvent {
private final int mItemId;
+ private final int mDownloadId;
private final int mMpuSequenceNumber;
private final int mItemFragmentIndex;
private final int mLastItemFragmentIndex;
private final int mDataLength;
// This constructor is used by JNI code only
- private DownloadEvent(int itemId, int mpuSequenceNumber, int itemFragmentIndex,
+ private DownloadEvent(int itemId, int downloadId, int mpuSequenceNumber, int itemFragmentIndex,
int lastItemFragmentIndex, int dataLength) {
mItemId = itemId;
+ mDownloadId = downloadId;
mMpuSequenceNumber = mpuSequenceNumber;
mItemFragmentIndex = itemFragmentIndex;
mLastItemFragmentIndex = lastItemFragmentIndex;
@@ -50,6 +52,15 @@
}
/**
+ * Gets download ID.
+ *
+ * <p>This query is only supported in Tuner 2.0 or higher version. Unsupported version will
+ * return {@code -1}.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ */
+ public int getDownloadId() { return mDownloadId; }
+
+ /**
* Gets MPU sequence number of filtered data.
*/
@IntRange(from = 0)
@@ -80,4 +91,3 @@
return mDataLength;
}
}
-
diff --git a/media/java/android/media/tv/tuner/filter/DownloadSettings.java b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
index 7ba923e..e2cfd7c 100644
--- a/media/java/android/media/tv/tuner/filter/DownloadSettings.java
+++ b/media/java/android/media/tv/tuner/filter/DownloadSettings.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.media.tv.tuner.TunerUtils;
+import android.media.tv.tuner.TunerVersionChecker;
/**
* Filter Settings for a Download.
@@ -27,10 +28,12 @@
*/
@SystemApi
public class DownloadSettings extends Settings {
+ private final boolean mUseDownloadId;
private final int mDownloadId;
- private DownloadSettings(int mainType, int downloadId) {
+ private DownloadSettings(int mainType, boolean useDownloadId, int downloadId) {
super(TunerUtils.getFilterSubtype(mainType, Filter.SUBTYPE_DOWNLOAD));
+ mUseDownloadId = useDownloadId;
mDownloadId = downloadId;
}
@@ -42,6 +45,15 @@
}
/**
+ * Gets whether download ID is used.
+ *
+ * <p>This query is only supported in Tuner 2.0 or higher version. Unsupported version will
+ * return {@code false}.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ */
+ public boolean useDownloadId() { return mUseDownloadId; }
+
+ /**
* Creates a builder for {@link DownloadSettings}.
*
* @param mainType the filter main type.
@@ -56,6 +68,7 @@
*/
public static class Builder {
private final int mMainType;
+ private boolean mUseDownloadId = false;
private int mDownloadId;
private Builder(int mainType) {
@@ -63,6 +76,24 @@
}
/**
+ * Sets whether download ID is used or not.
+ *
+ * <p>This configuration is only supported in Tuner 2.0 or higher version. Unsupported
+ * version will cause no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the
+ * version information.
+ *
+ * <p>Default value is {@code false}.
+ */
+ @NonNull
+ public Builder setUseDownloadId(boolean useDownloadId) {
+ if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "setUseDownloadId")) {
+ mUseDownloadId = useDownloadId;
+ }
+ return this;
+ }
+
+ /**
* Sets download ID.
*/
@NonNull
@@ -76,7 +107,7 @@
*/
@NonNull
public DownloadSettings build() {
- return new DownloadSettings(mMainType, mDownloadId);
+ return new DownloadSettings(mMainType, mUseDownloadId, mDownloadId);
}
}
}
diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java
index dbd85e9..79d4062 100644
--- a/media/java/android/media/tv/tuner/filter/MediaEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java
@@ -40,6 +40,8 @@
private final int mStreamId;
private final boolean mIsPtsPresent;
private final long mPts;
+ private final boolean mIsDtsPresent;
+ private final long mDts;
private final long mDataLength;
private final long mOffset;
private LinearBlock mLinearBlock;
@@ -50,12 +52,14 @@
private final AudioDescriptor mExtraMetaData;
// This constructor is used by JNI code only
- private MediaEvent(int streamId, boolean isPtsPresent, long pts, long dataLength, long offset,
- LinearBlock buffer, boolean isSecureMemory, long dataId, int mpuSequenceNumber,
- boolean isPrivateData, AudioDescriptor extraMetaData) {
+ private MediaEvent(int streamId, boolean isPtsPresent, long pts, boolean isDtsPresent, long dts,
+ long dataLength, long offset, LinearBlock buffer, boolean isSecureMemory, long dataId,
+ int mpuSequenceNumber, boolean isPrivateData, AudioDescriptor extraMetaData) {
mStreamId = streamId;
mIsPtsPresent = isPtsPresent;
mPts = pts;
+ mIsDtsPresent = isDtsPresent;
+ mDts = dts;
mDataLength = dataLength;
mOffset = offset;
mLinearBlock = buffer;
@@ -90,6 +94,26 @@
}
/**
+ * Returns whether DTS (Decode Time Stamp) is present.
+ *
+ * <p>This query is only supported in Tuner 2.0 or higher version. Unsupported version will
+ * return {@code false}.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ *
+ * @return {@code true} if DTS is present in PES header; {@code false} otherwise.
+ */
+ public boolean isDtsPresent() { return mIsDtsPresent; }
+
+ /**
+ * Gets DTS (Decode Time Stamp) for audio or video frame.
+ *
+ * * <p>This query is only supported in Tuner 2.0 or higher version. Unsupported version will
+ * return {@code -1}.
+ * Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
+ */
+ public long getDts() { return mDts; }
+
+ /**
* Gets data size in bytes of audio or video frame.
*/
@BytesLong
diff --git a/media/java/android/media/tv/tuner/filter/SectionEvent.java b/media/java/android/media/tv/tuner/filter/SectionEvent.java
index ff12492..182bb94 100644
--- a/media/java/android/media/tv/tuner/filter/SectionEvent.java
+++ b/media/java/android/media/tv/tuner/filter/SectionEvent.java
@@ -28,10 +28,10 @@
private final int mTableId;
private final int mVersion;
private final int mSectionNum;
- private final int mDataLength;
+ private final long mDataLength;
// This constructor is used by JNI code only
- private SectionEvent(int tableId, int version, int sectionNum, int dataLength) {
+ private SectionEvent(int tableId, int version, int sectionNum, long dataLength) {
mTableId = tableId;
mVersion = version;
mSectionNum = sectionNum;
@@ -61,8 +61,13 @@
/**
* Gets data size in bytes of filtered data.
+ *
+ * @deprecated Use {@link #getDataLengthLong()}
*/
+ @Deprecated
public int getDataLength() {
- return mDataLength;
+ return (int) getDataLengthLong();
}
+
+ public long getDataLengthLong() { return mDataLength; }
}
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index 31f1a63..582e4f5 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -19,10 +19,10 @@
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.media.tv.tuner.Lnb;
import android.media.tv.tuner.TunerVersionChecker;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -53,7 +53,7 @@
FRONTEND_STATUS_TYPE_MODULATIONS_EXT, FRONTEND_STATUS_TYPE_ROLL_OFF,
FRONTEND_STATUS_TYPE_IS_MISO_ENABLED, FRONTEND_STATUS_TYPE_IS_LINEAR,
FRONTEND_STATUS_TYPE_IS_SHORT_FRAMES_ENABLED, FRONTEND_STATUS_TYPE_ISDBT_MODE,
- FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG})
+ FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG, FRONTEND_STATUS_TYPE_STREAM_ID_LIST})
@Retention(RetentionPolicy.SOURCE)
public @interface FrontendStatusType {}
@@ -254,6 +254,12 @@
public static final int FRONTEND_STATUS_TYPE_ISDBT_PARTIAL_RECEPTION_FLAG =
android.hardware.tv.tuner.FrontendStatusType.ISDBT_PARTIAL_RECEPTION_FLAG;
+ /**
+ * Stream ID list included in a transponder.
+ */
+ public static final int FRONTEND_STATUS_TYPE_STREAM_ID_LIST =
+ android.hardware.tv.tuner.FrontendStatusType.STREAM_ID_LIST;
+
/** @hide */
@IntDef(value = {
AtscFrontendSettings.MODULATION_UNDEFINED,
@@ -493,6 +499,7 @@
private Boolean mIsShortFrames;
private Integer mIsdbtMode;
private Integer mIsdbtPartialReceptionFlag;
+ private int[] mStreamIds;
// Constructed and fields set by JNI code.
private FrontendStatus() {
@@ -1001,6 +1008,24 @@
}
/**
+ * Gets stream id list included in a transponder.
+ *
+ * <p>This query is only supported by Tuner HAL 2.0 or higher. Unsupported version or if HAL
+ * doesn't return stream id list status will throw IllegalStateException. Use
+ * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ */
+ @SuppressLint("ArrayReturn")
+ @NonNull
+ public int[] getStreamIdList() {
+ TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "stream id list status");
+ if (mStreamIds == null) {
+ throw new IllegalStateException("stream id list status is empty");
+ }
+ return mStreamIds;
+ }
+
+ /**
* Information of each tuning Physical Layer Pipes.
*/
public static class Atsc3PlpTuningInfo {
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index ded9652..e91e238 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -588,13 +588,13 @@
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/SectionEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIII)V");
+ jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIJ)V");
const DemuxFilterSectionEvent §ionEvent = event.get<DemuxFilterEvent::Tag::section>();
jint tableId = sectionEvent.tableId;
jint version = sectionEvent.version;
jint sectionNum = sectionEvent.sectionNum;
- jint dataLength = sectionEvent.dataLength;
+ jlong dataLength = sectionEvent.dataLength;
jobject obj = env->NewObject(eventClazz, eventInit, tableId, version, sectionNum, dataLength);
env->SetObjectArrayElement(arr, size, obj);
@@ -604,10 +604,9 @@
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/MediaEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz,
- "<init>",
- "(IZJJJLandroid/media/MediaCodec$LinearBlock;"
- "ZJIZLandroid/media/tv/tuner/filter/AudioDescriptor;)V");
+ jmethodID eventInit = env->GetMethodID(eventClazz, "<init>",
+ "(IZJZJJJLandroid/media/MediaCodec$LinearBlock;"
+ "ZJIZLandroid/media/tv/tuner/filter/AudioDescriptor;)V");
jfieldID eventContext = env->GetFieldID(eventClazz, "mNativeContext", "J");
const DemuxFilterMediaEvent &mediaEvent = event.get<DemuxFilterEvent::Tag::media>();
@@ -633,15 +632,17 @@
jint streamId = mediaEvent.streamId;
jboolean isPtsPresent = mediaEvent.isPtsPresent;
jlong pts = mediaEvent.pts;
+ jboolean isDtsPresent = mediaEvent.isDtsPresent;
+ jlong dts = mediaEvent.dts;
jlong offset = mediaEvent.offset;
jboolean isSecureMemory = mediaEvent.isSecureMemory;
jlong avDataId = mediaEvent.avDataId;
jint mpuSequenceNumber = mediaEvent.mpuSequenceNumber;
jboolean isPesPrivateData = mediaEvent.isPesPrivateData;
- jobject obj = env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, dataLength,
- offset, nullptr, isSecureMemory, avDataId, mpuSequenceNumber,
- isPesPrivateData, audioDescriptor);
+ jobject obj = env->NewObject(eventClazz, eventInit, streamId, isPtsPresent, pts, isDtsPresent,
+ dts, dataLength, offset, nullptr, isSecureMemory, avDataId,
+ mpuSequenceNumber, isPesPrivateData, audioDescriptor);
uint64_t avSharedMemSize = mFilterClient->getAvSharedHandleInfo().size;
if (mediaEvent.avMemory.fds.size() > 0 || mediaEvent.avDataId != 0 ||
@@ -733,16 +734,17 @@
const DemuxFilterEvent &event) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass eventClazz = env->FindClass("android/media/tv/tuner/filter/DownloadEvent");
- jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIII)V");
+ jmethodID eventInit = env->GetMethodID(eventClazz, "<init>", "(IIIIII)V");
const DemuxFilterDownloadEvent &downloadEvent = event.get<DemuxFilterEvent::Tag::download>();
jint itemId = downloadEvent.itemId;
+ jint downloadId = downloadEvent.downloadId;
jint mpuSequenceNumber = downloadEvent.mpuSequenceNumber;
jint itemFragmentIndex = downloadEvent.itemFragmentIndex;
jint lastItemFragmentIndex = downloadEvent.lastItemFragmentIndex;
jint dataLength = downloadEvent.dataLength;
- jobject obj = env->NewObject(eventClazz, eventInit, itemId, mpuSequenceNumber,
+ jobject obj = env->NewObject(eventClazz, eventInit, itemId, downloadId, mpuSequenceNumber,
itemFragmentIndex, lastItemFragmentIndex, dataLength);
env->SetObjectArrayElement(arr, size, obj);
}
@@ -951,20 +953,45 @@
}
/////////////// FrontendClientCallbackImpl ///////////////////////
-FrontendClientCallbackImpl::FrontendClientCallbackImpl(jweak tunerObj) : mObject(tunerObj) {}
+FrontendClientCallbackImpl::FrontendClientCallbackImpl(JTuner* jtuner, jweak listener) {
+ ALOGV("FrontendClientCallbackImpl() with listener:%p", listener);
+ addCallbackListener(jtuner, listener);
+}
+
+void FrontendClientCallbackImpl::addCallbackListener(JTuner* jtuner, jweak listener) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jweak listenerRef = env->NewWeakGlobalRef(listener);
+ ALOGV("addCallbackListener() with listener:%p and ref:%p @%p", listener, listenerRef, this);
+ std::scoped_lock<std::mutex> lock(mMutex);
+ mListenersMap[jtuner] = listenerRef;
+}
+
+void FrontendClientCallbackImpl::removeCallbackListener(JTuner* listener) {
+ ALOGV("removeCallbackListener for listener:%p", listener);
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ std::scoped_lock<std::mutex> lock(mMutex);
+ if (mListenersMap.find(listener) != mListenersMap.end() && mListenersMap[listener]) {
+ env->DeleteWeakGlobalRef(mListenersMap[listener]);
+ mListenersMap.erase(listener);
+ }
+}
void FrontendClientCallbackImpl::onEvent(FrontendEventType frontendEventType) {
ALOGV("FrontendClientCallbackImpl::onEvent, type=%d", frontendEventType);
JNIEnv *env = AndroidRuntime::getJNIEnv();
- jobject frontend(env->NewLocalRef(mObject));
- if (!env->IsSameObject(frontend, nullptr)) {
- env->CallVoidMethod(
- frontend,
- gFields.onFrontendEventID,
- (jint)frontendEventType);
- } else {
- ALOGE("FrontendClientCallbackImpl::onEvent:"
- "Frontend object has been freed. Ignoring callback.");
+ std::scoped_lock<std::mutex> lock(mMutex);
+ for (const auto& mapEntry : mListenersMap) {
+ ALOGV("JTuner:%p, jweak:%p", mapEntry.first, mapEntry.second);
+ jobject frontend(env->NewLocalRef(mapEntry.second));
+ if (!env->IsSameObject(frontend, nullptr)) {
+ env->CallVoidMethod(
+ frontend,
+ gFields.onFrontendEventID,
+ (jint)frontendEventType);
+ } else {
+ ALOGW("FrontendClientCallbackImpl::onEvent:"
+ "Frontend object has been freed. Ignoring callback.");
+ }
}
}
@@ -973,12 +1000,25 @@
ALOGV("FrontendClientCallbackImpl::onScanMessage, type=%d", type);
JNIEnv *env = AndroidRuntime::getJNIEnv();
jclass clazz = env->FindClass("android/media/tv/tuner/Tuner");
- jobject frontend(env->NewLocalRef(mObject));
- if (env->IsSameObject(frontend, nullptr)) {
- ALOGE("FrontendClientCallbackImpl::onScanMessage:"
- "Frontend object has been freed. Ignoring callback.");
- return;
+
+ std::scoped_lock<std::mutex> lock(mMutex);
+ for (const auto& mapEntry : mListenersMap) {
+ jobject frontend(env->NewLocalRef(mapEntry.second));
+ if (env->IsSameObject(frontend, nullptr)) {
+ ALOGE("FrontendClientCallbackImpl::onScanMessage:"
+ "Tuner object has been freed. Ignoring callback.");
+ continue;
+ }
+ executeOnScanMessage(env, clazz, frontend, type, message);
}
+}
+
+void FrontendClientCallbackImpl::executeOnScanMessage(
+ JNIEnv *env, const jclass& clazz, const jobject& frontend,
+ FrontendScanMessageType type,
+ const FrontendScanMessage& message) {
+ ALOGV("FrontendClientCallbackImpl::executeOnScanMessage, type=%d", type);
+
switch(type) {
case FrontendScanMessageType::LOCKED: {
if (message.get<FrontendScanMessage::Tag::isLocked>()) {
@@ -1157,11 +1197,14 @@
}
FrontendClientCallbackImpl::~FrontendClientCallbackImpl() {
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (mObject != nullptr) {
- env->DeleteWeakGlobalRef(mObject);
- mObject = nullptr;
+ JNIEnv *env = android::AndroidRuntime::getJNIEnv();
+ ALOGV("~FrontendClientCallbackImpl()");
+ std::scoped_lock<std::mutex> lock(mMutex);
+ for (const auto& mapEntry : mListenersMap) {
+ ALOGV("deleteRef :%p at @ %p", mapEntry.second, this);
+ env->DeleteWeakGlobalRef(mapEntry.second);
}
+ mListenersMap.clear();
}
/////////////// Tuner ///////////////////////
@@ -1180,6 +1223,10 @@
mSharedFeId = (int)Constant::INVALID_FRONTEND_ID;
}
+jweak JTuner::getObject() {
+ return mObject;
+}
+
JTuner::~JTuner() {
if (mFeClient != nullptr) {
mFeClient->close();
@@ -1192,6 +1239,7 @@
env->DeleteWeakGlobalRef(mObject);
env->DeleteGlobalRef(mClass);
mFeClient = nullptr;
+ mFeClientCb = nullptr;
mDemuxClient = nullptr;
mClass = nullptr;
mObject = nullptr;
@@ -1247,9 +1295,8 @@
return nullptr;
}
- sp<FrontendClientCallbackImpl> feClientCb =
- new FrontendClientCallbackImpl(env->NewWeakGlobalRef(mObject));
- mFeClient->setCallback(feClientCb);
+ mFeClientCb = new FrontendClientCallbackImpl(this, mObject);
+ mFeClient->setCallback(mFeClientCb);
// TODO: add more fields to frontend
return env->NewObject(
env->FindClass("android/media/tv/tuner/Tuner$Frontend"),
@@ -1269,6 +1316,18 @@
return (int)Result::SUCCESS;
}
+void JTuner::registerFeCbListener(JTuner* jtuner) {
+ if (mFeClientCb != nullptr && jtuner != nullptr) {
+ mFeClientCb->addCallbackListener(jtuner, jtuner->getObject());
+ }
+}
+
+void JTuner::unregisterFeCbListener(JTuner* jtuner) {
+ if (mFeClientCb != nullptr && jtuner != nullptr) {
+ mFeClientCb->removeCallbackListener(jtuner);
+ }
+}
+
jobject JTuner::getAnalogFrontendCaps(JNIEnv *env, FrontendCapabilities &caps) {
jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendCapabilities");
jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(II)V");
@@ -2450,7 +2509,14 @@
env->SetObjectField(statusObj, field, newIntegerObj);
break;
}
- default: {
+ case FrontendStatus::Tag::streamIdList: {
+ jfieldID field = env->GetFieldID(clazz, "mStreamIds", "[I");
+ std::vector<int32_t> ids = s.get<FrontendStatus::Tag::streamIdList>();
+
+ jintArray valObj = env->NewIntArray(v.size());
+ env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint *>(&ids[0]));
+
+ env->SetObjectField(statusObj, field, valObj);
break;
}
}
@@ -3188,6 +3254,20 @@
return tuner->shareFrontend(id);
}
+static void android_media_tv_Tuner_register_fe_cb_listener(
+ JNIEnv *env, jobject thiz, jlong shareeJTuner) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ JTuner *jtuner = (JTuner *)shareeJTuner;
+ tuner->registerFeCbListener(jtuner);
+}
+
+static void android_media_tv_Tuner_unregister_fe_cb_listener(
+ JNIEnv *env, jobject thiz, jlong shareeJTuner) {
+ sp<JTuner> tuner = getTuner(env, thiz);
+ JTuner *jtuner = (JTuner *)shareeJTuner;
+ tuner->unregisterFeCbListener(jtuner);
+}
+
static int android_media_tv_Tuner_tune(JNIEnv *env, jobject thiz, jint type, jobject settings) {
sp<JTuner> tuner = getTuner(env, thiz);
FrontendSettings setting = getFrontendSettings(env, type, settings);
@@ -3469,10 +3549,13 @@
static DemuxFilterDownloadSettings getFilterDownloadSettings(JNIEnv *env, const jobject& settings) {
jclass clazz = env->FindClass("android/media/tv/tuner/filter/DownloadSettings");
+ bool useDownloadId =
+ env->GetBooleanField(settings, env->GetFieldID(clazz, "mUseDownloadId", "Z"));
int32_t downloadId = env->GetIntField(settings, env->GetFieldID(clazz, "mDownloadId", "I"));
- DemuxFilterDownloadSettings filterDownloadSettings {
- .downloadId = downloadId,
+ DemuxFilterDownloadSettings filterDownloadSettings{
+ .useDownloadId = useDownloadId,
+ .downloadId = downloadId,
};
return filterDownloadSettings;
}
@@ -4253,6 +4336,17 @@
return (jlong)dvrClient->readFromFile(size);
}
+static jlong android_media_tv_Tuner_seek_dvr(JNIEnv *env, jobject dvr, jlong pos) {
+ sp<DvrClient> dvrClient = getDvrClient(env, dvr);
+ if (dvrClient == nullptr) {
+ jniThrowException(env, "java/lang/IllegalStateException",
+ "Failed to seek dvr: dvr client not found");
+ return -1;
+ }
+
+ return (jlong)dvrClient->seekFile(pos);
+}
+
static jlong android_media_tv_Tuner_read_dvr_from_array(
JNIEnv* env, jobject dvr, jbyteArray buffer, jlong offset, jlong size) {
sp<DvrClient> dvrClient = getDvrClient(env, dvr);
@@ -4361,6 +4455,10 @@
(void *)android_media_tv_Tuner_open_frontend_by_handle },
{ "nativeShareFrontend", "(I)I",
(void *)android_media_tv_Tuner_share_frontend },
+ { "nativeRegisterFeCbListener", "(J)V",
+ (void*)android_media_tv_Tuner_register_fe_cb_listener },
+ { "nativeUnregisterFeCbListener", "(J)V",
+ (void*)android_media_tv_Tuner_unregister_fe_cb_listener },
{ "nativeTune", "(ILandroid/media/tv/tuner/frontend/FrontendSettings;)I",
(void *)android_media_tv_Tuner_tune },
{ "nativeStopTune", "()I", (void *)android_media_tv_Tuner_stop_tune },
@@ -4403,38 +4501,37 @@
{ "nativeClose", "()I", (void *)android_media_tv_Tuner_close_tuner },
{ "nativeCloseFrontend", "(I)I", (void *)android_media_tv_Tuner_close_frontend },
{ "nativeCloseDemux", "(I)I", (void *)android_media_tv_Tuner_close_demux },
- {"nativeOpenSharedFilter",
+ { "nativeOpenSharedFilter",
"(Ljava/lang/String;)Landroid/media/tv/tuner/filter/SharedFilter;",
(void *)android_media_tv_Tuner_open_shared_filter},
};
static const JNINativeMethod gFilterMethods[] = {
{ "nativeConfigureFilter", "(IILandroid/media/tv/tuner/filter/FilterConfiguration;)I",
- (void *)android_media_tv_Tuner_configure_filter },
- { "nativeGetId", "()I", (void *)android_media_tv_Tuner_get_filter_id },
- { "nativeGetId64Bit", "()J",
- (void *)android_media_tv_Tuner_get_filter_64bit_id },
+ (void *)android_media_tv_Tuner_configure_filter},
+ { "nativeGetId", "()I", (void *)android_media_tv_Tuner_get_filter_id},
+ { "nativeGetId64Bit", "()J", (void *)android_media_tv_Tuner_get_filter_64bit_id},
{ "nativeConfigureMonitorEvent", "(I)I",
- (void *)android_media_tv_Tuner_configure_monitor_event },
+ (void *)android_media_tv_Tuner_configure_monitor_event},
{ "nativeSetDataSource", "(Landroid/media/tv/tuner/filter/Filter;)I",
- (void *)android_media_tv_Tuner_set_filter_data_source },
- { "nativeStartFilter", "()I", (void *)android_media_tv_Tuner_start_filter },
- { "nativeStopFilter", "()I", (void *)android_media_tv_Tuner_stop_filter },
- { "nativeFlushFilter", "()I", (void *)android_media_tv_Tuner_flush_filter },
- { "nativeRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq },
- { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_filter },
- {"nativeAcquireSharedFilterToken", "()Ljava/lang/String;",
+ (void *)android_media_tv_Tuner_set_filter_data_source},
+ { "nativeStartFilter", "()I", (void *)android_media_tv_Tuner_start_filter},
+ { "nativeStopFilter", "()I", (void *)android_media_tv_Tuner_stop_filter},
+ { "nativeFlushFilter", "()I", (void *)android_media_tv_Tuner_flush_filter},
+ { "nativeRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq},
+ { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_filter},
+ { "nativeAcquireSharedFilterToken", "()Ljava/lang/String;",
(void *)android_media_tv_Tuner_acquire_shared_filter_token},
- {"nativeFreeSharedFilterToken", "(Ljava/lang/String;)V",
+ { "nativeFreeSharedFilterToken", "(Ljava/lang/String;)V",
(void *)android_media_tv_Tuner_free_shared_filter_token},
};
static const JNINativeMethod gSharedFilterMethods[] = {
- {"nativeStartSharedFilter", "()I", (void *)android_media_tv_Tuner_start_filter},
- {"nativeStopSharedFilter", "()I", (void *)android_media_tv_Tuner_stop_filter},
- {"nativeFlushSharedFilter", "()I", (void *)android_media_tv_Tuner_flush_filter},
- {"nativeSharedRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq},
- {"nativeSharedClose", "()I", (void *)android_media_tv_Tuner_close_filter},
+ { "nativeStartSharedFilter", "()I", (void *)android_media_tv_Tuner_start_filter},
+ { "nativeStopSharedFilter", "()I", (void *)android_media_tv_Tuner_stop_filter},
+ { "nativeFlushSharedFilter", "()I", (void *)android_media_tv_Tuner_flush_filter},
+ { "nativeSharedRead", "([BJJ)I", (void *)android_media_tv_Tuner_read_filter_fmq},
+ { "nativeSharedClose", "()I", (void *)android_media_tv_Tuner_close_filter},
};
static const JNINativeMethod gTimeFilterMethods[] = {
@@ -4474,18 +4571,19 @@
static const JNINativeMethod gDvrPlaybackMethods[] = {
{ "nativeAttachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
- (void *)android_media_tv_Tuner_attach_filter },
+ (void *)android_media_tv_Tuner_attach_filter},
{ "nativeDetachFilter", "(Landroid/media/tv/tuner/filter/Filter;)I",
- (void *)android_media_tv_Tuner_detach_filter },
+ (void *)android_media_tv_Tuner_detach_filter},
{ "nativeConfigureDvr", "(Landroid/media/tv/tuner/dvr/DvrSettings;)I",
- (void *)android_media_tv_Tuner_configure_dvr },
- { "nativeStartDvr", "()I", (void *)android_media_tv_Tuner_start_dvr },
- { "nativeStopDvr", "()I", (void *)android_media_tv_Tuner_stop_dvr },
- { "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr },
- { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr },
- { "nativeSetFileDescriptor", "(I)V", (void *)android_media_tv_Tuner_dvr_set_fd },
- { "nativeRead", "(J)J", (void *)android_media_tv_Tuner_read_dvr },
- { "nativeRead", "([BJJ)J", (void *)android_media_tv_Tuner_read_dvr_from_array },
+ (void *)android_media_tv_Tuner_configure_dvr},
+ { "nativeStartDvr", "()I", (void *)android_media_tv_Tuner_start_dvr},
+ { "nativeStopDvr", "()I", (void *)android_media_tv_Tuner_stop_dvr},
+ { "nativeFlushDvr", "()I", (void *)android_media_tv_Tuner_flush_dvr},
+ { "nativeClose", "()I", (void *)android_media_tv_Tuner_close_dvr},
+ { "nativeSetFileDescriptor", "(I)V", (void *)android_media_tv_Tuner_dvr_set_fd},
+ { "nativeRead", "(J)J", (void *)android_media_tv_Tuner_read_dvr},
+ { "nativeRead", "([BJJ)J", (void *)android_media_tv_Tuner_read_dvr_from_array},
+ { "nativeSeek", "(J)J", (void *)android_media_tv_Tuner_seek_dvr},
};
static const JNINativeMethod gLnbMethods[] = {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 31d24ee..06e2492 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -149,14 +149,21 @@
void getRestartEvent(jobjectArray& arr, const int size, const DemuxFilterEvent& event);
};
+struct JTuner;
struct FrontendClientCallbackImpl : public FrontendClientCallback {
- FrontendClientCallbackImpl(jweak tunerObj);
+ FrontendClientCallbackImpl(JTuner*, jweak);
~FrontendClientCallbackImpl();
virtual void onEvent(FrontendEventType frontendEventType);
virtual void onScanMessage(
FrontendScanMessageType type, const FrontendScanMessage& message);
- jweak mObject;
+ void executeOnScanMessage(JNIEnv *env, const jclass& clazz, const jobject& frontend,
+ FrontendScanMessageType type,
+ const FrontendScanMessage& message);
+ void addCallbackListener(JTuner*, jweak obj);
+ void removeCallbackListener(JTuner* jtuner);
+ std::unordered_map<JTuner*, jweak> mListenersMap;
+ std::mutex mMutex;
};
struct JTuner : public RefBase {
@@ -171,6 +178,8 @@
jobject getFrontendIds();
jobject openFrontendByHandle(int feHandle);
int shareFrontend(int feId);
+ void registerFeCbListener(JTuner* jtuner);
+ void unregisterFeCbListener(JTuner* jtuner);
jint closeFrontendById(int id);
jobject getFrontendInfo(int id);
int tune(const FrontendSettings& settings);
@@ -192,6 +201,8 @@
jint closeFrontend();
jint closeDemux();
+ jweak getObject();
+
protected:
virtual ~JTuner();
@@ -200,6 +211,7 @@
jweak mObject;
static sp<TunerClient> mTunerClient;
sp<FrontendClient> mFeClient;
+ sp<FrontendClientCallbackImpl> mFeClientCb;
int mFeId;
int mSharedFeId;
sp<LnbClient> mLnbClient;
diff --git a/media/jni/tuner/DvrClient.cpp b/media/jni/tuner/DvrClient.cpp
index 3027838..05683b6 100644
--- a/media/jni/tuner/DvrClient.cpp
+++ b/media/jni/tuner/DvrClient.cpp
@@ -22,6 +22,8 @@
#include <aidl/android/hardware/tv/tuner/DemuxQueueNotifyBits.h>
#include <android-base/logging.h>
#include <inttypes.h>
+#include <sys/types.h>
+#include <unistd.h>
#include <utils/Log.h>
#include "ClientHelper.h"
@@ -200,6 +202,14 @@
return size;
}
+int64_t DvrClient::seekFile(int64_t pos) {
+ if (mFd < 0) {
+ ALOGE("Failed to seekFile. File is not configured");
+ return -1;
+ }
+ return lseek64(mFd, pos, SEEK_SET);
+}
+
Result DvrClient::configure(DvrSettings settings) {
if (mTunerDvr != nullptr) {
Status s = mTunerDvr->configure(settings);
diff --git a/media/jni/tuner/DvrClient.h b/media/jni/tuner/DvrClient.h
index 9080c72..61c0325 100644
--- a/media/jni/tuner/DvrClient.h
+++ b/media/jni/tuner/DvrClient.h
@@ -82,6 +82,11 @@
int64_t writeToFile(int64_t size);
/**
+ * Seeks the Dvr file descriptor from the beginning of the file.
+ */
+ int64_t seekFile(int64_t pos);
+
+ /**
* Write data to the given buffer with given size. Return the actual write size.
*/
int64_t writeToBuffer(int8_t* buffer, int64_t size);
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index ceba4d6..f76811e 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -364,7 +364,7 @@
sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
- sp<GraphicBuffer> graphic_buffer(reinterpret_cast<GraphicBuffer*>(buffer));
+ sp<GraphicBuffer> graphic_buffer(GraphicBuffer::fromAHardwareBuffer(buffer));
std::optional<sp<Fence>> fence = std::nullopt;
if (acquire_fence_fd != -1) {
diff --git a/packages/ConnectivityT/OWNERS b/packages/ConnectivityT/OWNERS
index 4862377..e267d19 100644
--- a/packages/ConnectivityT/OWNERS
+++ b/packages/ConnectivityT/OWNERS
@@ -1 +1,2 @@
-file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
\ No newline at end of file
+file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+per-file **IpSec* = file:/services/core/java/com/android/server/vcn/OWNERS
diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp
index 3e82b28..931a55b 100644
--- a/packages/ConnectivityT/framework-t/Android.bp
+++ b/packages/ConnectivityT/framework-t/Android.bp
@@ -100,6 +100,20 @@
],
}
+// IpSec related libraries.
+
+filegroup {
+ name: "framework-connectivity-ipsec-sources",
+ srcs: [
+ "src/android/net/IIpSecService.aidl",
+ "src/android/net/IpSec*.*",
+ ],
+ path: "src",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
// Connectivity-T common libraries.
filegroup {
@@ -116,11 +130,10 @@
filegroup {
name: "framework-connectivity-tiramisu-sources",
srcs: [
+ ":framework-connectivity-ipsec-sources",
":framework-connectivity-netstats-sources",
":framework-connectivity-nsd-sources",
":framework-connectivity-tiramisu-internal-sources",
],
- visibility: [
- "//frameworks/base",
- ],
+ visibility: ["//frameworks/base"],
}
diff --git a/core/java/android/net/IIpSecService.aidl b/packages/ConnectivityT/framework-t/src/android/net/IIpSecService.aidl
similarity index 100%
rename from core/java/android/net/IIpSecService.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IIpSecService.aidl
diff --git a/core/java/android/net/IpSecAlgorithm.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
similarity index 100%
rename from core/java/android/net/IpSecAlgorithm.java
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecAlgorithm.java
diff --git a/core/java/android/net/IpSecConfig.aidl b/packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.aidl
similarity index 100%
rename from core/java/android/net/IpSecConfig.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.aidl
diff --git a/core/java/android/net/IpSecConfig.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.java
similarity index 100%
rename from core/java/android/net/IpSecConfig.java
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecConfig.java
diff --git a/core/java/android/net/IpSecManager.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
similarity index 100%
rename from core/java/android/net/IpSecManager.java
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecManager.java
diff --git a/core/java/android/net/IpSecSpiResponse.aidl b/packages/ConnectivityT/framework-t/src/android/net/IpSecSpiResponse.aidl
similarity index 100%
rename from core/java/android/net/IpSecSpiResponse.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecSpiResponse.aidl
diff --git a/core/java/android/net/IpSecSpiResponse.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecSpiResponse.java
similarity index 100%
rename from core/java/android/net/IpSecSpiResponse.java
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecSpiResponse.java
diff --git a/core/java/android/net/IpSecTransform.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java
similarity index 100%
rename from core/java/android/net/IpSecTransform.java
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecTransform.java
diff --git a/core/java/android/net/IpSecTransformResponse.aidl b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransformResponse.aidl
similarity index 100%
rename from core/java/android/net/IpSecTransformResponse.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecTransformResponse.aidl
diff --git a/core/java/android/net/IpSecTransformResponse.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecTransformResponse.java
similarity index 100%
rename from core/java/android/net/IpSecTransformResponse.java
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecTransformResponse.java
diff --git a/core/java/android/net/IpSecTunnelInterfaceResponse.aidl b/packages/ConnectivityT/framework-t/src/android/net/IpSecTunnelInterfaceResponse.aidl
similarity index 100%
rename from core/java/android/net/IpSecTunnelInterfaceResponse.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecTunnelInterfaceResponse.aidl
diff --git a/core/java/android/net/IpSecTunnelInterfaceResponse.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecTunnelInterfaceResponse.java
similarity index 100%
rename from core/java/android/net/IpSecTunnelInterfaceResponse.java
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecTunnelInterfaceResponse.java
diff --git a/core/java/android/net/IpSecUdpEncapResponse.aidl b/packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.aidl
similarity index 100%
rename from core/java/android/net/IpSecUdpEncapResponse.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.aidl
diff --git a/core/java/android/net/IpSecUdpEncapResponse.java b/packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.java
similarity index 100%
rename from core/java/android/net/IpSecUdpEncapResponse.java
rename to packages/ConnectivityT/framework-t/src/android/net/IpSecUdpEncapResponse.java
diff --git a/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
index 6c597e2..0f21e55 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
@@ -16,10 +16,6 @@
package android.net.nsd;
-import static com.android.internal.util.Preconditions.checkArgument;
-import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.internal.util.Preconditions.checkStringNotEmpty;
-
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemService;
@@ -32,11 +28,13 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Protocol;
+
+import java.util.Objects;
/**
* The Network Service Discovery Manager class provides the API to discover services
@@ -175,65 +173,63 @@
*/
public static final int NSD_STATE_ENABLED = 2;
- private static final int BASE = Protocol.BASE_NSD_MANAGER;
+ /** @hide */
+ public static final int DISCOVER_SERVICES = 1;
+ /** @hide */
+ public static final int DISCOVER_SERVICES_STARTED = 2;
+ /** @hide */
+ public static final int DISCOVER_SERVICES_FAILED = 3;
+ /** @hide */
+ public static final int SERVICE_FOUND = 4;
+ /** @hide */
+ public static final int SERVICE_LOST = 5;
/** @hide */
- public static final int DISCOVER_SERVICES = BASE + 1;
+ public static final int STOP_DISCOVERY = 6;
/** @hide */
- public static final int DISCOVER_SERVICES_STARTED = BASE + 2;
+ public static final int STOP_DISCOVERY_FAILED = 7;
/** @hide */
- public static final int DISCOVER_SERVICES_FAILED = BASE + 3;
- /** @hide */
- public static final int SERVICE_FOUND = BASE + 4;
- /** @hide */
- public static final int SERVICE_LOST = BASE + 5;
+ public static final int STOP_DISCOVERY_SUCCEEDED = 8;
/** @hide */
- public static final int STOP_DISCOVERY = BASE + 6;
+ public static final int REGISTER_SERVICE = 9;
/** @hide */
- public static final int STOP_DISCOVERY_FAILED = BASE + 7;
+ public static final int REGISTER_SERVICE_FAILED = 10;
/** @hide */
- public static final int STOP_DISCOVERY_SUCCEEDED = BASE + 8;
+ public static final int REGISTER_SERVICE_SUCCEEDED = 11;
/** @hide */
- public static final int REGISTER_SERVICE = BASE + 9;
+ public static final int UNREGISTER_SERVICE = 12;
/** @hide */
- public static final int REGISTER_SERVICE_FAILED = BASE + 10;
+ public static final int UNREGISTER_SERVICE_FAILED = 13;
/** @hide */
- public static final int REGISTER_SERVICE_SUCCEEDED = BASE + 11;
+ public static final int UNREGISTER_SERVICE_SUCCEEDED = 14;
/** @hide */
- public static final int UNREGISTER_SERVICE = BASE + 12;
+ public static final int RESOLVE_SERVICE = 15;
/** @hide */
- public static final int UNREGISTER_SERVICE_FAILED = BASE + 13;
+ public static final int RESOLVE_SERVICE_FAILED = 16;
/** @hide */
- public static final int UNREGISTER_SERVICE_SUCCEEDED = BASE + 14;
+ public static final int RESOLVE_SERVICE_SUCCEEDED = 17;
/** @hide */
- public static final int RESOLVE_SERVICE = BASE + 18;
- /** @hide */
- public static final int RESOLVE_SERVICE_FAILED = BASE + 19;
- /** @hide */
- public static final int RESOLVE_SERVICE_SUCCEEDED = BASE + 20;
+ public static final int DAEMON_CLEANUP = 18;
/** @hide */
- public static final int DAEMON_CLEANUP = BASE + 21;
+ public static final int DAEMON_STARTUP = 19;
/** @hide */
- public static final int DAEMON_STARTUP = BASE + 22;
+ public static final int ENABLE = 20;
+ /** @hide */
+ public static final int DISABLE = 21;
/** @hide */
- public static final int ENABLE = BASE + 24;
- /** @hide */
- public static final int DISABLE = BASE + 25;
+ public static final int NATIVE_DAEMON_EVENT = 22;
/** @hide */
- public static final int NATIVE_DAEMON_EVENT = BASE + 26;
-
+ public static final int REGISTER_CLIENT = 23;
/** @hide */
- public static final int REGISTER_CLIENT = BASE + 27;
- /** @hide */
- public static final int UNREGISTER_CLIENT = BASE + 28;
+ public static final int UNREGISTER_CLIENT = 24;
/** Dns based service discovery protocol */
public static final int PROTOCOL_DNS_SD = 0x0001;
@@ -550,7 +546,9 @@
final int key;
synchronized (mMapLock) {
int valueIndex = mListenerMap.indexOfValue(listener);
- checkArgument(valueIndex == -1, "listener already in use");
+ if (valueIndex != -1) {
+ throw new IllegalArgumentException("listener already in use");
+ }
key = nextListenerKey();
mListenerMap.put(key, listener);
mServiceMap.put(key, s);
@@ -569,7 +567,9 @@
checkListener(listener);
synchronized (mMapLock) {
int valueIndex = mListenerMap.indexOfValue(listener);
- checkArgument(valueIndex != -1, "listener not registered");
+ if (valueIndex == -1) {
+ throw new IllegalArgumentException("listener not registered");
+ }
return mListenerMap.keyAt(valueIndex);
}
}
@@ -598,7 +598,9 @@
*/
public void registerService(NsdServiceInfo serviceInfo, int protocolType,
RegistrationListener listener) {
- checkArgument(serviceInfo.getPort() > 0, "Invalid port number");
+ if (serviceInfo.getPort() <= 0) {
+ throw new IllegalArgumentException("Invalid port number");
+ }
checkServiceInfo(serviceInfo);
checkProtocol(protocolType);
int key = putListener(listener, serviceInfo);
@@ -660,7 +662,9 @@
* Cannot be null. Cannot be in use for an active service discovery.
*/
public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) {
- checkStringNotEmpty(serviceType, "Service type cannot be empty");
+ if (TextUtils.isEmpty(serviceType)) {
+ throw new IllegalArgumentException("Service type cannot be empty");
+ }
checkProtocol(protocolType);
NsdServiceInfo s = new NsdServiceInfo();
@@ -719,16 +723,22 @@
}
private static void checkListener(Object listener) {
- checkNotNull(listener, "listener cannot be null");
+ Objects.requireNonNull(listener, "listener cannot be null");
}
private static void checkProtocol(int protocolType) {
- checkArgument(protocolType == PROTOCOL_DNS_SD, "Unsupported protocol");
+ if (protocolType != PROTOCOL_DNS_SD) {
+ throw new IllegalArgumentException("Unsupported protocol");
+ }
}
private static void checkServiceInfo(NsdServiceInfo serviceInfo) {
- checkNotNull(serviceInfo, "NsdServiceInfo cannot be null");
- checkStringNotEmpty(serviceInfo.getServiceName(), "Service name cannot be empty");
- checkStringNotEmpty(serviceInfo.getServiceType(), "Service type cannot be empty");
+ Objects.requireNonNull(serviceInfo, "NsdServiceInfo cannot be null");
+ if (TextUtils.isEmpty(serviceInfo.getServiceName())) {
+ throw new IllegalArgumentException("Service name cannot be empty");
+ }
+ if (TextUtils.isEmpty(serviceInfo.getServiceType())) {
+ throw new IllegalArgumentException("Service type cannot be empty");
+ }
}
}
diff --git a/packages/ConnectivityT/service/Android.bp b/packages/ConnectivityT/service/Android.bp
index 6a64910..7b88176 100644
--- a/packages/ConnectivityT/service/Android.bp
+++ b/packages/ConnectivityT/service/Android.bp
@@ -48,16 +48,28 @@
],
}
+// IpSec related libraries.
+
+filegroup {
+ name: "services.connectivity-ipsec-sources",
+ srcs: [
+ "src/com/android/server/IpSecService.java",
+ ],
+ path: "src",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
// Connectivity-T common libraries.
filegroup {
name: "services.connectivity-tiramisu-sources",
srcs: [
+ ":services.connectivity-ipsec-sources",
":services.connectivity-netstats-sources",
":services.connectivity-nsd-sources",
],
path: "src",
- visibility: [
- "//frameworks/base/services/core",
- ],
-}
\ No newline at end of file
+ visibility: ["//frameworks/base/services/core"],
+}
diff --git a/services/core/java/com/android/server/IpSecService.java b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
similarity index 100%
rename from services/core/java/com/android/server/IpSecService.java
rename to packages/ConnectivityT/service/src/com/android/server/IpSecService.java
diff --git a/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml b/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
index 67a70bb..b3987f1 100644
--- a/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
+++ b/packages/SystemUI/res-keyguard/drawable/super_lock_icon.xml
@@ -94,4 +94,9 @@
android:fromId="@id/unlocked"
android:toId="@id/unlocked_aod"
android:drawable="@drawable/unlocked_ls_to_aod" />
+
+ <transition
+ android:fromId="@id/unlocked"
+ android:toId="@id/locked_aod"
+ android:drawable="@drawable/unlocked_to_aod_lock" />
</animated-selector>
diff --git a/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml b/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml
new file mode 100644
index 0000000..b6d76e0
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/unlocked_to_aod_lock.xml
@@ -0,0 +1,169 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
+ <aapt:attr name="android:drawable">
+ <vector android:height="65dp" android:width="46dp" android:viewportHeight="65" android:viewportWidth="46">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_2_G_T_1" android:translateX="22.75" android:translateY="22.25" android:scaleX="1.02" android:scaleY="1.02">
+ <group android:name="_R_G_L_2_G" android:translateX="-8.75" android:translateY="-8.75">
+ <path android:name="_R_G_L_2_G_D_0_P_0" android:strokeColor="#FF000000"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2.5"
+ android:strokeAlpha="1"
+ android:pathData=" M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " />
+ </group>
+ </group>
+ <group android:name="_R_G_L_1_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_1_G_D_0_P_0"
+ android:strokeColor="#FF000000"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2"
+ android:strokeAlpha="1"
+ android:pathData=" M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " />
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="23" android:translateY="32.125">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#FF000000"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="strokeWidth"
+ android:duration="333" android:startOffset="0" android:valueFrom="2.5" android:valueTo="2" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0.833,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="83" android:startOffset="0" android:valueFrom="M27.19 14.81 C27.19,14.81 27.19,8.3 27.19,8.3 C27.19,4.92 24.44,2.88 21.19,2.75 C17.74,2.62 15,4.74 15,8.11 C15,8.11 15,15 15,15 " android:valueTo="M27.13 10.19 C27.13,10.19 27.13,3.67 27.13,3.67 C27.13,0.3 24.38,-1.75 21.13,-1.87 C17.68,-2.01 14.94,0.11 14.94,3.49 C14.94,3.49 15,15 15,15 " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.456,0 0.464,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData"
+ android:duration="133" android:startOffset="83" android:valueFrom="M27.13 10.19 C27.13,10.19 27.13,3.67 27.13,3.67 C27.13,0.3 24.38,-1.75 21.13,-1.87 C17.68,-2.01 14.94,0.11 14.94,3.49 C14.94,3.49 15,15 15,15 " android:valueTo="M2.5 10.38 C2.5,10.38 2.5,3.99 2.5,3.99 C2.5,0.61 5.3,-2.12 8.75,-2.12 C12.2,-2.12 15,0.61 15,3.99 C15,3.99 15,15 15,15 " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.606,0 0.035,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="pathData"
+ android:duration="117" android:startOffset="217" android:valueFrom="M2.5 10.38 C2.5,10.38 2.5,3.99 2.5,3.99 C2.5,0.61 5.3,-2.12 8.75,-2.12 C12.2,-2.12 15,0.61 15,3.99 C15,3.99 15,15 15,15 " android:valueTo="M2.5 15 C2.5,15 2.5,8.61 2.5,8.61 C2.5,5.24 5.3,2.5 8.75,2.5 C12.2,2.5 15,5.24 15,8.61 C15,8.61 15,15 15,15 " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.511,0 0.409,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="333" android:startOffset="0" android:valueFrom="22.75" android:valueTo="23" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateY" android:duration="333" android:startOffset="0" android:valueFrom="22.25" android:valueTo="25.5" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_2_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="scaleX"
+ android:duration="333" android:startOffset="0" android:valueFrom="1.02" android:valueTo="0.72" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator android:propertyName="scaleY"
+ android:duration="333" android:startOffset="0" android:valueFrom="1.02" android:valueTo="0.72" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="strokeWidth"
+ android:duration="333" android:startOffset="0" android:valueFrom="2" android:valueTo="1.5" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.38,0 0.274,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333" android:startOffset="0" android:valueFrom="M11.25 -0.64 C11.25,-0.64 11.25,13.64 11.25,13.64 C11.25,15.22 9.97,16.5 8.39,16.5 C8.39,16.5 -8.39,16.5 -8.39,16.5 C-9.97,16.5 -11.25,15.22 -11.25,13.64 C-11.25,13.64 -11.25,-0.64 -11.25,-0.64 C-11.25,-2.22 -9.97,-3.5 -8.39,-3.5 C-8.39,-3.5 8.39,-3.5 8.39,-3.5 C9.97,-3.5 11.25,-2.22 11.25,-0.64c " android:valueTo="M7.88 -0.62 C7.88,-0.62 7.88,9.38 7.88,9.38 C7.88,10.48 6.98,11.38 5.88,11.38 C5.88,11.38 -5.87,11.38 -5.87,11.38 C-6.98,11.38 -7.87,10.48 -7.87,9.38 C-7.87,9.38 -7.87,-0.62 -7.87,-0.62 C-7.87,-1.73 -6.98,-2.62 -5.87,-2.62 C-5.87,-2.62 5.88,-2.62 5.88,-2.62 C6.98,-2.62 7.88,-1.73 7.88,-0.62c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.431,0 0.133,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333" android:startOffset="0" android:valueFrom="M-0.09 8.63 C1.2,8.63 2.25,7.57 2.25,6.28 C2.25,4.99 1.2,3.94 -0.09,3.94 C-1.39,3.94 -2.44,4.99 -2.44,6.28 C-2.44,7.57 -1.39,8.63 -0.09,8.63c " android:valueTo="M0 6.13 C0.97,6.13 1.75,5.34 1.75,4.38 C1.75,3.41 0.97,2.63 0,2.63 C-0.97,2.63 -1.75,3.41 -1.75,4.38 C-1.75,5.34 -0.97,6.13 0,6.13c " android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.431,0 0.133,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="850" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
index a2b8bf6..4f0925f 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml
@@ -20,19 +20,18 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/keyguard_bouncer_user_switcher"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="vertical"
android:gravity="center"
- android:paddingTop="12dp"
android:importantForAccessibility="yes"> <!-- Needed because TYPE_WINDOW_STATE_CHANGED is sent
from this view when bouncer is shown -->
- <ImageView
- android:id="@+id/user_icon"
- android:layout_width="@dimen/keyguard_user_switcher_icon_size"
- android:layout_height="@dimen/keyguard_user_switcher_icon_size" />
+ <ImageView
+ android:id="@+id/user_icon"
+ android:layout_width="@dimen/keyguard_user_switcher_icon_size"
+ android:layout_height="@dimen/keyguard_user_switcher_icon_size" />
<!-- need to keep this outer view in order to have a correctly sized anchor
for the dropdown menu, as well as dropdown background in the right place -->
@@ -41,7 +40,7 @@
android:orientation="horizontal"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
- android:layout_marginTop="32dp"
+ android:layout_marginTop="30dp"
android:minHeight="48dp">
<TextView
style="@style/Keyguard.UserSwitcher.Spinner.Header"
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 9533040..2819dc9 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -108,10 +108,10 @@
<dimen name="one_handed_bouncer_move_animation_translation">120dp</dimen>
- <dimen name="keyguard_user_switcher_header_text_size">24sp</dimen>
- <dimen name="keyguard_user_switcher_item_text_size">18sp</dimen>
- <dimen name="keyguard_user_switcher_width">300dp</dimen>
- <dimen name="keyguard_user_switcher_icon_size">250dp</dimen>
+ <dimen name="keyguard_user_switcher_header_text_size">32sp</dimen>
+ <dimen name="keyguard_user_switcher_item_text_size">32sp</dimen>
+ <dimen name="keyguard_user_switcher_width">320dp</dimen>
+ <dimen name="keyguard_user_switcher_icon_size">310dp</dimen>
<dimen name="keyguard_user_switcher_corner">32dp</dimen>
<dimen name="keyguard_user_switcher_popup_corner">24dp</dimen>
<dimen name="keyguard_user_switcher_item_padding_vertical">15dp</dimen>
diff --git a/packages/SystemUI/res/drawable/ic_signal_wifi_off.xml b/packages/SystemUI/res/drawable/ic_signal_wifi_off.xml
new file mode 100644
index 0000000..a93abb7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_signal_wifi_off.xml
@@ -0,0 +1,24 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M18.575,15.2L7.55,4.175q1.1,-0.325 2.212,-0.462 1.113,-0.138 2.238,-0.138 3.45,0 6.55,1.45Q21.65,6.475 24,9zM20.225,23.575l-4.75,-4.75 -3.475,4.1L0,9q0.675,-0.725 1.438,-1.4 0.762,-0.675 1.612,-1.225l-2.625,-2.6L2.1,2.1l19.8,19.8z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
new file mode 100644
index 0000000..b611ffa
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+-->
+<com.android.systemui.dreams.DreamOverlayContainerView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/dream_overlay_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/dream_overlay_content"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent" />
+
+ <com.android.systemui.dreams.DreamOverlayStatusBarView
+ android:id="@+id/dream_overlay_status_bar"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/dream_overlay_status_bar_height"
+ android:layout_marginEnd="@dimen/dream_overlay_status_bar_margin"
+ android:layout_marginStart="@dimen/dream_overlay_status_bar_margin"
+ app:layout_constraintTop_toTopOf="parent">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/dream_overlay_system_status"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ app:layout_constraintEnd_toEndOf="parent">
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/dream_overlay_wifi_status"
+ android:layout_width="@dimen/status_bar_wifi_signal_size"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+ android:visibility="gone"
+ app:layout_constraintEnd_toStartOf="@id/dream_overlay_battery" />
+
+ <com.android.systemui.battery.BatteryMeterView
+ android:id="@+id/dream_overlay_battery"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ app:layout_constraintEnd_toEndOf="parent" />
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+ </com.android.systemui.dreams.DreamOverlayStatusBarView>
+</com.android.systemui.dreams.DreamOverlayContainerView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml
index f057603..1d6f279 100644
--- a/packages/SystemUI/res/values-h800dp/dimens.xml
+++ b/packages/SystemUI/res/values-h800dp/dimens.xml
@@ -22,5 +22,5 @@
<dimen name="large_clock_text_size">200dp</dimen>
<!-- With the large clock, move up slightly from the center -->
- <dimen name="keyguard_large_clock_top_margin">-104dp</dimen>
+ <dimen name="keyguard_large_clock_top_margin">-112dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c7350a1..d4398a8 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -30,10 +30,10 @@
<dimen name="navigation_bar_deadzone_size_max">32dp</dimen>
<!-- dimensions for the navigation bar handle -->
- <dimen name="navigation_handle_radius">1dp</dimen>
- <dimen name="navigation_handle_bottom">6dp</dimen>
+ <dimen name="navigation_handle_radius">2dp</dimen>
+ <dimen name="navigation_handle_bottom">10dp</dimen>
<dimen name="navigation_handle_sample_horizontal_margin">10dp</dimen>
- <dimen name="navigation_home_handle_width">72dp</dimen>
+ <dimen name="navigation_home_handle_width">108dp</dimen>
<!-- Size of the nav bar edge panels, should be greater to the
edge sensitivity + the drag threshold -->
@@ -602,7 +602,7 @@
<!-- When large clock is showing, offset the smartspace by this amount -->
<dimen name="keyguard_smartspace_top_offset">12dp</dimen>
<!-- With the large clock, move up slightly from the center -->
- <dimen name="keyguard_large_clock_top_margin">-52dp</dimen>
+ <dimen name="keyguard_large_clock_top_margin">-60dp</dimen>
<!-- Default line spacing multiplier between hours and minutes of the keyguard clock -->
<item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item>
@@ -1303,4 +1303,9 @@
<dimen name="keyguard_unfold_translation_x">16dp</dimen>
<dimen name="fgs_manager_min_width_minor">100%</dimen>
+
+ <!-- Dream overlay related dimensions -->
+ <dimen name="dream_overlay_status_bar_height">80dp</dimen>
+ <dimen name="dream_overlay_status_bar_margin">40dp</dimen>
+ <dimen name="dream_overlay_status_icon_margin">8dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
index ac463eb..1571913 100644
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java
@@ -168,6 +168,12 @@
if (!mIsDozing) mView.animateAppearOnLockscreen();
}
+ /** Animate the clock appearance when a foldable device goes from fully-open/half-open state to
+ * fully folded state and it goes to sleep (always on display screen) */
+ public void animateFoldAppear() {
+ mView.animateFoldAppear();
+ }
+
/**
* Updates the time for the view.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
deleted file mode 100644
index 2a0c285..0000000
--- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.java
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * 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 com.android.keyguard;
-
-import android.annotation.FloatRange;
-import android.annotation.IntRange;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Canvas;
-import android.text.format.DateFormat;
-import android.util.AttributeSet;
-import android.widget.TextView;
-
-import com.android.systemui.R;
-
-import java.util.Calendar;
-import java.util.Locale;
-import java.util.TimeZone;
-
-import kotlin.Unit;
-
-/**
- * Displays the time with the hour positioned above the minutes. (ie: 09 above 30 is 9:30)
- * The time's text color is a gradient that changes its colors based on its controller.
- */
-public class AnimatableClockView extends TextView {
- private static final CharSequence DOUBLE_LINE_FORMAT_12_HOUR = "hh\nmm";
- private static final CharSequence DOUBLE_LINE_FORMAT_24_HOUR = "HH\nmm";
- private static final long DOZE_ANIM_DURATION = 300;
- private static final long APPEAR_ANIM_DURATION = 350;
- private static final long CHARGE_ANIM_DURATION_PHASE_0 = 500;
- private static final long CHARGE_ANIM_DURATION_PHASE_1 = 1000;
-
- private final Calendar mTime = Calendar.getInstance();
-
- private final int mDozingWeight;
- private final int mLockScreenWeight;
- private CharSequence mFormat;
- private CharSequence mDescFormat;
- private int mDozingColor;
- private int mLockScreenColor;
- private float mLineSpacingScale = 1f;
- private int mChargeAnimationDelay = 0;
-
- private TextAnimator mTextAnimator = null;
- private Runnable mOnTextAnimatorInitialized;
-
- private boolean mIsSingleLine;
-
- public AnimatableClockView(Context context) {
- this(context, null, 0, 0);
- }
-
- public AnimatableClockView(Context context, AttributeSet attrs) {
- this(context, attrs, 0, 0);
- }
-
- public AnimatableClockView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public AnimatableClockView(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- TypedArray ta = context.obtainStyledAttributes(
- attrs, R.styleable.AnimatableClockView, defStyleAttr, defStyleRes);
- try {
- mDozingWeight = ta.getInt(R.styleable.AnimatableClockView_dozeWeight, 100);
- mLockScreenWeight = ta.getInt(R.styleable.AnimatableClockView_lockScreenWeight, 300);
- mChargeAnimationDelay = ta.getInt(
- R.styleable.AnimatableClockView_chargeAnimationDelay, 200);
- } finally {
- ta.recycle();
- }
-
- ta = context.obtainStyledAttributes(
- attrs, android.R.styleable.TextView, defStyleAttr, defStyleRes);
- try {
- mIsSingleLine = ta.getBoolean(android.R.styleable.TextView_singleLine, false);
- } finally {
- ta.recycle();
- }
-
- refreshFormat();
- }
-
- @Override
- public void onAttachedToWindow() {
- super.onAttachedToWindow();
- refreshFormat();
- }
-
- @Override
- public void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- }
-
- int getDozingWeight() {
- if (useBoldedVersion()) {
- return mDozingWeight + 100;
- }
- return mDozingWeight;
- }
-
- int getLockScreenWeight() {
- if (useBoldedVersion()) {
- return mLockScreenWeight + 100;
- }
- return mLockScreenWeight;
- }
-
- /**
- * Whether to use a bolded version based on the user specified fontWeightAdjustment.
- */
- boolean useBoldedVersion() {
- // "Bold text" fontWeightAdjustment is 300.
- return getResources().getConfiguration().fontWeightAdjustment > 100;
- }
-
- void refreshTime() {
- mTime.setTimeInMillis(System.currentTimeMillis());
- setText(DateFormat.format(mFormat, mTime));
- setContentDescription(DateFormat.format(mDescFormat, mTime));
- }
-
- void onTimeZoneChanged(TimeZone timeZone) {
- mTime.setTimeZone(timeZone);
- refreshFormat();
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- if (mTextAnimator == null) {
- mTextAnimator = new TextAnimator(
- getLayout(),
- () -> {
- invalidate();
- return Unit.INSTANCE;
- });
- if (mOnTextAnimatorInitialized != null) {
- mOnTextAnimatorInitialized.run();
- mOnTextAnimatorInitialized = null;
- }
- } else {
- mTextAnimator.updateLayout(getLayout());
- }
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- mTextAnimator.draw(canvas);
- }
-
- void setLineSpacingScale(float scale) {
- mLineSpacingScale = scale;
- setLineSpacing(0, mLineSpacingScale);
- }
-
- void setColors(int dozingColor, int lockScreenColor) {
- mDozingColor = dozingColor;
- mLockScreenColor = lockScreenColor;
- }
-
- void animateAppearOnLockscreen() {
- if (mTextAnimator == null) {
- return;
- }
-
- setTextStyle(
- getDozingWeight(),
- -1 /* text size, no update */,
- mLockScreenColor,
- false /* animate */,
- 0 /* duration */,
- 0 /* delay */,
- null /* onAnimationEnd */);
-
- setTextStyle(
- getLockScreenWeight(),
- -1 /* text size, no update */,
- mLockScreenColor,
- true, /* animate */
- APPEAR_ANIM_DURATION,
- 0 /* delay */,
- null /* onAnimationEnd */);
- }
-
- void animateCharge(DozeStateGetter dozeStateGetter) {
- if (mTextAnimator == null || mTextAnimator.isRunning()) {
- // Skip charge animation if dozing animation is already playing.
- return;
- }
- Runnable startAnimPhase2 = () -> setTextStyle(
- dozeStateGetter.isDozing() ? getDozingWeight() : getLockScreenWeight() /* weight */,
- -1,
- null,
- true /* animate */,
- CHARGE_ANIM_DURATION_PHASE_1,
- 0 /* delay */,
- null /* onAnimationEnd */);
- setTextStyle(dozeStateGetter.isDozing()
- ? getLockScreenWeight()
- : getDozingWeight()/* weight */,
- -1,
- null,
- true /* animate */,
- CHARGE_ANIM_DURATION_PHASE_0,
- mChargeAnimationDelay,
- startAnimPhase2);
- }
-
- void animateDoze(boolean isDozing, boolean animate) {
- setTextStyle(isDozing ? getDozingWeight() : getLockScreenWeight() /* weight */,
- -1,
- isDozing ? mDozingColor : mLockScreenColor,
- animate,
- DOZE_ANIM_DURATION,
- 0 /* delay */,
- null /* onAnimationEnd */);
- }
-
- /**
- * Set text style with an optional animation.
- *
- * By passing -1 to weight, the view preserves its current weight.
- * By passing -1 to textSize, the view preserves its current text size.
- *
- * @param weight text weight.
- * @param textSize font size.
- * @param animate true to animate the text style change, otherwise false.
- */
- private void setTextStyle(
- @IntRange(from = 0, to = 1000) int weight,
- @FloatRange(from = 0) float textSize,
- Integer color,
- boolean animate,
- long duration,
- long delay,
- Runnable onAnimationEnd) {
- if (mTextAnimator != null) {
- mTextAnimator.setTextStyle(weight, textSize, color, animate, duration, null,
- delay, onAnimationEnd);
- } else {
- // when the text animator is set, update its start values
- mOnTextAnimatorInitialized =
- () -> mTextAnimator.setTextStyle(
- weight, textSize, color, false, duration, null,
- delay, onAnimationEnd);
- }
- }
-
- void refreshFormat() {
- Patterns.update(mContext);
-
- final boolean use24HourFormat = DateFormat.is24HourFormat(getContext());
- if (mIsSingleLine && use24HourFormat) {
- mFormat = Patterns.sClockView24;
- } else if (!mIsSingleLine && use24HourFormat) {
- mFormat = DOUBLE_LINE_FORMAT_24_HOUR;
- } else if (mIsSingleLine && !use24HourFormat) {
- mFormat = Patterns.sClockView12;
- } else {
- mFormat = DOUBLE_LINE_FORMAT_12_HOUR;
- }
-
- mDescFormat = use24HourFormat ? Patterns.sClockView24 : Patterns.sClockView12;
- refreshTime();
- }
-
- // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
- // This is an optimization to ensure we only recompute the patterns when the inputs change.
- private static final class Patterns {
- static String sClockView12;
- static String sClockView24;
- static String sCacheKey;
-
- static void update(Context context) {
- final Locale locale = Locale.getDefault();
- final Resources res = context.getResources();
- final String clockView12Skel = res.getString(R.string.clock_12hr_format);
- final String clockView24Skel = res.getString(R.string.clock_24hr_format);
- final String key = locale.toString() + clockView12Skel + clockView24Skel;
- if (key.equals(sCacheKey)) return;
- sClockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel);
-
- // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
- // format. The following code removes the AM/PM indicator if we didn't want it.
- if (!clockView12Skel.contains("a")) {
- sClockView12 = sClockView12.replaceAll("a", "").trim();
- }
- sClockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel);
- sCacheKey = key;
- }
- }
-
- interface DozeStateGetter {
- boolean isDozing();
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt
new file mode 100644
index 0000000..357be25
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockView.kt
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard
+
+import android.animation.TimeInterpolator
+import android.annotation.ColorInt
+import android.annotation.FloatRange
+import android.annotation.IntRange
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.Canvas
+import android.text.format.DateFormat
+import android.util.AttributeSet
+import android.widget.TextView
+import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator
+import java.util.Calendar
+import java.util.Locale
+import java.util.TimeZone
+
+/**
+ * Displays the time with the hour positioned above the minutes. (ie: 09 above 30 is 9:30)
+ * The time's text color is a gradient that changes its colors based on its controller.
+ */
+class AnimatableClockView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : TextView(context, attrs, defStyleAttr, defStyleRes) {
+
+ private val time = Calendar.getInstance()
+
+ private val dozingWeightInternal: Int
+ private val lockScreenWeightInternal: Int
+ private val isSingleLineInternal: Boolean
+
+ private var format: CharSequence? = null
+ private var descFormat: CharSequence? = null
+
+ @ColorInt
+ private var dozingColor = 0
+
+ @ColorInt
+ private var lockScreenColor = 0
+
+ private var lineSpacingScale = 1f
+ private val chargeAnimationDelay: Int
+ private var textAnimator: TextAnimator? = null
+ private var onTextAnimatorInitialized: Runnable? = null
+
+ val dozingWeight: Int
+ get() = if (useBoldedVersion()) dozingWeightInternal + 100 else dozingWeightInternal
+
+ val lockScreenWeight: Int
+ get() = if (useBoldedVersion()) lockScreenWeightInternal + 100 else lockScreenWeightInternal
+
+ init {
+ val animatableClockViewAttributes = context.obtainStyledAttributes(
+ attrs, R.styleable.AnimatableClockView, defStyleAttr, defStyleRes
+ )
+
+ try {
+ dozingWeightInternal = animatableClockViewAttributes.getInt(
+ R.styleable.AnimatableClockView_dozeWeight,
+ 100
+ )
+ lockScreenWeightInternal = animatableClockViewAttributes.getInt(
+ R.styleable.AnimatableClockView_lockScreenWeight,
+ 300
+ )
+ chargeAnimationDelay = animatableClockViewAttributes.getInt(
+ R.styleable.AnimatableClockView_chargeAnimationDelay, 200
+ )
+ } finally {
+ animatableClockViewAttributes.recycle()
+ }
+
+ val textViewAttributes = context.obtainStyledAttributes(
+ attrs, android.R.styleable.TextView,
+ defStyleAttr, defStyleRes
+ )
+
+ isSingleLineInternal =
+ try {
+ textViewAttributes.getBoolean(android.R.styleable.TextView_singleLine, false)
+ } finally {
+ textViewAttributes.recycle()
+ }
+
+ refreshFormat()
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ refreshFormat()
+ }
+
+ /**
+ * Whether to use a bolded version based on the user specified fontWeightAdjustment.
+ */
+ fun useBoldedVersion(): Boolean {
+ // "Bold text" fontWeightAdjustment is 300.
+ return resources.configuration.fontWeightAdjustment > 100
+ }
+
+ fun refreshTime() {
+ time.timeInMillis = System.currentTimeMillis()
+ text = DateFormat.format(format, time)
+ contentDescription = DateFormat.format(descFormat, time)
+ }
+
+ fun onTimeZoneChanged(timeZone: TimeZone?) {
+ time.timeZone = timeZone
+ refreshFormat()
+ }
+
+ @SuppressLint("DrawAllocation")
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ val animator = textAnimator
+ if (animator == null) {
+ textAnimator = TextAnimator(layout) { invalidate() }
+ onTextAnimatorInitialized?.run()
+ onTextAnimatorInitialized = null
+ } else {
+ animator.updateLayout(layout)
+ }
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ textAnimator?.draw(canvas)
+ }
+
+ fun setLineSpacingScale(scale: Float) {
+ lineSpacingScale = scale
+ setLineSpacing(0f, lineSpacingScale)
+ }
+
+ fun setColors(@ColorInt dozingColor: Int, lockScreenColor: Int) {
+ this.dozingColor = dozingColor
+ this.lockScreenColor = lockScreenColor
+ }
+
+ fun animateAppearOnLockscreen() {
+ if (textAnimator == null) {
+ return
+ }
+ setTextStyle(
+ weight = dozingWeight,
+ textSize = -1f,
+ color = lockScreenColor,
+ animate = false,
+ duration = 0,
+ delay = 0,
+ onAnimationEnd = null
+ )
+ setTextStyle(
+ weight = lockScreenWeight,
+ textSize = -1f,
+ color = lockScreenColor,
+ animate = true,
+ duration = APPEAR_ANIM_DURATION,
+ delay = 0,
+ onAnimationEnd = null
+ )
+ }
+
+ fun animateFoldAppear() {
+ if (textAnimator == null) {
+ return
+ }
+ setTextStyle(
+ weight = lockScreenWeightInternal,
+ textSize = -1f,
+ color = lockScreenColor,
+ animate = false,
+ duration = 0,
+ delay = 0,
+ onAnimationEnd = null
+ )
+ setTextStyle(
+ weight = dozingWeightInternal,
+ textSize = -1f,
+ color = dozingColor,
+ animate = true,
+ interpolator = Interpolators.EMPHASIZED_DECELERATE,
+ duration = StackStateAnimator.ANIMATION_DURATION_FOLD_TO_AOD.toLong(),
+ delay = 0,
+ onAnimationEnd = null
+ )
+ }
+
+ fun animateCharge(dozeStateGetter: DozeStateGetter) {
+ if (textAnimator == null || textAnimator!!.isRunning()) {
+ // Skip charge animation if dozing animation is already playing.
+ return
+ }
+ val startAnimPhase2 = Runnable {
+ setTextStyle(
+ weight = if (dozeStateGetter.isDozing) dozingWeight else lockScreenWeight,
+ textSize = -1f,
+ color = null,
+ animate = true,
+ duration = CHARGE_ANIM_DURATION_PHASE_1,
+ delay = 0,
+ onAnimationEnd = null
+ )
+ }
+ setTextStyle(
+ weight = if (dozeStateGetter.isDozing) lockScreenWeight else dozingWeight,
+ textSize = -1f,
+ color = null,
+ animate = true,
+ duration = CHARGE_ANIM_DURATION_PHASE_0,
+ delay = chargeAnimationDelay.toLong(),
+ onAnimationEnd = startAnimPhase2
+ )
+ }
+
+ fun animateDoze(isDozing: Boolean, animate: Boolean) {
+ setTextStyle(
+ weight = if (isDozing) dozingWeight else lockScreenWeight,
+ textSize = -1f,
+ color = if (isDozing) dozingColor else lockScreenColor,
+ animate = animate,
+ duration = DOZE_ANIM_DURATION,
+ delay = 0,
+ onAnimationEnd = null
+ )
+ }
+
+ /**
+ * Set text style with an optional animation.
+ *
+ * By passing -1 to weight, the view preserves its current weight.
+ * By passing -1 to textSize, the view preserves its current text size.
+ *
+ * @param weight text weight.
+ * @param textSize font size.
+ * @param animate true to animate the text style change, otherwise false.
+ */
+ private fun setTextStyle(
+ @IntRange(from = 0, to = 1000) weight: Int,
+ @FloatRange(from = 0.0) textSize: Float,
+ color: Int?,
+ animate: Boolean,
+ interpolator: TimeInterpolator?,
+ duration: Long,
+ delay: Long,
+ onAnimationEnd: Runnable?
+ ) {
+ if (textAnimator != null) {
+ textAnimator?.setTextStyle(
+ weight = weight,
+ textSize = textSize,
+ color = color,
+ animate = animate,
+ duration = duration,
+ interpolator = interpolator,
+ delay = delay,
+ onAnimationEnd = onAnimationEnd
+ )
+ } else {
+ // when the text animator is set, update its start values
+ onTextAnimatorInitialized = Runnable {
+ textAnimator?.setTextStyle(
+ weight = weight,
+ textSize = textSize,
+ color = color,
+ animate = false,
+ duration = duration,
+ interpolator = interpolator,
+ delay = delay,
+ onAnimationEnd = onAnimationEnd
+ )
+ }
+ }
+ }
+
+ private fun setTextStyle(
+ @IntRange(from = 0, to = 1000) weight: Int,
+ @FloatRange(from = 0.0) textSize: Float,
+ color: Int?,
+ animate: Boolean,
+ duration: Long,
+ delay: Long,
+ onAnimationEnd: Runnable?
+ ) {
+ setTextStyle(
+ weight = weight,
+ textSize = textSize,
+ color = color,
+ animate = animate,
+ interpolator = null,
+ duration = duration,
+ delay = delay,
+ onAnimationEnd = onAnimationEnd
+ )
+ }
+
+ fun refreshFormat() {
+ Patterns.update(context)
+ val use24HourFormat = DateFormat.is24HourFormat(context)
+
+ format = when {
+ isSingleLineInternal && use24HourFormat -> Patterns.sClockView24
+ !isSingleLineInternal && use24HourFormat -> DOUBLE_LINE_FORMAT_24_HOUR
+ isSingleLineInternal && !use24HourFormat -> Patterns.sClockView12
+ else -> DOUBLE_LINE_FORMAT_12_HOUR
+ }
+
+ descFormat = if (use24HourFormat) Patterns.sClockView24 else Patterns.sClockView12
+
+ refreshTime()
+ }
+
+ // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
+ // This is an optimization to ensure we only recompute the patterns when the inputs change.
+ private object Patterns {
+ var sClockView12: String? = null
+ var sClockView24: String? = null
+ var sCacheKey: String? = null
+
+ fun update(context: Context) {
+ val locale = Locale.getDefault()
+ val res = context.resources
+ val clockView12Skel = res.getString(R.string.clock_12hr_format)
+ val clockView24Skel = res.getString(R.string.clock_24hr_format)
+ val key = locale.toString() + clockView12Skel + clockView24Skel
+ if (key == sCacheKey) return
+
+ val clockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel)
+ sClockView12 = clockView12
+
+ // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
+ // format. The following code removes the AM/PM indicator if we didn't want it.
+ if (!clockView12Skel.contains("a")) {
+ sClockView12 = clockView12.replace("a".toRegex(), "").trim { it <= ' ' }
+ }
+ sClockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel)
+ sCacheKey = key
+ }
+ }
+
+ interface DozeStateGetter {
+ val isDozing: Boolean
+ }
+}
+
+private const val DOUBLE_LINE_FORMAT_12_HOUR = "hh\nmm"
+private const val DOUBLE_LINE_FORMAT_24_HOUR = "HH\nmm"
+private const val DOZE_ANIM_DURATION: Long = 300
+private const val APPEAR_ANIM_DURATION: Long = 350
+private const val CHARGE_ANIM_DURATION_PHASE_0: Long = 500
+private const val CHARGE_ANIM_DURATION_PHASE_1: Long = 1000
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 9238b82..25dcdf9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -190,11 +190,15 @@
}
}
- private void animateClockChange(boolean useLargeClock) {
+ private void updateClockViews(boolean useLargeClock, boolean animate) {
if (mClockInAnim != null) mClockInAnim.cancel();
if (mClockOutAnim != null) mClockOutAnim.cancel();
if (mStatusAreaAnim != null) mStatusAreaAnim.cancel();
+ mClockInAnim = null;
+ mClockOutAnim = null;
+ mStatusAreaAnim = null;
+
View in, out;
int direction = 1;
float statusAreaYTranslation;
@@ -214,6 +218,14 @@
removeView(out);
}
+ if (!animate) {
+ out.setAlpha(0f);
+ in.setAlpha(1f);
+ in.setVisibility(VISIBLE);
+ mStatusArea.setTranslationY(statusAreaYTranslation);
+ return;
+ }
+
mClockOutAnim = new AnimatorSet();
mClockOutAnim.setDuration(CLOCK_OUT_MILLIS);
mClockOutAnim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
@@ -273,7 +285,7 @@
*
* @return true if desired clock appeared and false if it was already visible
*/
- boolean switchToClock(@ClockSize int clockSize) {
+ boolean switchToClock(@ClockSize int clockSize, boolean animate) {
if (mDisplayedClockSize != null && clockSize == mDisplayedClockSize) {
return false;
}
@@ -281,7 +293,7 @@
// let's make sure clock is changed only after all views were laid out so we can
// translate them properly
if (mChildrenAreLaidOut) {
- animateClockChange(clockSize == LARGE);
+ updateClockViews(clockSize == LARGE, animate);
}
mDisplayedClockSize = clockSize;
@@ -293,7 +305,7 @@
super.onLayout(changed, l, t, r, b);
if (mDisplayedClockSize != null && !mChildrenAreLaidOut) {
- animateClockChange(mDisplayedClockSize == LARGE);
+ updateClockViews(mDisplayedClockSize == LARGE, /* animate */ true);
}
mChildrenAreLaidOut = true;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 032da78..b7a5aae 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -275,11 +275,9 @@
}
private void updateClockLayout() {
- int largeClockTopMargin = 0;
- if (mSmartspaceController.isEnabled()) {
- largeClockTopMargin = getContext().getResources().getDimensionPixelSize(
- R.dimen.keyguard_large_clock_top_margin);
- }
+ int largeClockTopMargin = getContext().getResources().getDimensionPixelSize(
+ R.dimen.keyguard_large_clock_top_margin);
+
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(MATCH_PARENT,
MATCH_PARENT);
lp.topMargin = largeClockTopMargin;
@@ -290,17 +288,24 @@
* Set which clock should be displayed on the keyguard. The other one will be automatically
* hidden.
*/
- public void displayClock(@KeyguardClockSwitch.ClockSize int clockSize) {
+ public void displayClock(@KeyguardClockSwitch.ClockSize int clockSize, boolean animate) {
if (!mCanShowDoubleLineClock && clockSize == KeyguardClockSwitch.LARGE) {
return;
}
- boolean appeared = mView.switchToClock(clockSize);
- if (appeared && clockSize == LARGE) {
+ boolean appeared = mView.switchToClock(clockSize, animate);
+ if (animate && appeared && clockSize == LARGE) {
mLargeClockViewController.animateAppear();
}
}
+ public void animateFoldToAod() {
+ if (mClockViewController != null) {
+ mClockViewController.animateFoldAppear();
+ mLargeClockViewController.animateFoldAppear();
+ }
+ }
+
/**
* If we're presenting a custom clock of just the default one.
*/
@@ -443,7 +448,7 @@
Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, 1) != 0;
if (!mCanShowDoubleLineClock) {
- mUiExecutor.execute(() -> displayClock(KeyguardClockSwitch.SMALL));
+ mUiExecutor.execute(() -> displayClock(KeyguardClockSwitch.SMALL, /* animate */ true));
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 67ef02a..b84cb19 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -866,7 +866,6 @@
((LayerDrawable) mUserSwitcher.getBackground()).getDrawable(1).setAlpha(255);
}
- anchor.setClickable(true);
anchor.setOnClickListener((v) -> {
if (mFalsingManager.isFalseTap(LOW_PENALTY)) return;
@@ -877,8 +876,7 @@
public void onItemClick(AdapterView parent, View view, int pos, long id) {
if (mFalsingManager.isFalseTap(LOW_PENALTY)) return;
- // - 1 to account for the header view
- UserRecord user = adapter.getItem(pos - 1);
+ UserRecord user = adapter.getItem(pos);
if (!user.isCurrent) {
adapter.onUserListItemClicked(user);
}
@@ -907,9 +905,16 @@
== Configuration.ORIENTATION_PORTRAIT) {
updateViewGravity(mViewFlipper, Gravity.CENTER_HORIZONTAL);
updateViewGravity(mUserSwitcherViewGroup, Gravity.CENTER_HORIZONTAL);
+ mUserSwitcherViewGroup.setTranslationY(0);
} else {
updateViewGravity(mViewFlipper, Gravity.RIGHT | Gravity.BOTTOM);
- updateViewGravity(mUserSwitcherViewGroup, Gravity.LEFT | Gravity.TOP);
+ updateViewGravity(mUserSwitcherViewGroup, Gravity.LEFT | Gravity.CENTER_VERTICAL);
+
+ // Attempt to reposition a bit higher to make up for this frame being a bit lower
+ // on the device
+ int yTrans = mView.getContext().getResources().getDimensionPixelSize(
+ R.dimen.status_bar_height);
+ mUserSwitcherViewGroup.setTranslationY(-yTrans);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 986d0de..8bf890d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -123,8 +123,17 @@
* Set which clock should be displayed on the keyguard. The other one will be automatically
* hidden.
*/
- public void displayClock(@ClockSize int clockSize) {
- mKeyguardClockSwitchController.displayClock(clockSize);
+ public void displayClock(@ClockSize int clockSize, boolean animate) {
+ mKeyguardClockSwitchController.displayClock(clockSize, animate);
+ }
+
+ /**
+ * Performs fold to aod animation of the clocks (changes font weight from bold to thin).
+ * This animation is played when AOD is enabled and foldable device is fully folded, it is
+ * displayed on the outer screen
+ */
+ public void animateFoldToAod() {
+ mKeyguardClockSwitchController.animateFoldToAod();
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
index dfb4667..7b6ce3e 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java
@@ -18,12 +18,10 @@
import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListPopupWindow;
import android.widget.ListView;
-import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.plugins.FalsingManager;
@@ -68,12 +66,6 @@
// This will force the popupwindow to show upward instead of drop down
listView.addOnLayoutChangeListener(mLayoutListener);
- TextView header = (TextView) LayoutInflater.from(mContext).inflate(
- R.layout.keyguard_bouncer_user_switcher_item, listView, false);
- header.setText(mContext.getResources().getString(
- R.string.accessibility_multi_user_switch_switcher));
- listView.addHeaderView(header);
-
listView.setOnTouchListener((v, ev) -> {
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
return mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY);
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index cd57af4..6626f59 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -278,10 +278,6 @@
mView.setContentDescription(mUnlockedLabel);
mView.setVisibility(View.VISIBLE);
} else if (mShowAodLockIcon) {
- if (wasShowingUnlock) {
- // transition to the unlock icon first
- mView.updateIcon(ICON_LOCK, false);
- }
mView.updateIcon(ICON_LOCK, true);
mView.setContentDescription(mLockedLabel);
mView.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
index 4aa46f18..58cf35f 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
@@ -16,12 +16,14 @@
package com.android.systemui.communal;
+import static com.android.systemui.communal.dagger.CommunalModule.COMMUNAL_CONDITIONS;
+
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.condition.Monitor;
import com.google.android.collect.Lists;
@@ -31,6 +33,7 @@
import java.util.concurrent.Executor;
import javax.inject.Inject;
+import javax.inject.Named;
/**
* A Monitor for reporting a {@link CommunalSource} presence.
@@ -42,7 +45,7 @@
// A list of {@link Callback} that have registered to receive updates.
private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
- private final CommunalConditionsMonitor mConditionsMonitor;
+ private final Monitor mConditionsMonitor;
private final Executor mExecutor;
private CommunalSource mCurrentSource;
@@ -53,7 +56,7 @@
// Whether the class is currently listening for condition changes.
private boolean mListeningForConditions = false;
- private final CommunalConditionsMonitor.Callback mConditionsCallback =
+ private final Monitor.Callback mConditionsCallback =
allConditionsMet -> {
if (mAllCommunalConditionsMet != allConditionsMet) {
if (DEBUG) Log.d(TAG, "communal conditions changed: " + allConditionsMet);
@@ -66,7 +69,7 @@
@VisibleForTesting
@Inject
public CommunalSourceMonitor(@Main Executor executor,
- CommunalConditionsMonitor communalConditionsMonitor) {
+ @Named(COMMUNAL_CONDITIONS) Monitor communalConditionsMonitor) {
mExecutor = executor;
mConditionsMonitor = communalConditionsMonitor;
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java
deleted file mode 100644
index 1197816..0000000
--- a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.communal.conditions;
-
-
-import static com.android.systemui.communal.dagger.CommunalModule.COMMUNAL_CONDITIONS;
-
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.util.condition.Condition;
-import com.android.systemui.util.condition.Monitor;
-
-import java.util.Set;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-/**
- * A concrete implementation of {@Monitor} with conditions for monitoring when communal mode should
- * be enabled.
- */
-@SysUISingleton
-public class CommunalConditionsMonitor extends Monitor {
- @Inject
- public CommunalConditionsMonitor(
- @Named(COMMUNAL_CONDITIONS) Set<Condition> communalConditions) {
- super(communalConditions);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
index f27ae34..e1f1ac4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
@@ -34,6 +34,8 @@
import com.android.systemui.idle.LightSensorEventsDebounceAlgorithm;
import com.android.systemui.idle.dagger.IdleViewComponent;
import com.android.systemui.util.condition.Condition;
+import com.android.systemui.util.condition.Monitor;
+import com.android.systemui.util.condition.dagger.MonitorComponent;
import java.util.Collections;
import java.util.HashSet;
@@ -135,4 +137,14 @@
return Optional.empty();
}
}
+
+ /** */
+ @Provides
+ @Named(COMMUNAL_CONDITIONS)
+ static Monitor provideCommunalSourceMonitor(
+ @Named(COMMUNAL_CONDITIONS) Set<Condition> communalConditions,
+ MonitorComponent.Factory factory) {
+ final MonitorComponent component = factory.create(communalConditions, new HashSet<>());
+ return component.getMonitor();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 471a327..b2fe3bb 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -37,10 +37,14 @@
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.unfold.FoldAodAnimationController;
+import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.util.AlarmTimeout;
import com.android.systemui.util.wakelock.WakeLock;
import java.util.Calendar;
+import java.util.Optional;
import javax.inject.Inject;
@@ -49,7 +53,8 @@
*/
@DozeScope
public class DozeUi implements DozeMachine.Part, TunerService.Tunable,
- ConfigurationController.ConfigurationListener, StatusBarStateController.StateListener {
+ ConfigurationController.ConfigurationListener, FoldAodAnimationStatus,
+ StatusBarStateController.StateListener {
// if enabled, calls dozeTimeTick() whenever the time changes:
private static final boolean BURN_IN_TESTING_ENABLED = false;
private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min
@@ -57,6 +62,7 @@
private final DozeHost mHost;
private final Handler mHandler;
private final WakeLock mWakeLock;
+ private final FoldAodAnimationController mFoldAodAnimationController;
private DozeMachine mMachine;
private final AlarmTimeout mTimeTicker;
private final boolean mCanAnimateTransition;
@@ -100,6 +106,7 @@
DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor,
DozeLog dozeLog, TunerService tunerService,
StatusBarStateController statusBarStateController,
+ Optional<SysUIUnfoldComponent> sysUiUnfoldComponent,
ConfigurationController configurationController) {
mContext = context;
mWakeLock = wakeLock;
@@ -118,12 +125,23 @@
mConfigurationController = configurationController;
mConfigurationController.addCallback(this);
+
+ mFoldAodAnimationController = sysUiUnfoldComponent
+ .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
+
+ if (mFoldAodAnimationController != null) {
+ mFoldAodAnimationController.addCallback(this);
+ }
}
@Override
public void destroy() {
mTunerService.removeTunable(this);
mConfigurationController.removeCallback(this);
+
+ if (mFoldAodAnimationController != null) {
+ mFoldAodAnimationController.removeCallback(this);
+ }
}
@Override
@@ -142,7 +160,8 @@
&& (mKeyguardShowing || mDozeParameters.shouldControlUnlockedScreenOff())
&& !mHost.isPowerSaveActive();
mDozeParameters.setControlScreenOffAnimation(controlScreenOff);
- mHost.setAnimateScreenOff(controlScreenOff);
+ mHost.setAnimateScreenOff(controlScreenOff
+ && mDozeParameters.shouldAnimateDozingChange());
}
}
@@ -299,4 +318,9 @@
public void onStatePostChange() {
updateAnimateScreenOff();
}
+
+ @Override
+ public void onFoldToAodAnimationChanged() {
+ updateAnimateScreenOff();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerView.java
new file mode 100644
index 0000000..bc1f772
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerView.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+/**
+ * {@link DreamOverlayContainerView} contains a dream overlay and its status bar.
+ */
+public class DreamOverlayContainerView extends ConstraintLayout {
+ public DreamOverlayContainerView(Context context) {
+ this(context, null);
+ }
+
+ public DreamOverlayContainerView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DreamOverlayContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DreamOverlayContainerView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 8f0ea2f..393f039 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -29,11 +29,11 @@
import android.view.WindowManager;
import androidx.annotation.NonNull;
-import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.PhoneWindow;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import java.util.concurrent.Executor;
@@ -54,57 +54,75 @@
private final Executor mExecutor;
// The state controller informs the service of updates to the overlays present.
private final DreamOverlayStateController mStateController;
+ // The component used to resolve dream overlay dependencies.
+ private final DreamOverlayComponent mDreamOverlayComponent;
- // The window is populated once the dream informs the service it has begun dreaming.
- private Window mWindow;
- private ConstraintLayout mLayout;
+ // The dream overlay's content view, which is located below the status bar (in z-order) and is
+ // the space into which widgets are placed.
+ private ViewGroup mDreamOverlayContentView;
private final DreamOverlayStateController.Callback mOverlayStateCallback =
new DreamOverlayStateController.Callback() {
- @Override
- public void onOverlayChanged() {
- mExecutor.execute(() -> reloadOverlaysLocked());
- }
- };
+ @Override
+ public void onOverlayChanged() {
+ mExecutor.execute(() -> reloadOverlaysLocked());
+ }
+ };
// The service listens to view changes in order to declare that input occurring in areas outside
// the overlay should be passed through to the dream underneath.
- private View.OnAttachStateChangeListener mRootViewAttachListener =
+ private final View.OnAttachStateChangeListener mRootViewAttachListener =
new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View v) {
- v.getViewTreeObserver()
- .addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
- }
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ v.getViewTreeObserver()
+ .addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
+ }
- @Override
- public void onViewDetachedFromWindow(View v) {
- v.getViewTreeObserver()
- .removeOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener);
- }
- };
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ v.getViewTreeObserver()
+ .removeOnComputeInternalInsetsListener(
+ mOnComputeInternalInsetsListener);
+ }
+ };
// A hook into the internal inset calculation where we declare the overlays as the only
// touchable regions.
- private ViewTreeObserver.OnComputeInternalInsetsListener mOnComputeInternalInsetsListener =
+ private final ViewTreeObserver.OnComputeInternalInsetsListener
+ mOnComputeInternalInsetsListener =
new ViewTreeObserver.OnComputeInternalInsetsListener() {
- @Override
- public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
- if (mLayout != null) {
- inoutInfo.setTouchableInsets(
- ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
- final Region region = new Region();
- for (int i = 0; i < mLayout.getChildCount(); i++) {
- View child = mLayout.getChildAt(i);
- final Rect rect = new Rect();
- child.getGlobalVisibleRect(rect);
- region.op(rect, Region.Op.UNION);
- }
+ @Override
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
+ if (mDreamOverlayContentView != null) {
+ inoutInfo.setTouchableInsets(
+ ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ final Region region = new Region();
+ for (int i = 0; i < mDreamOverlayContentView.getChildCount(); i++) {
+ View child = mDreamOverlayContentView.getChildAt(i);
+ final Rect rect = new Rect();
+ child.getGlobalVisibleRect(rect);
+ region.op(rect, Region.Op.UNION);
+ }
- inoutInfo.touchableRegion.set(region);
- }
- }
- };
+ inoutInfo.touchableRegion.set(region);
+ }
+ }
+ };
+
+ @Inject
+ public DreamOverlayService(
+ Context context,
+ @Main Executor executor,
+ DreamOverlayStateController overlayStateController,
+ DreamOverlayComponent.Factory dreamOverlayComponentFactory) {
+ mContext = context;
+ mExecutor = executor;
+ mStateController = overlayStateController;
+ mDreamOverlayComponent = dreamOverlayComponentFactory.create();
+
+ mStateController.addCallback(mOverlayStateCallback);
+ }
@Override
public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) {
@@ -112,10 +130,10 @@
}
private void reloadOverlaysLocked() {
- if (mLayout == null) {
+ if (mDreamOverlayContentView == null) {
return;
}
- mLayout.removeAllViews();
+ mDreamOverlayContentView.removeAllViews();
for (OverlayProvider overlayProvider : mStateController.getOverlays()) {
addOverlay(overlayProvider);
}
@@ -129,31 +147,32 @@
* into the dream window.
*/
private void addOverlayWindowLocked(WindowManager.LayoutParams layoutParams) {
- mWindow = new PhoneWindow(mContext);
- mWindow.setAttributes(layoutParams);
- mWindow.setWindowManager(null, layoutParams.token, "DreamOverlay", true);
+ final PhoneWindow window = new PhoneWindow(mContext);
+ window.setAttributes(layoutParams);
+ window.setWindowManager(null, layoutParams.token, "DreamOverlay", true);
- mWindow.setBackgroundDrawable(new ColorDrawable(0));
+ window.setBackgroundDrawable(new ColorDrawable(0));
- mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
- mWindow.requestFeature(Window.FEATURE_NO_TITLE);
+ window.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+ window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+ window.requestFeature(Window.FEATURE_NO_TITLE);
// Hide all insets when the dream is showing
- mWindow.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
- mWindow.setDecorFitsSystemWindows(false);
+ window.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars());
+ window.setDecorFitsSystemWindows(false);
if (DEBUG) {
Log.d(TAG, "adding overlay window to dream");
}
- mLayout = new ConstraintLayout(mContext);
- mLayout.setLayoutParams(new ViewGroup.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
- mLayout.addOnAttachStateChangeListener(mRootViewAttachListener);
- mWindow.setContentView(mLayout);
+ window.setContentView(mDreamOverlayComponent.getDreamOverlayContainerView());
+
+ mDreamOverlayContentView = mDreamOverlayComponent.getDreamOverlayContentView();
+ mDreamOverlayContentView.addOnAttachStateChangeListener(mRootViewAttachListener);
+
+ mDreamOverlayComponent.getDreamOverlayStatusBarViewController().init();
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
- windowManager.addView(mWindow.getDecorView(), mWindow.getAttributes());
+ windowManager.addView(window.getDecorView(), window.getAttributes());
mExecutor.execute(this::reloadOverlaysLocked);
}
@@ -163,11 +182,11 @@
(view, layoutParams) -> {
// Always move UI related work to the main thread.
mExecutor.execute(() -> {
- if (mLayout == null) {
+ if (mDreamOverlayContentView == null) {
return;
}
- mLayout.addView(view, layoutParams);
+ mDreamOverlayContentView.addView(view, layoutParams);
});
},
() -> {
@@ -178,15 +197,6 @@
});
}
- @Inject
- public DreamOverlayService(Context context, @Main Executor executor,
- DreamOverlayStateController overlayStateController) {
- mContext = context;
- mExecutor = executor;
- mStateController = overlayStateController;
- mStateController.addCallback(mOverlayStateCallback);
- }
-
@Override
public void onDestroy() {
mStateController.removeCallback(mOverlayStateCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
new file mode 100644
index 0000000..9847ef6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterView;
+import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
+
+/**
+ * {@link DreamOverlayStatusBarView} is the view responsible for displaying the status bar in a
+ * dream. The status bar includes status icons such as battery and wifi.
+ */
+public class DreamOverlayStatusBarView extends ConstraintLayout implements
+ BatteryStateChangeCallback {
+
+ private BatteryMeterView mBatteryView;
+ private ImageView mWifiStatusView;
+
+ public DreamOverlayStatusBarView(Context context) {
+ this(context, null);
+ }
+
+ public DreamOverlayStatusBarView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public DreamOverlayStatusBarView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public DreamOverlayStatusBarView(
+ Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mBatteryView = Preconditions.checkNotNull(findViewById(R.id.dream_overlay_battery),
+ "R.id.dream_overlay_battery must not be null");
+ mWifiStatusView = Preconditions.checkNotNull(findViewById(R.id.dream_overlay_wifi_status),
+ "R.id.dream_overlay_wifi_status must not be null");
+
+ mWifiStatusView.setImageDrawable(getContext().getDrawable(R.drawable.ic_signal_wifi_off));
+ }
+
+ /**
+ * Whether to show the battery percent text next to the battery status icons.
+ * @param show True if the battery percent text should be shown.
+ */
+ void showBatteryPercentText(boolean show) {
+ mBatteryView.setForceShowPercent(show);
+ }
+
+ /**
+ * Whether to show the wifi status icon.
+ * @param show True if the wifi status icon should be shown.
+ */
+ void showWifiStatus(boolean show) {
+ // Only show the wifi status icon when wifi isn't available.
+ mWifiStatusView.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
new file mode 100644
index 0000000..5674b9f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams;
+
+import android.annotation.IntDef;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+
+import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.dreams.dagger.DreamOverlayComponent;
+import com.android.systemui.dreams.dagger.DreamOverlayModule;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.util.ViewController;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * View controller for {@link DreamOverlayStatusBarView}.
+ */
+@DreamOverlayComponent.DreamOverlayScope
+public class DreamOverlayStatusBarViewController extends ViewController<DreamOverlayStatusBarView> {
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "WIFI_STATUS_" }, value = {
+ WIFI_STATUS_UNKNOWN,
+ WIFI_STATUS_UNAVAILABLE,
+ WIFI_STATUS_AVAILABLE
+ })
+ private @interface WifiStatus {}
+ private static final int WIFI_STATUS_UNKNOWN = 0;
+ private static final int WIFI_STATUS_UNAVAILABLE = 1;
+ private static final int WIFI_STATUS_AVAILABLE = 2;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "BATTERY_STATUS_" }, value = {
+ BATTERY_STATUS_UNKNOWN,
+ BATTERY_STATUS_NOT_CHARGING,
+ BATTERY_STATUS_CHARGING
+ })
+ private @interface BatteryStatus {}
+ private static final int BATTERY_STATUS_UNKNOWN = 0;
+ private static final int BATTERY_STATUS_NOT_CHARGING = 1;
+ private static final int BATTERY_STATUS_CHARGING = 2;
+
+ private final BatteryController mBatteryController;
+ private final BatteryMeterViewController mBatteryMeterViewController;
+ private final ConnectivityManager mConnectivityManager;
+ private final boolean mShowPercentAvailable;
+
+ private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
+
+ private final NetworkCallback mNetworkCallback = new NetworkCallback() {
+ @Override
+ public void onCapabilitiesChanged(
+ Network network, NetworkCapabilities networkCapabilities) {
+ onWifiAvailabilityChanged(
+ networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
+ }
+
+ @Override
+ public void onAvailable(Network network) {
+ onWifiAvailabilityChanged(true);
+ }
+
+ @Override
+ public void onLost(Network network) {
+ onWifiAvailabilityChanged(false);
+ }
+ };
+
+ private final BatteryController.BatteryStateChangeCallback mBatteryStateChangeCallback =
+ new BatteryController.BatteryStateChangeCallback() {
+ @Override
+ public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
+ DreamOverlayStatusBarViewController.this.onBatteryLevelChanged(charging);
+ }
+ };
+
+ private @WifiStatus int mWifiStatus = WIFI_STATUS_UNKNOWN;
+ private @BatteryStatus int mBatteryStatus = BATTERY_STATUS_UNKNOWN;
+
+ @Inject
+ public DreamOverlayStatusBarViewController(
+ Context context,
+ DreamOverlayStatusBarView view,
+ BatteryController batteryController,
+ @Named(DreamOverlayModule.DREAM_OVERLAY_BATTERY_CONTROLLER)
+ BatteryMeterViewController batteryMeterViewController,
+ ConnectivityManager connectivityManager) {
+ super(view);
+ mBatteryController = batteryController;
+ mBatteryMeterViewController = batteryMeterViewController;
+ mConnectivityManager = connectivityManager;
+
+ mShowPercentAvailable = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_battery_percentage_setting_available);
+ }
+
+ @Override
+ protected void onInit() {
+ super.onInit();
+ mBatteryMeterViewController.init();
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mBatteryController.addCallback(mBatteryStateChangeCallback);
+ mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
+
+ NetworkCapabilities capabilities =
+ mConnectivityManager.getNetworkCapabilities(
+ mConnectivityManager.getActiveNetwork());
+ onWifiAvailabilityChanged(
+ capabilities != null
+ && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mBatteryController.removeCallback(mBatteryStateChangeCallback);
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+ }
+
+ /**
+ * Wifi availability has changed. Update the wifi status icon as appropriate.
+ * @param available Whether wifi is available.
+ */
+ private void onWifiAvailabilityChanged(boolean available) {
+ final int newWifiStatus = available ? WIFI_STATUS_AVAILABLE : WIFI_STATUS_UNAVAILABLE;
+ if (mWifiStatus != newWifiStatus) {
+ mWifiStatus = newWifiStatus;
+ mView.showWifiStatus(mWifiStatus == WIFI_STATUS_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * The battery level has changed. Update the battery status icon as appropriate.
+ * @param charging Whether the battery is currently charging.
+ */
+ private void onBatteryLevelChanged(boolean charging) {
+ final int newBatteryStatus =
+ charging ? BATTERY_STATUS_CHARGING : BATTERY_STATUS_NOT_CHARGING;
+ if (mBatteryStatus != newBatteryStatus) {
+ mBatteryStatus = newBatteryStatus;
+ mView.showBatteryPercentText(
+ mBatteryStatus == BATTERY_STATUS_CHARGING && mShowPercentAvailable);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 7bf2361..ff5beb5 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -21,8 +21,6 @@
/**
* Dagger Module providing Communal-related functionality.
*/
-@Module(subcomponents = {
- AppWidgetOverlayComponent.class,
-})
+@Module(subcomponents = {AppWidgetOverlayComponent.class, DreamOverlayComponent.class})
public interface DreamModule {
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
new file mode 100644
index 0000000..a3a446a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.view.ViewGroup;
+
+import com.android.systemui.dreams.DreamOverlayContainerView;
+import com.android.systemui.dreams.DreamOverlayStatusBarViewController;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Scope;
+
+import dagger.Subcomponent;
+
+/**
+ * Dagger subcomponent for {@link DreamOverlayModule}.
+ */
+@Subcomponent(modules = {DreamOverlayModule.class})
+@DreamOverlayComponent.DreamOverlayScope
+public interface DreamOverlayComponent {
+ /** Simple factory for {@link DreamOverlayComponent}. */
+ @Subcomponent.Factory
+ interface Factory {
+ DreamOverlayComponent create();
+ }
+
+ /** Scope annotation for singleton items within the {@link DreamOverlayComponent}. */
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface DreamOverlayScope {}
+
+ /** Builds a {@link DreamOverlayContainerView} */
+ @DreamOverlayScope
+ DreamOverlayContainerView getDreamOverlayContainerView();
+
+ /** Builds a content view for dream overlays */
+ @DreamOverlayScope
+ ViewGroup getDreamOverlayContentView();
+
+ /** Builds a {@link DreamOverlayStatusBarViewController}. */
+ @DreamOverlayScope
+ DreamOverlayStatusBarViewController getDreamOverlayStatusBarViewController();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
new file mode 100644
index 0000000..d0a8fad
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.dagger;
+
+import android.content.ContentResolver;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.R;
+import com.android.systemui.battery.BatteryMeterView;
+import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.DreamOverlayContainerView;
+import com.android.systemui.dreams.DreamOverlayStatusBarView;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.tuner.TunerService;
+
+import javax.inject.Named;
+
+import dagger.Module;
+import dagger.Provides;
+
+/** Dagger module for {@link DreamOverlayComponent}. */
+@Module
+public abstract class DreamOverlayModule {
+ private static final String DREAM_OVERLAY_BATTERY_VIEW = "dream_overlay_battery_view";
+ public static final String DREAM_OVERLAY_BATTERY_CONTROLLER =
+ "dream_overlay_battery_controller";
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ public static DreamOverlayContainerView providesDreamOverlayContainerView(
+ LayoutInflater layoutInflater) {
+ return Preconditions.checkNotNull((DreamOverlayContainerView)
+ layoutInflater.inflate(R.layout.dream_overlay_container, null),
+ "R.layout.dream_layout_container could not be properly inflated");
+ }
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ public static ViewGroup providesDreamOverlayContentView(DreamOverlayContainerView view) {
+ return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_content),
+ "R.id.dream_overlay_content must not be null");
+ }
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ public static DreamOverlayStatusBarView providesDreamOverlayStatusBarView(
+ DreamOverlayContainerView view) {
+ return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_status_bar),
+ "R.id.status_bar must not be null");
+ }
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ @Named(DREAM_OVERLAY_BATTERY_VIEW)
+ static BatteryMeterView providesBatteryMeterView(DreamOverlayContainerView view) {
+ return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_battery),
+ "R.id.battery must not be null");
+ }
+
+ /** */
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ @Named(DREAM_OVERLAY_BATTERY_CONTROLLER)
+ static BatteryMeterViewController providesBatteryMeterViewController(
+ @Named(DREAM_OVERLAY_BATTERY_VIEW) BatteryMeterView batteryMeterView,
+ ConfigurationController configurationController,
+ TunerService tunerService,
+ BroadcastDispatcher broadcastDispatcher,
+ @Main Handler mainHandler,
+ ContentResolver contentResolver,
+ BatteryController batteryController) {
+ return new BatteryMeterViewController(
+ batteryMeterView,
+ configurationController,
+ tunerService,
+ broadcastDispatcher,
+ mainHandler,
+ contentResolver,
+ batteryController);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index c592431..0c9e315 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -122,6 +122,7 @@
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.unfold.FoldAodAnimationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
import com.android.systemui.util.DeviceConfigProxy;
@@ -131,7 +132,6 @@
import java.util.ArrayList;
import java.util.Optional;
import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicInteger;
import dagger.Lazy;
@@ -439,7 +439,7 @@
private boolean mInGestureNavigationMode;
private boolean mWakeAndUnlocking;
- private IKeyguardDrawnCallback mDrawnCallback;
+ private Runnable mWakeAndUnlockingDrawnCallback;
private CharSequence mCustomMessage;
/**
@@ -817,7 +817,8 @@
private DozeParameters mDozeParameters;
private final Optional<UnfoldLightRevealOverlayAnimation> mUnfoldLightRevealAnimation;
- private final AtomicInteger mPendingDrawnTasks = new AtomicInteger();
+ private final Optional<FoldAodAnimationController> mFoldAodAnimationController;
+ private final PendingDrawnTasksContainer mPendingDrawnTasks = new PendingDrawnTasksContainer();
private final KeyguardStateController mKeyguardStateController;
private final Lazy<KeyguardUnlockAnimationController> mKeyguardUnlockAnimationControllerLazy;
@@ -877,8 +878,12 @@
mInGestureNavigationMode = QuickStepContract.isGesturalMode(mode);
}));
mDozeParameters = dozeParameters;
- mUnfoldLightRevealAnimation = unfoldComponent.map(
- c -> c.getUnfoldLightRevealOverlayAnimation());
+
+ mUnfoldLightRevealAnimation = unfoldComponent
+ .map(SysUIUnfoldComponent::getUnfoldLightRevealOverlayAnimation);
+ mFoldAodAnimationController = unfoldComponent
+ .map(SysUIUnfoldComponent::getFoldAodAnimationController);
+
mStatusBarStateController = statusBarStateController;
statusBarStateController.addCallback(this);
@@ -1069,7 +1074,7 @@
mDeviceInteractive = false;
mGoingToSleep = false;
mWakeAndUnlocking = false;
- mAnimatingScreenOff = mDozeParameters.shouldControlUnlockedScreenOff();
+ mAnimatingScreenOff = mDozeParameters.shouldAnimateDozingChange();
resetKeyguardDonePendingLocked();
mHideAnimationRun = false;
@@ -2230,14 +2235,14 @@
IRemoteAnimationRunner runner = mKeyguardExitAnimationRunner;
mKeyguardExitAnimationRunner = null;
- if (mWakeAndUnlocking && mDrawnCallback != null) {
+ if (mWakeAndUnlocking && mWakeAndUnlockingDrawnCallback != null) {
// Hack level over 9000: To speed up wake-and-unlock sequence, force it to report
// the next draw from here so we don't have to wait for window manager to signal
// this to our ViewRootImpl.
mKeyguardViewControllerLazy.get().getViewRootImpl().setReportNextDraw();
- notifyDrawn(mDrawnCallback);
- mDrawnCallback = null;
+ mWakeAndUnlockingDrawnCallback.run();
+ mWakeAndUnlockingDrawnCallback = null;
}
LatencyTracker.getInstance(mContext)
@@ -2573,31 +2578,27 @@
synchronized (KeyguardViewMediator.this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurningOn");
- if (mUnfoldLightRevealAnimation.isPresent()) {
- mPendingDrawnTasks.set(2); // unfold overlay and keyguard drawn
+ mPendingDrawnTasks.reset();
+ if (mUnfoldLightRevealAnimation.isPresent()) {
mUnfoldLightRevealAnimation.get()
- .onScreenTurningOn(() -> {
- if (mPendingDrawnTasks.decrementAndGet() == 0) {
- try {
- callback.onDrawn();
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception calling onDrawn():", e);
- }
- }
- });
- } else {
- mPendingDrawnTasks.set(1); // only keyguard drawn
+ .onScreenTurningOn(mPendingDrawnTasks.registerTask("unfold-reveal"));
+ }
+
+ if (mFoldAodAnimationController.isPresent()) {
+ mFoldAodAnimationController.get()
+ .onScreenTurningOn(mPendingDrawnTasks.registerTask("fold-to-aod"));
}
mKeyguardViewControllerLazy.get().onScreenTurningOn();
if (callback != null) {
if (mWakeAndUnlocking) {
- mDrawnCallback = callback;
- } else {
- notifyDrawn(callback);
+ mWakeAndUnlockingDrawnCallback =
+ mPendingDrawnTasks.registerTask("wake-and-unlocking");
}
}
+
+ mPendingDrawnTasks.onTasksComplete(() -> notifyDrawn(callback));
}
Trace.endSection();
}
@@ -2606,6 +2607,8 @@
Trace.beginSection("KeyguardViewMediator#handleNotifyScreenTurnedOn");
synchronized (this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOn");
+
+ mPendingDrawnTasks.reset();
mKeyguardViewControllerLazy.get().onScreenTurnedOn();
}
Trace.endSection();
@@ -2614,18 +2617,18 @@
private void handleNotifyScreenTurnedOff() {
synchronized (this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOff");
- mDrawnCallback = null;
+ mWakeAndUnlockingDrawnCallback = null;
}
}
private void notifyDrawn(final IKeyguardDrawnCallback callback) {
Trace.beginSection("KeyguardViewMediator#notifyDrawn");
- if (mPendingDrawnTasks.decrementAndGet() == 0) {
- try {
+ try {
+ if (callback != null) {
callback.onDrawn();
- } catch (RemoteException e) {
- Slog.w(TAG, "Exception calling onDrawn():", e);
}
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception calling onDrawn():", e);
}
Trace.endSection();
}
@@ -2784,9 +2787,9 @@
pw.print(" mHideAnimationRun: "); pw.println(mHideAnimationRun);
pw.print(" mPendingReset: "); pw.println(mPendingReset);
pw.print(" mPendingLock: "); pw.println(mPendingLock);
- pw.print(" mPendingDrawnTasks: "); pw.println(mPendingDrawnTasks.get());
+ pw.print(" mPendingDrawnTasks: "); pw.println(mPendingDrawnTasks.getPendingCount());
pw.print(" mWakeAndUnlocking: "); pw.println(mWakeAndUnlocking);
- pw.print(" mDrawnCallback: "); pw.println(mDrawnCallback);
+ pw.print(" mDrawnCallback: "); pw.println(mWakeAndUnlockingDrawnCallback);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt
new file mode 100644
index 0000000..bccd106
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/PendingDrawnTasksContainer.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard
+
+import android.os.Trace
+import java.util.concurrent.atomic.AtomicInteger
+import java.util.concurrent.atomic.AtomicReference
+
+/**
+ * Allows to wait for multiple callbacks and notify when the last one is executed
+ */
+class PendingDrawnTasksContainer {
+
+ private lateinit var pendingDrawnTasksCount: AtomicInteger
+ private var completionCallback: AtomicReference<Runnable> = AtomicReference()
+
+ /**
+ * Registers a task that we should wait for
+ * @return a runnable that should be invoked when the task is finished
+ */
+ fun registerTask(name: String): Runnable {
+ pendingDrawnTasksCount.incrementAndGet()
+
+ if (ENABLE_TRACE) {
+ Trace.beginAsyncSection("PendingDrawnTasksContainer#$name", 0)
+ }
+
+ return Runnable {
+ if (pendingDrawnTasksCount.decrementAndGet() == 0) {
+ val onComplete = completionCallback.getAndSet(null)
+ onComplete?.run()
+
+ if (ENABLE_TRACE) {
+ Trace.endAsyncSection("PendingDrawnTasksContainer#$name", 0)
+ }
+ }
+ }
+ }
+
+ /**
+ * Clears state and initializes the container
+ */
+ fun reset() {
+ // Create new objects in case if there are pending callbacks from the previous invocations
+ completionCallback = AtomicReference()
+ pendingDrawnTasksCount = AtomicInteger(0)
+ }
+
+ /**
+ * Starts waiting for all tasks to be completed
+ * When all registered tasks complete it will invoke the [onComplete] callback
+ */
+ fun onTasksComplete(onComplete: Runnable) {
+ completionCallback.set(onComplete)
+
+ if (pendingDrawnTasksCount.get() == 0) {
+ val currentOnComplete = completionCallback.getAndSet(null)
+ currentOnComplete?.run()
+ }
+ }
+
+ /**
+ * Returns current pending tasks count
+ */
+ fun getPendingCount(): Int = pendingDrawnTasksCount.get()
+}
+
+private const val ENABLE_TRACE = false
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 5bd02cc..10efec3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -48,6 +48,7 @@
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
@@ -562,7 +563,8 @@
return this;
}
- CustomTile build() {
+ @VisibleForTesting
+ public CustomTile build() {
if (mUserContext == null) {
throw new NullPointerException("UserContext cannot be null");
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index ac95bf5..c2a9e3a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -169,7 +169,7 @@
mFgsManagerTileProvider = fgsManagerTileProvider;
}
- public QSTile createTile(String tileSpec) {
+ public final QSTile createTile(String tileSpec) {
QSTileImpl tile = createTileInternal(tileSpec);
if (tile != null) {
tile.initialize();
@@ -178,7 +178,7 @@
return tile;
}
- private QSTileImpl createTileInternal(String tileSpec) {
+ protected QSTileImpl createTileInternal(String tileSpec) {
// Stock tiles.
switch (tileSpec) {
case "wifi":
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FgsManagerTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FgsManagerTile.kt
index 75cf4d1..939a297 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FgsManagerTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FgsManagerTile.kt
@@ -54,7 +54,7 @@
qsLogger: QSLogger?,
private val fgsManagerDialogFactory: FgsManagerDialogFactory,
private val runningFgsController: RunningFgsController
-) : QSTileImpl<QSTile.State?>(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
+) : QSTileImpl<QSTile.State>(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger), RunningFgsController.Callback {
override fun handleInitialize() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 4908d4f..dd742b8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -90,6 +90,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
@@ -485,6 +486,11 @@
private Map<Integer, CharSequence> getUniqueSubscriptionDisplayNames(Context context) {
class DisplayInfo {
+ DisplayInfo(SubscriptionInfo subscriptionInfo, CharSequence originalName) {
+ this.subscriptionInfo = subscriptionInfo;
+ this.originalName = originalName;
+ }
+
public SubscriptionInfo subscriptionInfo;
public CharSequence originalName;
public CharSequence uniqueName;
@@ -498,12 +504,7 @@
// Filter out null values.
return (i != null && i.getDisplayName() != null);
})
- .map(i -> {
- DisplayInfo info = new DisplayInfo();
- info.subscriptionInfo = i;
- info.originalName = i.getDisplayName().toString().trim();
- return info;
- });
+ .map(i -> new DisplayInfo(i, i.getDisplayName().toString().trim()));
// A Unique set of display names
Set<CharSequence> uniqueNames = new HashSet<>();
@@ -582,7 +583,7 @@
return "";
}
- int resId = mapIconSets(config).get(iconKey).dataContentDescription;
+ int resId = Objects.requireNonNull(mapIconSets(config).get(iconKey)).dataContentDescription;
if (isCarrierNetworkActive()) {
SignalIcon.MobileIconGroup carrierMergedWifiIconGroup =
TelephonyIcons.CARRIER_MERGED_WIFI;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
index f8d6c6d..b2e15f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/BypassHeadsUpNotifier.kt
@@ -25,8 +25,8 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.tuner.TunerService
@@ -44,7 +44,7 @@
private val headsUpManager: HeadsUpManagerPhone,
private val notificationLockscreenUserManager: NotificationLockscreenUserManager,
private val mediaManager: NotificationMediaManager,
- private val entryManager: NotificationEntryManager,
+ private val commonNotifCollection: CommonNotifCollection,
tunerService: TunerService
) : StatusBarStateController.StateListener, NotificationMediaManager.MediaListener {
@@ -77,8 +77,7 @@
override fun onPrimaryMetadataOrStateChanged(metadata: MediaMetadata?, state: Int) {
val previous = currentMediaEntry
- var newEntry = entryManager
- .getActiveNotificationUnfiltered(mediaManager.mediaNotificationKey)
+ var newEntry = commonNotifCollection.getEntry(mediaManager.mediaNotificationKey)
if (!NotificationMediaManager.isPlayingState(state)) {
newEntry = null
}
@@ -112,7 +111,7 @@
// filter notifications invisible on Keyguard
return false
}
- if (entryManager.getActiveNotificationUnfiltered(entry.key) != null) {
+ if (commonNotifCollection.getEntry(entry.key) != null) {
// filter notifications not the active list currently
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index e1dbf4e..518788b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -669,6 +669,7 @@
public void setIsRemoteInputActive(boolean isActive) {
mIsRemoteInputActive = isActive;
+ updateFooter();
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index e3a4bf0..2dc9276 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -51,6 +51,7 @@
public static final int ANIMATION_DURATION_CLOSE_REMOTE_INPUT = 150;
public static final int ANIMATION_DURATION_HEADS_UP_APPEAR = 400;
public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 400;
+ public static final int ANIMATION_DURATION_FOLD_TO_AOD = 600;
public static final int ANIMATION_DURATION_PULSE_APPEAR =
KeyguardSliceView.DEFAULT_ANIM_DURATION;
public static final int ANIMATION_DURATION_BLOCKING_HELPER_FADE = 240;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 4b8b580..d87a024 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -243,6 +243,10 @@
return mScreenOffAnimationController.shouldShowLightRevealScrim();
}
+ public boolean shouldAnimateDozingChange() {
+ return mScreenOffAnimationController.shouldAnimateDozingChange();
+ }
+
/**
* Whether we're capable of controlling the screen off animation if we want to. This isn't
* possible if AOD isn't even enabled or if the flag is disabled.
@@ -324,6 +328,7 @@
for (Callback callback : mCallbacks) {
callback.onAlwaysOnChange();
}
+ mScreenOffAnimationController.onAlwaysOnChanged(getAlwaysOn());
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 097c0d1..3b7063e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -26,6 +26,7 @@
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
import static com.android.keyguard.KeyguardClockSwitch.LARGE;
import static com.android.keyguard.KeyguardClockSwitch.SMALL;
+import static com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
@@ -34,6 +35,7 @@
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
+import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_FOLD_TO_AOD;
import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_CLOSED;
import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_OPEN;
import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_OPENING;
@@ -1423,11 +1425,12 @@
.getVisibleNotificationCount() != 0 || mMediaDataManager.hasActiveMedia();
boolean splitShadeWithActiveMedia =
mShouldUseSplitNotificationShade && mMediaDataManager.hasActiveMedia();
+ boolean shouldAnimateClockChange = mScreenOffAnimationController.shouldAnimateClockChange();
if ((hasVisibleNotifications && !mShouldUseSplitNotificationShade)
|| (splitShadeWithActiveMedia && !mDozing)) {
- mKeyguardStatusViewController.displayClock(SMALL);
+ mKeyguardStatusViewController.displayClock(SMALL, shouldAnimateClockChange);
} else {
- mKeyguardStatusViewController.displayClock(LARGE);
+ mKeyguardStatusViewController.displayClock(LARGE, shouldAnimateClockChange);
}
updateKeyguardStatusViewAlignment(true /* animate */);
int userIconHeight = mKeyguardQsUserSwitchController != null
@@ -1463,7 +1466,7 @@
mKeyguardStatusViewController.isClockTopAligned());
mClockPositionAlgorithm.run(mClockPositionResult);
boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
- boolean animateClock = animate || mAnimateNextPositionUpdate;
+ boolean animateClock = (animate || mAnimateNextPositionUpdate) && shouldAnimateClockChange;
mKeyguardStatusViewController.updatePosition(
mClockPositionResult.clockX, mClockPositionResult.clockY,
mClockPositionResult.clockScale, animateClock);
@@ -3821,6 +3824,45 @@
}
}
+ /**
+ * Updates the views to the initial state for the fold to AOD animation
+ */
+ public void prepareFoldToAodAnimation() {
+ // Force show AOD UI even if we are not locked
+ showAodUi();
+
+ // Move the content of the AOD all the way to the left
+ // so we can animate to the initial position
+ final int translationAmount = mView.getResources().getDimensionPixelSize(
+ R.dimen.below_clock_padding_start);
+ mView.setTranslationX(-translationAmount);
+ mView.setAlpha(0);
+ }
+
+ /**
+ * Starts fold to AOD animation
+ */
+ public void startFoldToAodAnimation(Runnable endAction) {
+ mView.animate()
+ .translationX(0)
+ .alpha(1f)
+ .setDuration(ANIMATION_DURATION_FOLD_TO_AOD)
+ .setInterpolator(EMPHASIZED_DECELERATE)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ endAction.run();
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ endAction.run();
+ }
+ })
+ .start();
+
+ mKeyguardStatusViewController.animateFoldToAod();
+ }
+
/** */
public void setImportantForAccessibility(int mode) {
mView.setImportantForAccessibility(mode);
@@ -3935,6 +3977,10 @@
mView.setAlpha(alpha);
}
+ public void resetTranslation() {
+ mView.setTranslationX(0f);
+ }
+
public ViewPropertyAnimator fadeOut(long startDelayMs, long durationMs, Runnable endAction) {
return mView.animate().alpha(0).setStartDelay(startDelayMs).setDuration(
durationMs).setInterpolator(Interpolators.ALPHA_OUT).withLayer().withEndAction(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index f67d181..1e71ceb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -38,7 +38,6 @@
import com.android.systemui.R;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.window.StatusBarWindowView;
import com.android.systemui.util.leak.RotationUtils;
import java.util.Objects;
@@ -76,6 +75,7 @@
@Override
public void onFinishInflate() {
+ super.onFinishInflate();
mBattery = findViewById(R.id.battery);
mClock = findViewById(R.id.clock);
mCutoutSpace = findViewById(R.id.cutout_space_view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
index 497e7d7..e806ca0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
@@ -19,16 +19,23 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.unfold.FoldAodAnimationController
+import com.android.systemui.unfold.SysUIUnfoldComponent
+import java.util.Optional
import javax.inject.Inject
@SysUISingleton
class ScreenOffAnimationController @Inject constructor(
+ sysUiUnfoldComponent: Optional<SysUIUnfoldComponent>,
unlockedScreenOffAnimation: UnlockedScreenOffAnimationController,
private val wakefulnessLifecycle: WakefulnessLifecycle,
) : WakefulnessLifecycle.Observer {
- // TODO(b/202844967) add fold to aod animation here
- private val animations: List<ScreenOffAnimation> = listOf(unlockedScreenOffAnimation)
+ private val foldToAodAnimation: FoldAodAnimationController? = sysUiUnfoldComponent
+ .orElse(null)?.getFoldAodAnimationController()
+
+ private val animations: List<ScreenOffAnimation> =
+ listOfNotNull(foldToAodAnimation, unlockedScreenOffAnimation)
fun initialize(statusBar: StatusBar, lightRevealScrim: LightRevealScrim) {
animations.forEach { it.initialize(statusBar, lightRevealScrim) }
@@ -43,6 +50,19 @@
}
/**
+ * Called when opaqueness of the light reveal scrim has change
+ * When [isOpaque] is true then scrim is visible and covers the screen
+ */
+ fun onScrimOpaqueChanged(isOpaque: Boolean) =
+ animations.forEach { it.onScrimOpaqueChanged(isOpaque) }
+
+ /**
+ * Called when always on display setting changed
+ */
+ fun onAlwaysOnChanged(alwaysOn: Boolean) =
+ animations.forEach { it.onAlwaysOnChanged(alwaysOn) }
+
+ /**
* If returns true we are taking over the screen off animation from display manager to SysUI.
* We can play our custom animation instead of default fade out animation.
*/
@@ -103,6 +123,12 @@
animations.any { it.isAnimationPlaying() }
/**
+ * Return true to ignore requests to hide keyguard
+ */
+ fun isKeyguardHideDelayed(): Boolean =
+ animations.any { it.isKeyguardHideDelayed() }
+
+ /**
* Return true to make the StatusBar expanded so we can animate [LightRevealScrim]
*/
fun shouldShowLightRevealScrim(): Boolean =
@@ -145,6 +171,19 @@
*/
fun shouldAnimateAodIcons(): Boolean =
animations.all { it.shouldAnimateAodIcons() }
+
+ /**
+ * Return true to animate doze state change, if returns false dozing will be applied without
+ * animation (sends only 0.0f or 1.0f dozing progress)
+ */
+ fun shouldAnimateDozingChange(): Boolean =
+ animations.all { it.shouldAnimateDozingChange() }
+
+ /**
+ * Return true to animate large <-> small clock transition
+ */
+ fun shouldAnimateClockChange(): Boolean =
+ animations.all { it.shouldAnimateClockChange() }
}
interface ScreenOffAnimation {
@@ -158,11 +197,17 @@
fun shouldPlayAnimation(): Boolean = false
fun isAnimationPlaying(): Boolean = false
+ fun onScrimOpaqueChanged(isOpaque: Boolean) {}
+ fun onAlwaysOnChanged(alwaysOn: Boolean) {}
+
fun shouldAnimateInKeyguard(): Boolean = false
fun animateInKeyguard(keyguardView: View, after: Runnable) = after.run()
+ fun isKeyguardHideDelayed(): Boolean = false
fun shouldHideScrimOnWakeUp(): Boolean = false
fun overrideNotificationsDozeAmount(): Boolean = false
fun shouldShowAodIconsWhenShade(): Boolean = false
fun shouldAnimateAodIcons(): Boolean = true
+ fun shouldAnimateDozingChange(): Boolean = true
+ fun shouldAnimateClockChange(): Boolean = true
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
index f6e19bf..8cf7288 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -49,8 +49,9 @@
}
private val combinedHeaders = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)
- // TODO(b/194178072) Handle RSSI hiding when multi carrier
private val iconManager: StatusBarIconController.TintedIconManager
+ private val iconContainer: StatusIconContainer
+ private val carrierIconSlots: List<String>
private val qsCarrierGroupController: QSCarrierGroupController
private var visible = false
set(value) {
@@ -117,10 +118,19 @@
batteryMeterViewController.ignoreTunerUpdates()
batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
- val iconContainer: StatusIconContainer = statusBar.findViewById(R.id.statusIcons)
+ iconContainer = statusBar.findViewById(R.id.statusIcons)
iconManager = StatusBarIconController.TintedIconManager(iconContainer, featureFlags)
iconManager.setTint(Utils.getColorAttrDefaultColor(statusBar.context,
android.R.attr.textColorPrimary))
+
+ carrierIconSlots = if (featureFlags.isEnabled(Flags.COMBINED_STATUS_BAR_SIGNAL_ICONS)) {
+ listOf(
+ statusBar.context.getString(com.android.internal.R.string.status_bar_no_calling),
+ statusBar.context.getString(com.android.internal.R.string.status_bar_call_strength)
+ )
+ } else {
+ listOf(statusBar.context.getString(com.android.internal.R.string.status_bar_mobile))
+ }
qsCarrierGroupController = qsCarrierGroupControllerBuilder
.setQSCarrierGroup(statusBar.findViewById(R.id.carrier_group))
.build()
@@ -185,9 +195,20 @@
private fun updateListeners() {
qsCarrierGroupController.setListening(visible)
if (visible) {
+ updateSingleCarrier(qsCarrierGroupController.isSingleCarrier)
+ qsCarrierGroupController.setOnSingleCarrierChangedListener { updateSingleCarrier(it) }
statusBarIconController.addIconGroup(iconManager)
} else {
+ qsCarrierGroupController.setOnSingleCarrierChangedListener(null)
statusBarIconController.removeIconGroup(iconManager)
}
}
+
+ private fun updateSingleCarrier(singleCarrier: Boolean) {
+ if (singleCarrier) {
+ iconContainer.removeIgnoredSlots(carrierIconSlots)
+ } else {
+ iconContainer.addIgnoredSlots(carrierIconSlots)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 6c0b717..3312996 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -341,7 +341,7 @@
mStatusBarWindowState = state;
mStatusBarWindowHidden = state == WINDOW_STATE_HIDDEN;
mStatusBarHideIconsForBouncerManager.setStatusBarWindowHidden(mStatusBarWindowHidden);
- if (getStatusBarView() != null) {
+ if (mStatusBarView != null) {
// Should #updateHideIconsForBouncer always be called, regardless of whether we have a
// status bar view? If so, we can make #updateHideIconsForBouncer private.
mStatusBarHideIconsForBouncerManager.updateHideIconsForBouncer(/* animate= */ false);
@@ -1124,23 +1124,18 @@
// Set up CollapsedStatusBarFragment and PhoneStatusBarView
StatusBarInitializer initializer = mStatusBarComponent.getStatusBarInitializer();
initializer.setStatusBarViewUpdatedListener(
- new StatusBarInitializer.OnStatusBarViewUpdatedListener() {
- @Override
- public void onStatusBarViewUpdated(
- @NonNull PhoneStatusBarView statusBarView,
- @NonNull PhoneStatusBarViewController statusBarViewController) {
- mStatusBarView = statusBarView;
- mPhoneStatusBarViewController = statusBarViewController;
- mNotificationShadeWindowViewController.setStatusBarView(mStatusBarView);
- // Ensure we re-propagate panel expansion values to the panel controller and
- // any listeners it may have, such as PanelBar. This will also ensure we
- // re-display the notification panel if necessary (for example, if
- // a heads-up notification was being displayed and should continue being
- // displayed).
- mNotificationPanelViewController.updatePanelExpansionAndVisibility();
- setBouncerShowingForStatusBarComponents(mBouncerShowing);
- checkBarModes();
- }
+ (statusBarView, statusBarViewController) -> {
+ mStatusBarView = statusBarView;
+ mPhoneStatusBarViewController = statusBarViewController;
+ mNotificationShadeWindowViewController.setStatusBarView(mStatusBarView);
+ // Ensure we re-propagate panel expansion values to the panel controller and
+ // any listeners it may have, such as PanelBar. This will also ensure we
+ // re-display the notification panel if necessary (for example, if
+ // a heads-up notification was being displayed and should continue being
+ // displayed).
+ mNotificationPanelViewController.updatePanelExpansionAndVisibility();
+ setBouncerShowingForStatusBarComponents(mBouncerShowing);
+ checkBarModes();
});
initializer.initializeStatusBar(mStatusBarComponent);
@@ -1199,6 +1194,8 @@
Runnable updateOpaqueness = () -> {
mNotificationShadeWindowController.setLightRevealScrimOpaque(
mLightRevealScrim.isScrimOpaque());
+ mScreenOffAnimationController
+ .onScrimOpaqueChanged(mLightRevealScrim.isScrimOpaque());
};
if (opaque) {
// Delay making the view opaque for a frame, because it needs some time to render
@@ -1578,10 +1575,6 @@
Trace.endSection();
}
- protected PhoneStatusBarView getStatusBarView() {
- return mStatusBarView;
- }
-
public NotificationShadeWindowView getNotificationShadeWindowView() {
return mNotificationShadeWindowView;
}
@@ -2915,7 +2908,17 @@
showKeyguardImpl();
}
} else {
- return hideKeyguardImpl(force);
+ // During folding a foldable device this might be called as a result of
+ // 'onScreenTurnedOff' call for the inner display.
+ // In this case:
+ // * When phone is locked on folding: it doesn't make sense to hide keyguard as it
+ // will be immediately locked again
+ // * When phone is unlocked: we still don't want to execute hiding of the keyguard
+ // as the animation could prepare 'fake AOD' interface (without actually
+ // transitioning to keyguard state) and this might reset the view states
+ if (!mScreenOffAnimationController.isKeyguardHideDelayed()) {
+ return hideKeyguardImpl(force);
+ }
}
return false;
}
@@ -3078,6 +3081,7 @@
mNotificationPanelViewController.onAffordanceLaunchEnded();
mNotificationPanelViewController.cancelAnimation();
mNotificationPanelViewController.setAlpha(1f);
+ mNotificationPanelViewController.resetTranslation();
mNotificationPanelViewController.resetViewGroupFade();
updateDozingState();
updateScrimController();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index b84e6e6..a4aeae9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -111,8 +111,6 @@
private final NavigationModeController mNavigationModeController;
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final KeyguardBouncer.Factory mKeyguardBouncerFactory;
- private final WakefulnessLifecycle mWakefulnessLifecycle;
- private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private final KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory;
private KeyguardMessageAreaController mKeyguardMessageAreaController;
private final Lazy<ShadeController> mShadeController;
@@ -244,8 +242,6 @@
KeyguardStateController keyguardStateController,
NotificationMediaManager notificationMediaManager,
KeyguardBouncer.Factory keyguardBouncerFactory,
- WakefulnessLifecycle wakefulnessLifecycle,
- UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
KeyguardMessageAreaController.Factory keyguardMessageAreaFactory,
Lazy<ShadeController> shadeController) {
mContext = context;
@@ -260,8 +256,6 @@
mStatusBarStateController = sysuiStatusBarStateController;
mDockManager = dockManager;
mKeyguardBouncerFactory = keyguardBouncerFactory;
- mWakefulnessLifecycle = wakefulnessLifecycle;
- mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
mKeyguardMessageAreaFactory = keyguardMessageAreaFactory;
mShadeController = shadeController;
}
@@ -1155,7 +1149,7 @@
@Override
public ViewRootImpl getViewRootImpl() {
- return mStatusBar.getStatusBarView().getViewRootImpl();
+ return mNotificationShadeWindowController.getNotificationShadeView().getViewRootImpl();
}
public void launchPendingWakeupAction() {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
new file mode 100644
index 0000000..fb9df01
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.unfold
+
+import android.os.PowerManager
+import android.provider.Settings
+import com.android.systemui.keyguard.KeyguardViewMediator
+import com.android.systemui.keyguard.ScreenLifecycle
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.statusbar.LightRevealScrim
+import com.android.systemui.statusbar.phone.ScreenOffAnimation
+import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus
+import com.android.systemui.util.settings.GlobalSettings
+import dagger.Lazy
+import javax.inject.Inject
+
+/**
+ * Controls folding to AOD animation: when AOD is enabled and foldable device is folded
+ * we play a special AOD animation on the outer screen
+ */
+@SysUIUnfoldScope
+class FoldAodAnimationController @Inject constructor(
+ private val screenLifecycle: ScreenLifecycle,
+ private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>,
+ private val wakefulnessLifecycle: WakefulnessLifecycle,
+ private val globalSettings: GlobalSettings
+) : ScreenLifecycle.Observer,
+ CallbackController<FoldAodAnimationStatus>,
+ ScreenOffAnimation,
+ WakefulnessLifecycle.Observer {
+
+ private var alwaysOnEnabled: Boolean = false
+ private var isScrimOpaque: Boolean = false
+ private lateinit var statusBar: StatusBar
+ private var pendingScrimReadyCallback: Runnable? = null
+
+ private var shouldPlayAnimation = false
+ private val statusListeners = arrayListOf<FoldAodAnimationStatus>()
+
+ private var isAnimationPlaying = false
+
+ override fun initialize(statusBar: StatusBar, lightRevealScrim: LightRevealScrim) {
+ this.statusBar = statusBar
+
+ screenLifecycle.addObserver(this)
+ wakefulnessLifecycle.addObserver(this)
+ }
+
+ /**
+ * Returns true if we should run fold to AOD animation
+ */
+ override fun shouldPlayAnimation(): Boolean =
+ shouldPlayAnimation
+
+ override fun startAnimation(): Boolean =
+ if (alwaysOnEnabled &&
+ wakefulnessLifecycle.lastSleepReason == PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD &&
+ globalSettings.getString(Settings.Global.ANIMATOR_DURATION_SCALE) != "0"
+ ) {
+ shouldPlayAnimation = true
+
+ isAnimationPlaying = true
+ statusBar.notificationPanelViewController.prepareFoldToAodAnimation()
+
+ statusListeners.forEach(FoldAodAnimationStatus::onFoldToAodAnimationChanged)
+
+ true
+ } else {
+ shouldPlayAnimation = false
+ false
+ }
+
+ override fun onStartedWakingUp() {
+ shouldPlayAnimation = false
+ isAnimationPlaying = false
+ }
+
+ /**
+ * Called when screen starts turning on, the contents of the screen might not be visible yet.
+ * This method reports back that the animation is ready in [onReady] callback.
+ *
+ * @param onReady callback when the animation is ready
+ * @see [com.android.systemui.keyguard.KeyguardViewMediator]
+ */
+ fun onScreenTurningOn(onReady: Runnable) {
+ if (shouldPlayAnimation) {
+ if (isScrimOpaque) {
+ onReady.run()
+ } else {
+ pendingScrimReadyCallback = onReady
+ }
+ } else {
+ // No animation, call ready callback immediately
+ onReady.run()
+ }
+ }
+
+ /**
+ * Called when keyguard scrim opaque changed
+ */
+ override fun onScrimOpaqueChanged(isOpaque: Boolean) {
+ isScrimOpaque = isOpaque
+
+ if (isOpaque) {
+ pendingScrimReadyCallback?.run()
+ pendingScrimReadyCallback = null
+ }
+ }
+
+ override fun onScreenTurnedOn() {
+ if (shouldPlayAnimation) {
+ statusBar.notificationPanelViewController.startFoldToAodAnimation {
+ // End action
+ isAnimationPlaying = false
+ keyguardViewMediatorLazy.get().maybeHandlePendingLock()
+ }
+ shouldPlayAnimation = false
+ }
+ }
+
+ override fun isAnimationPlaying(): Boolean =
+ isAnimationPlaying
+
+ override fun isKeyguardHideDelayed(): Boolean =
+ isAnimationPlaying()
+
+ override fun shouldShowAodIconsWhenShade(): Boolean =
+ shouldPlayAnimation()
+
+ override fun shouldAnimateAodIcons(): Boolean =
+ !shouldPlayAnimation()
+
+ override fun shouldAnimateDozingChange(): Boolean =
+ !shouldPlayAnimation()
+
+ override fun shouldAnimateClockChange(): Boolean =
+ !isAnimationPlaying()
+
+ /**
+ * Called when AOD status is changed
+ */
+ override fun onAlwaysOnChanged(alwaysOn: Boolean) {
+ alwaysOnEnabled = alwaysOn
+ }
+
+ override fun addCallback(listener: FoldAodAnimationStatus) {
+ statusListeners += listener
+ }
+
+ override fun removeCallback(listener: FoldAodAnimationStatus) {
+ statusListeners.remove(listener)
+ }
+
+ interface FoldAodAnimationStatus {
+ fun onFoldToAodAnimationChanged()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
index b53ab21..ccde316 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
@@ -78,6 +78,8 @@
fun getStatusBarMoveFromCenterAnimationController(): StatusBarMoveFromCenterAnimationController
+ fun getFoldAodAnimationController(): FoldAodAnimationController
+
fun getUnfoldTransitionWallpaperController(): UnfoldTransitionWallpaperController
fun getUnfoldLightRevealOverlayAnimation(): UnfoldLightRevealOverlayAnimation
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
index a7e9cdb..8b6e982 100644
--- a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java
@@ -23,7 +23,6 @@
import org.jetbrains.annotations.NotNull;
-import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -38,7 +37,7 @@
public class Monitor implements CallbackController<Monitor.Callback> {
private final String mTag = getClass().getSimpleName();
- private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
+ private final ArrayList<Callback> mCallbacks = new ArrayList<>();
// Set of all conditions that need to be monitored.
private final Set<Condition> mConditions;
@@ -66,9 +65,9 @@
mAllConditionsMet = newAllConditionsMet;
// Updates all callbacks.
- final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ final Iterator<Callback> iterator = mCallbacks.iterator();
while (iterator.hasNext()) {
- final Callback callback = iterator.next().get();
+ final Callback callback = iterator.next();
if (callback == null) {
iterator.remove();
} else {
@@ -78,7 +77,7 @@
};
@Inject
- public Monitor(Set<Condition> conditions) {
+ public Monitor(Set<Condition> conditions, Set<Callback> callbacks) {
mConditions = conditions;
// If there is no condition, give green pass.
@@ -89,12 +88,20 @@
// Initializes the conditions map and registers a callback for each condition.
mConditions.forEach((condition -> mConditionsMap.put(condition, false)));
+
+ if (callbacks == null) {
+ return;
+ }
+
+ for (Callback callback : callbacks) {
+ addCallback(callback);
+ }
}
@Override
public void addCallback(@NotNull Callback callback) {
if (shouldLog()) Log.d(mTag, "adding callback");
- mCallbacks.add(new WeakReference<>(callback));
+ mCallbacks.add(callback);
// Updates the callback immediately.
callback.onConditionsChanged(mAllConditionsMet);
@@ -109,9 +116,9 @@
@Override
public void removeCallback(@NotNull Callback callback) {
if (shouldLog()) Log.d(mTag, "removing callback");
- final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ final Iterator<Callback> iterator = mCallbacks.iterator();
while (iterator.hasNext()) {
- final Callback cb = iterator.next().get();
+ final Callback cb = iterator.next();
if (cb == null || cb == callback) {
iterator.remove();
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java b/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java
new file mode 100644
index 0000000..fc67973
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.condition.dagger;
+
+import com.android.systemui.util.condition.Condition;
+import com.android.systemui.util.condition.Monitor;
+
+import java.util.Set;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Component for {@link Monitor}.
+ */
+@Subcomponent
+public interface MonitorComponent {
+ /**
+ * Factory for {@link MonitorComponent}.
+ */
+ @Subcomponent.Factory
+ interface Factory {
+ MonitorComponent create(@BindsInstance Set<Condition> conditions,
+ @BindsInstance Set<Monitor.Callback> callbacks);
+ }
+
+ /**
+ * Provides {@link Monitor}.
+ * @return
+ */
+ Monitor getMonitor();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
index 981bf01..7892d6e 100644
--- a/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/dagger/UtilModule.java
@@ -18,6 +18,7 @@
import com.android.systemui.util.RingerModeTracker;
import com.android.systemui.util.RingerModeTrackerImpl;
+import com.android.systemui.util.condition.dagger.MonitorComponent;
import com.android.systemui.util.wrapper.UtilWrapperModule;
import dagger.Binds;
@@ -26,6 +27,9 @@
/** Dagger Module for code in the util package. */
@Module(includes = {
UtilWrapperModule.class
+ },
+ subcomponents = {
+ MonitorComponent.class,
})
public interface UtilModule {
/** */
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index e967033..74e0f40 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -264,7 +264,7 @@
reset(mView);
observer.onChange(true);
mExecutor.runAllReady();
- verify(mView).switchToClock(KeyguardClockSwitch.SMALL);
+ verify(mView).switchToClock(KeyguardClockSwitch.SMALL, /* animate */ true);
}
private void verifyAttachment(VerificationMode times) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
index e4336fe..8717a0e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java
@@ -253,8 +253,8 @@
}
@Test
- public void switchingToBigClock_makesSmallClockDisappear() {
- mKeyguardClockSwitch.switchToClock(LARGE);
+ public void switchingToBigClockWithAnimation_makesSmallClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true);
mKeyguardClockSwitch.mClockInAnim.end();
mKeyguardClockSwitch.mClockOutAnim.end();
@@ -265,8 +265,17 @@
}
@Test
- public void switchingToSmallClock_makesBigClockDisappear() {
- mKeyguardClockSwitch.switchToClock(SMALL);
+ public void switchingToBigClockNoAnimation_makesSmallClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ false);
+
+ assertThat(mLargeClockFrame.getAlpha()).isEqualTo(1);
+ assertThat(mLargeClockFrame.getVisibility()).isEqualTo(VISIBLE);
+ assertThat(mClockFrame.getAlpha()).isEqualTo(0);
+ }
+
+ @Test
+ public void switchingToSmallClockWithAnimation_makesBigClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(SMALL, /* animate */ true);
mKeyguardClockSwitch.mClockInAnim.end();
mKeyguardClockSwitch.mClockOutAnim.end();
@@ -279,8 +288,19 @@
}
@Test
+ public void switchingToSmallClockNoAnimation_makesBigClockDisappear() {
+ mKeyguardClockSwitch.switchToClock(SMALL, false);
+
+ assertThat(mClockFrame.getAlpha()).isEqualTo(1);
+ assertThat(mClockFrame.getVisibility()).isEqualTo(VISIBLE);
+ // only big clock is removed at switch
+ assertThat(mLargeClockFrame.getParent()).isNull();
+ assertThat(mLargeClockFrame.getAlpha()).isEqualTo(0);
+ }
+
+ @Test
public void switchingToBigClock_returnsTrueOnlyWhenItWasNotVisibleBefore() {
- assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isTrue();
- assertThat(mKeyguardClockSwitch.switchToClock(LARGE)).isFalse();
+ assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isTrue();
+ assertThat(mKeyguardClockSwitch.switchToClock(LARGE, /* animate */ true)).isFalse();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 7776285..24b01e0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -244,7 +244,7 @@
ViewGroup userSwitcher = mKeyguardSecurityContainer.findViewById(
R.id.keyguard_bouncer_user_switcher);
assertThat(((FrameLayout.LayoutParams) userSwitcher.getLayoutParams()).gravity)
- .isEqualTo(Gravity.LEFT | Gravity.TOP);
+ .isEqualTo(Gravity.LEFT | Gravity.CENTER_VERTICAL);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
index 9a9b7c4..4a29ada 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
@@ -28,8 +28,8 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.condition.Monitor;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -45,7 +45,7 @@
@Mock
private CommunalManager mCommunalManager;
@Mock
- private CommunalConditionsMonitor mCommunalConditionsMonitor;
+ private Monitor mCommunalConditionsMonitor;
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@@ -55,7 +55,7 @@
mContext.addMockSystemService(CommunalManager.class, mCommunalManager);
doAnswer(invocation -> {
- final CommunalConditionsMonitor.Callback callback = invocation.getArgument(0);
+ final Monitor.Callback callback = invocation.getArgument(0);
callback.onConditionsChanged(true);
return null;
}).when(mCommunalConditionsMonitor).addCallback(any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
index 409dd94..df1cc76 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
@@ -31,8 +31,8 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.condition.Monitor;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
@@ -49,9 +49,9 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class CommunalSourceMonitorTest extends SysuiTestCase {
- @Mock private CommunalConditionsMonitor mCommunalConditionsMonitor;
+ @Mock private Monitor mCommunalConditionsMonitor;
- @Captor private ArgumentCaptor<CommunalConditionsMonitor.Callback> mConditionsCallbackCaptor;
+ @Captor private ArgumentCaptor<Monitor.Callback> mConditionsCallbackCaptor;
private CommunalSourceMonitor mCommunalSourceMonitor;
private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@@ -156,7 +156,7 @@
private void setConditionsMet(boolean value) {
mExecutor.runAllReady();
verify(mCommunalConditionsMonitor).addCallback(mConditionsCallbackCaptor.capture());
- final CommunalConditionsMonitor.Callback conditionsCallback =
+ final Monitor.Callback conditionsCallback =
mConditionsCallbackCaptor.getValue();
conditionsCallback.onConditionsChanged(value);
mExecutor.runAllReady();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
index 873f7a4..55af51d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java
@@ -45,6 +45,8 @@
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.unfold.FoldAodAnimationController;
+import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.util.wakelock.WakeLockFake;
import org.junit.After;
@@ -54,6 +56,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DozeUiTest extends SysuiTestCase {
@@ -79,6 +83,10 @@
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
+ private FoldAodAnimationController mFoldAodAnimationController;
+ @Mock
+ private SysUIUnfoldComponent mSysUIUnfoldComponent;
+ @Mock
private ConfigurationController mConfigurationController;
@Before
@@ -90,9 +98,13 @@
mWakeLock = new WakeLockFake();
mHandler = mHandlerThread.getThreadHandler();
+ when(mSysUIUnfoldComponent.getFoldAodAnimationController())
+ .thenReturn(mFoldAodAnimationController);
+
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService,
- mStatusBarStateController, mConfigurationController);
+ mStatusBarStateController, Optional.of(mSysUIUnfoldComponent),
+ mConfigurationController);
mDozeUi.setDozeMachine(mMachine);
}
@@ -121,6 +133,7 @@
reset(mHost);
when(mDozeParameters.getAlwaysOn()).thenReturn(false);
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
+ when(mDozeParameters.shouldAnimateDozingChange()).thenReturn(true);
mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(false);
verify(mHost).setAnimateScreenOff(eq(false));
@@ -131,6 +144,7 @@
reset(mHost);
when(mDozeParameters.getAlwaysOn()).thenReturn(true);
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
+ when(mDozeParameters.shouldAnimateDozingChange()).thenReturn(true);
// Take over when the keyguard is visible.
mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
@@ -142,6 +156,18 @@
}
@Test
+ public void propagatesAnimateScreenOff_alwaysOn_shouldAnimateDozingChangeIsFalse() {
+ reset(mHost);
+ when(mDozeParameters.getAlwaysOn()).thenReturn(true);
+ when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(false);
+ when(mDozeParameters.shouldAnimateDozingChange()).thenReturn(false);
+
+ // Take over when the keyguard is visible.
+ mDozeUi.getKeyguardCallback().onKeyguardVisibilityChanged(true);
+ verify(mHost).setAnimateScreenOff(eq(false));
+ }
+
+ @Test
public void neverAnimateScreenOff_whenNotSupported() {
// Re-initialize DozeParameters saying that the display requires blanking.
reset(mDozeParameters);
@@ -149,7 +175,8 @@
when(mDozeParameters.getDisplayNeedsBlanking()).thenReturn(true);
mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler,
mDozeParameters, mKeyguardUpdateMonitor, mDozeLog, mTunerService,
- mStatusBarStateController, mConfigurationController);
+ mStatusBarStateController, Optional.of(mSysUIUnfoldComponent),
+ mConfigurationController);
mDozeUi.setDozeMachine(mMachine);
// Never animate if display doesn't support it.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 53bfeee..904ee91 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -37,6 +37,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
+import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -78,16 +79,41 @@
@Mock
DreamOverlayStateController mDreamOverlayStateController;
+ @Mock
+ DreamOverlayComponent.Factory mDreamOverlayStatusBarViewComponentFactory;
+
+ @Mock
+ DreamOverlayComponent mDreamOverlayComponent;
+
+ @Mock
+ DreamOverlayStatusBarViewController mDreamOverlayStatusBarViewController;
+
+ @Mock
+ DreamOverlayContainerView mDreamOverlayContainerView;
+
+ @Mock
+ ViewGroup mDreamOverlayContentView;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext.addMockSystemService(WindowManager.class, mWindowManager);
+
+ when(mDreamOverlayComponent.getDreamOverlayContentView())
+ .thenReturn(mDreamOverlayContentView);
+ when(mDreamOverlayComponent.getDreamOverlayContainerView())
+ .thenReturn(mDreamOverlayContainerView);
+ when(mDreamOverlayComponent.getDreamOverlayStatusBarViewController())
+ .thenReturn(mDreamOverlayStatusBarViewController);
+ when(mDreamOverlayStatusBarViewComponentFactory.create())
+ .thenReturn(mDreamOverlayComponent);
+
}
@Test
public void testInteraction() throws Exception {
final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor,
- mDreamOverlayStateController);
+ mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory);
final IBinder proxy = service.onBind(new Intent());
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
clearInvocations(mWindowManager);
@@ -128,7 +154,7 @@
@Test
public void testListening() throws Exception {
final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor,
- mDreamOverlayStateController);
+ mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory);
final IBinder proxy = service.onBind(new Intent());
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
@@ -150,4 +176,35 @@
// Verify provider is asked to create overlay.
verify(mProvider).onCreateOverlay(any(), any(), any());
}
+
+ @Test
+ public void testDreamOverlayStatusBarViewControllerInitialized() throws Exception {
+ final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor,
+ mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory);
+
+ final IBinder proxy = service.onBind(new Intent());
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+
+ // Inform the overlay service of dream starting.
+ overlay.startDream(mWindowParams, mDreamOverlayCallback);
+ mMainExecutor.runAllReady();
+
+ verify(mDreamOverlayStatusBarViewController).init();
+ }
+
+ @Test
+ public void testRootViewAttachListenerIsAddedToDreamOverlayContentView() throws Exception {
+ final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor,
+ mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory);
+
+ final IBinder proxy = service.onBind(new Intent());
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+
+ // Inform the overlay service of dream starting.
+ overlay.startDream(mWindowParams, mDreamOverlayCallback);
+ mMainExecutor.runAllReady();
+
+ verify(mDreamOverlayContentView).addOnAttachStateChangeListener(
+ any(View.OnAttachStateChangeListener.class));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
new file mode 100644
index 0000000..7f72dda
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.battery.BatteryMeterViewController;
+import com.android.systemui.statusbar.policy.BatteryController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
+
+ @Mock
+ DreamOverlayStatusBarView mView;
+ @Mock
+ BatteryController mBatteryController;
+ @Mock
+ BatteryMeterViewController mBatteryMeterViewController;
+ @Mock
+ ConnectivityManager mConnectivityManager;
+ @Mock
+ NetworkCapabilities mNetworkCapabilities;
+ @Mock
+ Network mNetwork;
+
+ DreamOverlayStatusBarViewController mController;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mController = new DreamOverlayStatusBarViewController(
+ mContext, mView, mBatteryController, mBatteryMeterViewController,
+ mConnectivityManager);
+ }
+
+ @Test
+ public void testOnInitInitializesControllers() {
+ mController.onInit();
+ verify(mBatteryMeterViewController).init();
+ }
+
+ @Test
+ public void testOnViewAttachedAddsBatteryControllerCallback() {
+ mController.onViewAttached();
+ verify(mBatteryController)
+ .addCallback(any(BatteryController.BatteryStateChangeCallback.class));
+ }
+
+ @Test
+ public void testOnViewAttachedRegistersNetworkCallback() {
+ mController.onViewAttached();
+ verify(mConnectivityManager)
+ .registerNetworkCallback(any(NetworkRequest.class), any(
+ ConnectivityManager.NetworkCallback.class));
+ }
+
+ @Test
+ public void testOnViewAttachedShowsWifiStatusWhenWifiUnavailable() {
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(false);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+ verify(mView).showWifiStatus(true);
+ }
+
+ @Test
+ public void testOnViewAttachedHidesWifiStatusWhenWifiAvailable() {
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(true);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+ verify(mView).showWifiStatus(false);
+ }
+
+ @Test
+ public void testOnViewAttachedShowsWifiStatusWhenNetworkCapabilitiesUnavailable() {
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(null);
+ mController.onViewAttached();
+ verify(mView).showWifiStatus(true);
+ }
+
+ @Test
+ public void testOnViewDetachedRemovesBatteryControllerCallback() {
+ mController.onViewDetached();
+ verify(mBatteryController)
+ .removeCallback(any(BatteryController.BatteryStateChangeCallback.class));
+ }
+
+ @Test
+ public void testOnViewDetachedUnregistersNetworkCallback() {
+ mController.onViewDetached();
+ verify(mConnectivityManager)
+ .unregisterNetworkCallback(any(ConnectivityManager.NetworkCallback.class));
+ }
+
+ @Test
+ public void testBatteryPercentTextShownWhenBatteryLevelChangesWhileCharging() {
+ final ArgumentCaptor<BatteryController.BatteryStateChangeCallback> callbackCapture =
+ ArgumentCaptor.forClass(BatteryController.BatteryStateChangeCallback.class);
+ mController.onViewAttached();
+ verify(mBatteryController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onBatteryLevelChanged(1, true, true);
+ verify(mView).showBatteryPercentText(true);
+ }
+
+ @Test
+ public void testBatteryPercentTextHiddenWhenBatteryLevelChangesWhileNotCharging() {
+ final ArgumentCaptor<BatteryController.BatteryStateChangeCallback> callbackCapture =
+ ArgumentCaptor.forClass(BatteryController.BatteryStateChangeCallback.class);
+ mController.onViewAttached();
+ verify(mBatteryController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onBatteryLevelChanged(1, true, false);
+ verify(mView).showBatteryPercentText(false);
+ }
+
+ @Test
+ public void testWifiStatusHiddenWhenWifiBecomesAvailable() {
+ // Make sure wifi starts out unavailable when onViewAttached is called.
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(false);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+
+ final ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackCapture =
+ ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+ verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
+ callbackCapture.getValue().onAvailable(mNetwork);
+ verify(mView).showWifiStatus(false);
+ }
+
+ @Test
+ public void testWifiStatusShownWhenWifiBecomesUnavailable() {
+ // Make sure wifi starts out available when onViewAttached is called.
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(true);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+
+ final ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackCapture =
+ ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+ verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
+ callbackCapture.getValue().onLost(mNetwork);
+ verify(mView).showWifiStatus(true);
+ }
+
+ @Test
+ public void testWifiStatusHiddenWhenCapabilitiesChange() {
+ // Make sure wifi starts out unavailable when onViewAttached is called.
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(false);
+ when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
+ mController.onViewAttached();
+
+ final ArgumentCaptor<ConnectivityManager.NetworkCallback> callbackCapture =
+ ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+ verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
+ when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
+ .thenReturn(true);
+ callbackCapture.getValue().onCapabilitiesChanged(mNetwork, mNetworkCapabilities);
+ verify(mView).showWifiStatus(false);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 8d329c5..27fcb11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -58,6 +58,7 @@
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.unfold.FoldAodAnimationController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.android.systemui.unfold.UnfoldLightRevealOverlayAnimation;
import com.android.systemui.util.DeviceConfigProxy;
@@ -69,7 +70,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -95,62 +95,40 @@
private @Mock NavigationModeController mNavigationModeController;
private @Mock KeyguardDisplayManager mKeyguardDisplayManager;
private @Mock DozeParameters mDozeParameters;
- private @Mock Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent;
- private @Mock Optional<UnfoldLightRevealOverlayAnimation> mUnfoldAnimationOptional;
+ private @Mock SysUIUnfoldComponent mSysUIUnfoldComponent;
private @Mock UnfoldLightRevealOverlayAnimation mUnfoldAnimation;
private @Mock SysuiStatusBarStateController mStatusBarStateController;
private @Mock KeyguardStateController mKeyguardStateController;
private @Mock NotificationShadeDepthController mNotificationShadeDepthController;
private @Mock KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
private @Mock ScreenOffAnimationController mScreenOffAnimationController;
+ private @Mock FoldAodAnimationController mFoldAodAnimationController;
private @Mock IKeyguardDrawnCallback mKeyguardDrawnCallback;
private @Mock InteractionJankMonitor mInteractionJankMonitor;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
+ private Optional<SysUIUnfoldComponent> mSysUiUnfoldComponentOptional;
+
private FalsingCollectorFake mFalsingCollector;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mFalsingCollector = new FalsingCollectorFake();
+ mSysUiUnfoldComponentOptional = Optional.of(mSysUIUnfoldComponent);
when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class));
- when(mSysUIUnfoldComponent.map(
- ArgumentMatchers.<Function<SysUIUnfoldComponent, UnfoldLightRevealOverlayAnimation>>
- any()))
- .thenReturn(mUnfoldAnimationOptional);
- when(mUnfoldAnimationOptional.isPresent()).thenReturn(true);
- when(mUnfoldAnimationOptional.get()).thenReturn(mUnfoldAnimation);
+ when(mSysUIUnfoldComponent.getUnfoldLightRevealOverlayAnimation())
+ .thenReturn(mUnfoldAnimation);
+ when(mSysUIUnfoldComponent.getFoldAodAnimationController())
+ .thenReturn(mFoldAodAnimationController);
+
when(mInteractionJankMonitor.begin(any(), anyInt())).thenReturn(true);
when(mInteractionJankMonitor.end(anyInt())).thenReturn(true);
- mViewMediator = new KeyguardViewMediator(
- mContext,
- mFalsingCollector,
- mLockPatternUtils,
- mBroadcastDispatcher,
- () -> mStatusBarKeyguardViewManager,
- mDismissCallbackRegistry,
- mUpdateMonitor,
- mDumpManager,
- mUiBgExecutor,
- mPowerManager,
- mTrustManager,
- mUserSwitcherController,
- mDeviceConfig,
- mNavigationModeController,
- mKeyguardDisplayManager,
- mDozeParameters,
- mSysUIUnfoldComponent,
- mStatusBarStateController,
- mKeyguardStateController,
- () -> mKeyguardUnlockAnimationController,
- mScreenOffAnimationController,
- () -> mNotificationShadeDepthController,
- mInteractionJankMonitor);
- mViewMediator.start();
+ createAndStartViewMediator();
}
@Test
@@ -179,6 +157,7 @@
mViewMediator.onScreenTurningOn(mKeyguardDrawnCallback);
TestableLooper.get(this).processAllMessages();
onUnfoldOverlayReady();
+ onFoldAodReady();
// Should be called when both unfold overlay and keyguard drawn ready
verify(mKeyguardDrawnCallback).onDrawn();
@@ -188,7 +167,8 @@
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public void testUnfoldTransitionDisabledDrawnTasksReady_onScreenTurningOn_callsDrawnCallback()
throws RemoteException {
- when(mUnfoldAnimationOptional.isPresent()).thenReturn(false);
+ mSysUiUnfoldComponentOptional = Optional.empty();
+ createAndStartViewMediator();
mViewMediator.onScreenTurningOn(mKeyguardDrawnCallback);
TestableLooper.get(this).processAllMessages();
@@ -200,6 +180,7 @@
@Test
public void testIsAnimatingScreenOff() {
when(mDozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true);
+ when(mDozeParameters.shouldAnimateDozingChange()).thenReturn(true);
mViewMediator.onFinishedGoingToSleep(OFF_BECAUSE_OF_USER, false);
mViewMediator.setDozing(true);
@@ -244,4 +225,39 @@
overlayReadyCaptor.getValue().run();
TestableLooper.get(this).processAllMessages();
}
+
+ private void onFoldAodReady() {
+ ArgumentCaptor<Runnable> ready = ArgumentCaptor.forClass(Runnable.class);
+ verify(mFoldAodAnimationController).onScreenTurningOn(ready.capture());
+ ready.getValue().run();
+ TestableLooper.get(this).processAllMessages();
+ }
+
+ private void createAndStartViewMediator() {
+ mViewMediator = new KeyguardViewMediator(
+ mContext,
+ mFalsingCollector,
+ mLockPatternUtils,
+ mBroadcastDispatcher,
+ () -> mStatusBarKeyguardViewManager,
+ mDismissCallbackRegistry,
+ mUpdateMonitor,
+ mDumpManager,
+ mUiBgExecutor,
+ mPowerManager,
+ mTrustManager,
+ mUserSwitcherController,
+ mDeviceConfig,
+ mNavigationModeController,
+ mKeyguardDisplayManager,
+ mDozeParameters,
+ mSysUiUnfoldComponentOptional,
+ mStatusBarStateController,
+ mKeyguardStateController,
+ () -> mKeyguardUnlockAnimationController,
+ mScreenOffAnimationController,
+ () -> mNotificationShadeDepthController,
+ mInteractionJankMonitor);
+ mViewMediator.start();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
new file mode 100644
index 0000000..af33daf
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tileimpl
+
+import android.content.ComponentName
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.external.CustomTile
+import com.android.systemui.qs.tiles.AirplaneModeTile
+import com.android.systemui.qs.tiles.AlarmTile
+import com.android.systemui.qs.tiles.BatterySaverTile
+import com.android.systemui.qs.tiles.BluetoothTile
+import com.android.systemui.qs.tiles.CameraToggleTile
+import com.android.systemui.qs.tiles.CastTile
+import com.android.systemui.qs.tiles.CellularTile
+import com.android.systemui.qs.tiles.ColorInversionTile
+import com.android.systemui.qs.tiles.DataSaverTile
+import com.android.systemui.qs.tiles.DeviceControlsTile
+import com.android.systemui.qs.tiles.DndTile
+import com.android.systemui.qs.tiles.FgsManagerTile
+import com.android.systemui.qs.tiles.FlashlightTile
+import com.android.systemui.qs.tiles.HotspotTile
+import com.android.systemui.qs.tiles.InternetTile
+import com.android.systemui.qs.tiles.LocationTile
+import com.android.systemui.qs.tiles.MicrophoneToggleTile
+import com.android.systemui.qs.tiles.NfcTile
+import com.android.systemui.qs.tiles.NightDisplayTile
+import com.android.systemui.qs.tiles.OneHandedModeTile
+import com.android.systemui.qs.tiles.QRCodeScannerTile
+import com.android.systemui.qs.tiles.QuickAccessWalletTile
+import com.android.systemui.qs.tiles.ReduceBrightColorsTile
+import com.android.systemui.qs.tiles.RotationLockTile
+import com.android.systemui.qs.tiles.ScreenRecordTile
+import com.android.systemui.qs.tiles.UiModeNightTile
+import com.android.systemui.qs.tiles.UserTile
+import com.android.systemui.qs.tiles.WifiTile
+import com.android.systemui.qs.tiles.WorkModeTile
+import com.android.systemui.util.leak.GarbageMonitor
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Answers
+import org.mockito.Mock
+import org.mockito.Mockito.inOrder
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.`when` as whenever
+
+private val specMap = mapOf(
+ "wifi" to WifiTile::class.java,
+ "internet" to InternetTile::class.java,
+ "bt" to BluetoothTile::class.java,
+ "cell" to CellularTile::class.java,
+ "dnd" to DndTile::class.java,
+ "inversion" to ColorInversionTile::class.java,
+ "airplane" to AirplaneModeTile::class.java,
+ "work" to WorkModeTile::class.java,
+ "rotation" to RotationLockTile::class.java,
+ "flashlight" to FlashlightTile::class.java,
+ "location" to LocationTile::class.java,
+ "cast" to CastTile::class.java,
+ "hotspot" to HotspotTile::class.java,
+ "user" to UserTile::class.java,
+ "battery" to BatterySaverTile::class.java,
+ "saver" to DataSaverTile::class.java,
+ "night" to NightDisplayTile::class.java,
+ "nfc" to NfcTile::class.java,
+ "dark" to UiModeNightTile::class.java,
+ "screenrecord" to ScreenRecordTile::class.java,
+ "reduce_brightness" to ReduceBrightColorsTile::class.java,
+ "cameratoggle" to CameraToggleTile::class.java,
+ "mictoggle" to MicrophoneToggleTile::class.java,
+ "controls" to DeviceControlsTile::class.java,
+ "alarm" to AlarmTile::class.java,
+ "wallet" to QuickAccessWalletTile::class.java,
+ "qr_code_scanner" to QRCodeScannerTile::class.java,
+ "onehanded" to OneHandedModeTile::class.java,
+ "fgsmanager" to FgsManagerTile::class.java
+)
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class QSFactoryImplTest : SysuiTestCase() {
+
+ @Mock private lateinit var qsHost: QSHost
+ @Mock(answer = Answers.RETURNS_SELF) private lateinit var customTileBuilder: CustomTile.Builder
+ @Mock private lateinit var customTile: CustomTile
+
+ @Mock private lateinit var wifiTile: WifiTile
+ @Mock private lateinit var internetTile: InternetTile
+ @Mock private lateinit var bluetoothTile: BluetoothTile
+ @Mock private lateinit var cellularTile: CellularTile
+ @Mock private lateinit var dndTile: DndTile
+ @Mock private lateinit var colorInversionTile: ColorInversionTile
+ @Mock private lateinit var airplaneTile: AirplaneModeTile
+ @Mock private lateinit var workTile: WorkModeTile
+ @Mock private lateinit var rotationTile: RotationLockTile
+ @Mock private lateinit var flashlightTile: FlashlightTile
+ @Mock private lateinit var locationTile: LocationTile
+ @Mock private lateinit var castTile: CastTile
+ @Mock private lateinit var hotspotTile: HotspotTile
+ @Mock private lateinit var userTile: UserTile
+ @Mock private lateinit var batterySaverTile: BatterySaverTile
+ @Mock private lateinit var dataSaverTile: DataSaverTile
+ @Mock private lateinit var nightDisplayTile: NightDisplayTile
+ @Mock private lateinit var nfcTile: NfcTile
+ @Mock private lateinit var memoryTile: GarbageMonitor.MemoryTile
+ @Mock private lateinit var darkModeTile: UiModeNightTile
+ @Mock private lateinit var screenRecordTile: ScreenRecordTile
+ @Mock private lateinit var reduceBrightColorsTile: ReduceBrightColorsTile
+ @Mock private lateinit var cameraToggleTile: CameraToggleTile
+ @Mock private lateinit var microphoneToggleTile: MicrophoneToggleTile
+ @Mock private lateinit var deviceControlsTile: DeviceControlsTile
+ @Mock private lateinit var alarmTile: AlarmTile
+ @Mock private lateinit var quickAccessWalletTile: QuickAccessWalletTile
+ @Mock private lateinit var qrCodeScannerTile: QRCodeScannerTile
+ @Mock private lateinit var oneHandedModeTile: OneHandedModeTile
+ @Mock private lateinit var fgsManagerTile: FgsManagerTile
+
+ private lateinit var factory: QSFactoryImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ whenever(qsHost.context).thenReturn(mContext)
+ whenever(qsHost.userContext).thenReturn(mContext)
+ whenever(customTileBuilder.build()).thenReturn(customTile)
+
+ factory = QSFactoryImpl(
+ { qsHost },
+ { customTileBuilder },
+ { wifiTile },
+ { internetTile },
+ { bluetoothTile },
+ { cellularTile },
+ { dndTile },
+ { colorInversionTile },
+ { airplaneTile },
+ { workTile },
+ { rotationTile },
+ { flashlightTile },
+ { locationTile },
+ { castTile },
+ { hotspotTile },
+ { userTile },
+ { batterySaverTile },
+ { dataSaverTile },
+ { nightDisplayTile },
+ { nfcTile },
+ { memoryTile },
+ { darkModeTile },
+ { screenRecordTile },
+ { reduceBrightColorsTile },
+ { cameraToggleTile },
+ { microphoneToggleTile },
+ { deviceControlsTile },
+ { alarmTile },
+ { quickAccessWalletTile },
+ { qrCodeScannerTile },
+ { oneHandedModeTile },
+ { fgsManagerTile }
+ )
+ // When adding/removing tiles, fix also [specMap]
+ }
+
+ @Test
+ fun testCorrectTileClassStock() {
+ specMap.forEach { spec, klazz ->
+ assertThat(factory.createTile(spec)).isInstanceOf(klazz)
+ }
+ }
+
+ @Test
+ fun testCustomTileClass() {
+ val customSpec = CustomTile.toSpec(ComponentName("test", "test"))
+ assertThat(factory.createTile(customSpec)).isInstanceOf(CustomTile::class.java)
+ }
+
+ @Test
+ fun testBadTileNull() {
+ assertThat(factory.createTile("-432~")).isNull()
+ }
+
+ @Test
+ fun testTileInitializedAndStale() {
+ specMap.forEach { spec, _ ->
+ val tile = factory.createTile(spec) as QSTileImpl<*>
+ val inOrder = inOrder(tile)
+ inOrder.verify(tile).initialize()
+ inOrder.verify(tile).postStale()
+ }
+
+ val customSpec = CustomTile.toSpec(ComponentName("test", "test"))
+ val tile = factory.createTile(customSpec) as QSTileImpl<*>
+ val inOrder = inOrder(tile)
+ inOrder.verify(tile).initialize()
+ inOrder.verify(tile).postStale()
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 9bcdcc9..1cd9b9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -425,6 +425,7 @@
.thenReturn(mKeyguardUserSwitcherComponent);
when(mKeyguardUserSwitcherComponent.getKeyguardUserSwitcherController())
.thenReturn(mKeyguardUserSwitcherController);
+ when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(true);
doAnswer((Answer<Void>) invocation -> {
mTouchHandler = invocation.getArgument(0);
@@ -880,11 +881,11 @@
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController).displayClock(LARGE);
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
mNotificationPanelViewController.closeQs();
- verify(mKeyguardStatusViewController).displayClock(SMALL);
+ verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
}
@Test
@@ -894,12 +895,14 @@
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController).displayClock(LARGE);
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController, times(2)).displayClock(LARGE);
- verify(mKeyguardStatusViewController, never()).displayClock(SMALL);
+ verify(mKeyguardStatusViewController, times(2))
+ .displayClock(LARGE, /* animate */ true);
+ verify(mKeyguardStatusViewController, never())
+ .displayClock(SMALL, /* animate */ true);
}
@Test
@@ -911,7 +914,20 @@
mNotificationPanelViewController.setDozing(true, false, null);
- verify(mKeyguardStatusViewController).displayClock(LARGE);
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
+ }
+
+ @Test
+ public void testSwitchesToBigClockInSplitShadeOnAodAnimateDisabled() {
+ when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(false);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+ when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+
+ mNotificationPanelViewController.setDozing(true, false, null);
+
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ false);
}
@Test
@@ -923,13 +939,13 @@
// one notification + media player visible
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController).displayClock(SMALL);
+ verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
// only media player visible
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
triggerPositionClockAndNotifications();
- verify(mKeyguardStatusViewController, times(2)).displayClock(SMALL);
- verify(mKeyguardStatusViewController, never()).displayClock(LARGE);
+ verify(mKeyguardStatusViewController, times(2)).displayClock(SMALL, true);
+ verify(mKeyguardStatusViewController, never()).displayClock(LARGE, /* animate */ true);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
index 2d548e9..a8544a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SplitShadeHeaderControllerTest.kt
@@ -1,8 +1,8 @@
package com.android.systemui.statusbar.phone
-import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
import android.view.View
+import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
@@ -42,6 +42,7 @@
var viewVisibility = View.GONE
private lateinit var splitShadeHeaderController: SplitShadeHeaderController
+ private lateinit var carrierIconSlots: List<String>
@Before
fun setup() {
@@ -67,12 +68,13 @@
featureFlags,
batteryMeterViewController
)
+ carrierIconSlots = listOf(
+ context.getString(com.android.internal.R.string.status_bar_mobile))
}
@Test
fun setVisible_onlyInSplitShade() {
- splitShadeHeaderController.splitShadeMode = true
- splitShadeHeaderController.shadeExpanded = true
+ makeShadeVisible()
assertThat(viewVisibility).isEqualTo(View.VISIBLE)
splitShadeHeaderController.splitShadeMode = false
@@ -81,17 +83,38 @@
@Test
fun updateListeners_registersWhenVisible() {
- splitShadeHeaderController.splitShadeMode = true
- splitShadeHeaderController.shadeExpanded = true
+ makeShadeVisible()
verify(qsCarrierGroupController).setListening(true)
verify(statusBarIconController).addIconGroup(any())
}
@Test
fun shadeExpandedFraction_updatesAlpha() {
- splitShadeHeaderController.splitShadeMode = true
- splitShadeHeaderController.shadeExpanded = true
+ makeShadeVisible()
splitShadeHeaderController.shadeExpandedFraction = 0.5f
verify(view).setAlpha(ShadeInterpolation.getContentAlpha(0.5f))
}
-}
\ No newline at end of file
+
+ @Test
+ fun singleCarrier_enablesCarrierIconsInStatusIcons() {
+ whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(true)
+
+ makeShadeVisible()
+
+ verify(statusIcons).removeIgnoredSlots(carrierIconSlots)
+ }
+
+ @Test
+ fun dualCarrier_disablesCarrierIconsInStatusIcons() {
+ whenever(qsCarrierGroupController.isSingleCarrier).thenReturn(false)
+
+ makeShadeVisible()
+
+ verify(statusIcons).addIgnoredSlots(carrierIconSlots)
+ }
+
+ private fun makeShadeVisible() {
+ splitShadeHeaderController.splitShadeMode = true
+ splitShadeHeaderController.shadeExpanded = true
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index c5bdfed..cbaa460 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -43,9 +43,6 @@
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.DismissCallbackRegistry;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -79,9 +76,7 @@
@Mock
private NotificationPanelViewController mNotificationPanelView;
@Mock
- private BiometricUnlockController mBiometrucUnlockController;
- @Mock
- private DismissCallbackRegistry mDismissCallbackRegistry;
+ private BiometricUnlockController mBiometricUnlockController;
@Mock
private SysuiStatusBarStateController mStatusBarStateController;
@Mock
@@ -97,15 +92,12 @@
@Mock
private KeyguardBouncer mBouncer;
@Mock
- private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
- @Mock
private StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor;
@Mock
private KeyguardMessageArea mKeyguardMessageArea;
@Mock
private ShadeController mShadeController;
- private WakefulnessLifecycle mWakefulnessLifecycle;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Before
@@ -117,10 +109,6 @@
.thenReturn(mBouncer);
when(mStatusBar.getBouncerContainer()).thenReturn(mContainer);
when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
- mWakefulnessLifecycle = new WakefulnessLifecycle(
- getContext(),
- null,
- mock(DumpManager.class));
mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager(
getContext(),
mViewMediatorCallback,
@@ -134,15 +122,13 @@
mKeyguardStateController,
mock(NotificationMediaManager.class),
mKeyguardBouncerFactory,
- mWakefulnessLifecycle,
- mUnlockedScreenOffAnimationController,
mKeyguardMessageAreaFactory,
() -> mShadeController);
mStatusBarKeyguardViewManager.registerStatusBar(
mStatusBar,
mNotificationPanelView,
new PanelExpansionStateManager(),
- mBiometrucUnlockController,
+ mBiometricUnlockController,
mNotificationContainer,
mBypassController);
mStatusBarKeyguardViewManager.show(null);
@@ -261,7 +247,7 @@
@Test
public void onPanelExpansionChanged_neverTranslatesBouncerWhenWakeAndUnlock() {
- when(mBiometrucUnlockController.getMode())
+ when(mBiometricUnlockController.getMode())
.thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
/* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
index 878bdea..d645449 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java
@@ -58,7 +58,7 @@
mCondition3 = spy(new FakeCondition());
mConditions = new HashSet<>(Arrays.asList(mCondition1, mCondition2, mCondition3));
- mConditionMonitor = new Monitor(mConditions);
+ mConditionMonitor = new Monitor(mConditions, null /*callbacks*/);
}
@Test
@@ -98,7 +98,7 @@
@Test
public void addCallback_noConditions_reportAllConditionsMet() {
- final Monitor monitor = new Monitor(new HashSet<>());
+ final Monitor monitor = new Monitor(new HashSet<>(), null /*callbacks*/);
final Monitor.Callback callback = mock(Monitor.Callback.class);
monitor.addCallback(callback);
diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/res/values-land/dimens.xml b/packages/overlays/NavigationBarModeGesturalOverlay/res/values-land/dimens.xml
index 1f5834d..a03dcbd 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlay/res/values-land/dimens.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlay/res/values-land/dimens.xml
@@ -18,5 +18,5 @@
-->
<resources>
<!-- The height of the bottom navigation gesture area. -->
- <dimen name="navigation_bar_gesture_height">32dp</dimen>
+ <dimen name="navigation_bar_gesture_height">24dp</dimen>
</resources>
diff --git a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/dimens.xml b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/dimens.xml
index ac1f022..c5d0c9e 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlay/res/values/dimens.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlay/res/values/dimens.xml
@@ -18,13 +18,13 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">16dp</dimen>
+ <dimen name="navigation_bar_height">24dp</dimen>
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
- <dimen name="navigation_bar_height_landscape">16dp</dimen>
+ <dimen name="navigation_bar_height_landscape">24dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">16dp</dimen>
+ <dimen name="navigation_bar_width">24dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_frame_height">48dp</dimen>
<!-- The height of the bottom navigation gesture area. -->
- <dimen name="navigation_bar_gesture_height">32dp</dimen>
+ <dimen name="navigation_bar_gesture_height">24dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values/dimens.xml b/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values/dimens.xml
index ac1f022..c5d0c9e 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values/dimens.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlayExtraWideBack/res/values/dimens.xml
@@ -18,13 +18,13 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">16dp</dimen>
+ <dimen name="navigation_bar_height">24dp</dimen>
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
- <dimen name="navigation_bar_height_landscape">16dp</dimen>
+ <dimen name="navigation_bar_height_landscape">24dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">16dp</dimen>
+ <dimen name="navigation_bar_width">24dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_frame_height">48dp</dimen>
<!-- The height of the bottom navigation gesture area. -->
- <dimen name="navigation_bar_gesture_height">32dp</dimen>
+ <dimen name="navigation_bar_gesture_height">24dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values/dimens.xml b/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values/dimens.xml
index ac1f022..c5d0c9e 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values/dimens.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlayNarrowBack/res/values/dimens.xml
@@ -18,13 +18,13 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">16dp</dimen>
+ <dimen name="navigation_bar_height">24dp</dimen>
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
- <dimen name="navigation_bar_height_landscape">16dp</dimen>
+ <dimen name="navigation_bar_height_landscape">24dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">16dp</dimen>
+ <dimen name="navigation_bar_width">24dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_frame_height">48dp</dimen>
<!-- The height of the bottom navigation gesture area. -->
- <dimen name="navigation_bar_gesture_height">32dp</dimen>
+ <dimen name="navigation_bar_gesture_height">24dp</dimen>
</resources>
\ No newline at end of file
diff --git a/packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values/dimens.xml b/packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values/dimens.xml
index ac1f022..c5d0c9e 100644
--- a/packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values/dimens.xml
+++ b/packages/overlays/NavigationBarModeGesturalOverlayWideBack/res/values/dimens.xml
@@ -18,13 +18,13 @@
-->
<resources>
<!-- Height of the bottom navigation / system bar. -->
- <dimen name="navigation_bar_height">16dp</dimen>
+ <dimen name="navigation_bar_height">24dp</dimen>
<!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height -->
- <dimen name="navigation_bar_height_landscape">16dp</dimen>
+ <dimen name="navigation_bar_height_landscape">24dp</dimen>
<!-- Width of the navigation bar when it is placed vertically on the screen -->
- <dimen name="navigation_bar_width">16dp</dimen>
+ <dimen name="navigation_bar_width">24dp</dimen>
<!-- Height of the bottom navigation / system bar. -->
<dimen name="navigation_bar_frame_height">48dp</dimen>
<!-- The height of the bottom navigation gesture area. -->
- <dimen name="navigation_bar_gesture_height">32dp</dimen>
+ <dimen name="navigation_bar_gesture_height">24dp</dimen>
</resources>
\ No newline at end of file
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 881c910..f050b66 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1027,8 +1027,8 @@
mSystemSupport.getMagnificationProcessor();
final long identity = Binder.clearCallingIdentity();
try {
- magnificationProcessor.getMagnificationRegion(displayId, region,
- mSecurityPolicy.canControlMagnification(this));
+ magnificationProcessor.getFullscreenMagnificationRegion(displayId,
+ region, mSecurityPolicy.canControlMagnification(this));
return region;
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1095,7 +1095,7 @@
try {
MagnificationProcessor magnificationProcessor =
mSystemSupport.getMagnificationProcessor();
- return (magnificationProcessor.reset(displayId, animate)
+ return (magnificationProcessor.resetFullscreenMagnification(displayId, animate)
|| !magnificationProcessor.isMagnifying(displayId));
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
index d0b9895..7a525ee 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -119,6 +119,18 @@
return false;
}
+ private boolean setScaleAndCenterForFullScreenMagnification(int displayId, float scale,
+ float centerX, float centerY,
+ boolean animate, int id) {
+ if (!isRegistered(displayId)) {
+ register(displayId);
+ }
+ return mController.getFullScreenMagnificationController().setScaleAndCenter(
+ displayId,
+ scale,
+ centerX, centerY, animate, id);
+ }
+
/**
* Returns {@code true} if transition magnification mode needed. And it is no need to transition
* mode when the controlling mode is unchanged or the controlling magnifier is not activated.
@@ -135,24 +147,18 @@
}
/**
- * Returns the magnification scale. If an animation is in progress,
- * this reflects the end state of the animation.
+ * Returns the magnification scale of full-screen magnification on the display.
+ * If an animation is in progress, this reflects the end state of the animation.
*
* @param displayId The logical display id.
* @return the scale
*/
public float getScale(int displayId) {
- int mode = getControllingMode(displayId);
- if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
- return mController.getFullScreenMagnificationController().getScale(displayId);
- } else if (mode == MAGNIFICATION_MODE_WINDOW) {
- return mController.getWindowMagnificationMgr().getScale(displayId);
- }
- return 0;
+ return mController.getFullScreenMagnificationController().getScale(displayId);
}
/**
- * Returns the magnification center in X coordinate of the controlling magnification mode.
+ * Returns the magnification center in X coordinate of full-screen magnification.
* If the service can control magnification but fullscreen magnifier is not registered, it will
* register the magnifier for this call then unregister the magnifier finally to make the
* magnification center correct.
@@ -162,25 +168,19 @@
* @return the X coordinate
*/
public float getCenterX(int displayId, boolean canControlMagnification) {
- int mode = getControllingMode(displayId);
- if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
- boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
- canControlMagnification);
- try {
- return mController.getFullScreenMagnificationController().getCenterX(displayId);
- } finally {
- if (registeredJustForThisCall) {
- unregister(displayId);
- }
+ boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
+ canControlMagnification);
+ try {
+ return mController.getFullScreenMagnificationController().getCenterX(displayId);
+ } finally {
+ if (registeredJustForThisCall) {
+ unregister(displayId);
}
- } else if (mode == MAGNIFICATION_MODE_WINDOW) {
- return mController.getWindowMagnificationMgr().getCenterX(displayId);
}
- return 0;
}
/**
- * Returns the magnification center in Y coordinate of the controlling magnification mode.
+ * Returns the magnification center in Y coordinate of full-screen magnification.
* If the service can control magnification but fullscreen magnifier is not registered, it will
* register the magnifier for this call then unregister the magnifier finally to make the
* magnification center correct.
@@ -190,49 +190,25 @@
* @return the Y coordinate
*/
public float getCenterY(int displayId, boolean canControlMagnification) {
- int mode = getControllingMode(displayId);
- if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
- boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
- canControlMagnification);
- try {
- return mController.getFullScreenMagnificationController().getCenterY(displayId);
- } finally {
- if (registeredJustForThisCall) {
- unregister(displayId);
- }
+ boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
+ canControlMagnification);
+ try {
+ return mController.getFullScreenMagnificationController().getCenterY(displayId);
+ } finally {
+ if (registeredJustForThisCall) {
+ unregister(displayId);
}
- } else if (mode == MAGNIFICATION_MODE_WINDOW) {
- return mController.getWindowMagnificationMgr().getCenterY(displayId);
}
- return 0;
}
/**
- * Return the magnification bounds of the current controlling magnification on the given
- * display. If the magnifier is not enabled, it returns an empty region.
- * If the service can control magnification but fullscreen magnifier is not registered, it will
- * register the magnifier for this call then unregister the magnifier finally to make
- * the magnification region correct.
+ * Returns the magnification bounds of full-screen magnification on the given display.
*
* @param displayId The logical display id
* @param outRegion the region to populate
* @param canControlMagnification Whether the service can control magnification
- * @return outRegion the magnification bounds of full-screen magnifier or the magnification
- * source bounds of window magnifier
*/
- public Region getMagnificationRegion(int displayId, @NonNull Region outRegion,
- boolean canControlMagnification) {
- int mode = getControllingMode(displayId);
- if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
- getFullscreenMagnificationRegion(displayId, outRegion, canControlMagnification);
- } else if (mode == MAGNIFICATION_MODE_WINDOW) {
- mController.getWindowMagnificationMgr().getMagnificationSourceBounds(displayId,
- outRegion);
- }
- return outRegion;
- }
-
- private void getFullscreenMagnificationRegion(int displayId, @NonNull Region outRegion,
+ public void getFullscreenMagnificationRegion(int displayId, @NonNull Region outRegion,
boolean canControlMagnification) {
boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
canControlMagnification);
@@ -246,21 +222,9 @@
}
}
- private boolean setScaleAndCenterForFullScreenMagnification(int displayId, float scale,
- float centerX, float centerY,
- boolean animate, int id) {
- if (!isRegistered(displayId)) {
- register(displayId);
- }
- return mController.getFullScreenMagnificationController().setScaleAndCenter(
- displayId,
- scale,
- centerX, centerY, animate, id);
- }
-
/**
- * Resets the magnification on the given display. The reset mode could be full-screen or
- * window if it is activated.
+ * Resets the current magnification on the given display. The reset mode could be
+ * full-screen or window if it is activated.
*
* @param displayId The logical display id.
* @param animate {@code true} to animate the transition, {@code false}
@@ -268,7 +232,7 @@
* @return {@code true} if the magnification spec changed, {@code false} if
* the spec did not change
*/
- public boolean reset(int displayId, boolean animate) {
+ public boolean resetCurrentMagnification(int displayId, boolean animate) {
int mode = getControllingMode(displayId);
if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
return mController.getFullScreenMagnificationController().reset(displayId, animate);
@@ -279,6 +243,19 @@
}
/**
+ * Resets the full-screen magnification on the given display.
+ *
+ * @param displayId The logical display id.
+ * @param animate {@code true} to animate the transition, {@code false}
+ * to transition immediately
+ * @return {@code true} if the magnification spec changed, {@code false} if
+ * the spec did not change
+ */
+ public boolean resetFullscreenMagnification(int displayId, boolean animate) {
+ return mController.getFullScreenMagnificationController().reset(displayId, animate);
+ }
+
+ /**
* {@link FullScreenMagnificationController#resetIfNeeded(int, boolean)}
*/
// TODO: support window magnification
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 0855b9d..0fe90b1 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -6,7 +6,6 @@
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
-import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
import android.app.ApplicationThreadConstants;
import android.app.IBackupAgent;
@@ -22,6 +21,7 @@
import android.os.SELinux;
import android.util.Slog;
+import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.fullbackup.AppMetadataBackupWriter;
import com.android.server.backup.remote.ServiceBackupCallback;
import com.android.server.backup.utils.FullBackupUtils;
@@ -162,7 +162,7 @@
long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
try {
mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
- OP_TYPE_BACKUP_WAIT);
+ OpType.BACKUP_WAIT);
IBackupCallback callback =
new ServiceBackupCallback(
@@ -262,7 +262,7 @@
pipes = ParcelFileDescriptor.createPipe();
mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
- OP_TYPE_BACKUP_WAIT);
+ OpType.BACKUP_WAIT);
// We will have to create a runnable that will read the manifest and backup data we
// created, such that we can pipe the data into mOutput. The reason we do this is that
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 98ea03e..81d6381 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -107,12 +107,14 @@
import com.android.server.AppWidgetBackupBridge;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
+import com.android.server.backup.OperationStorage.OpState;
+import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.fullbackup.FullBackupEntry;
import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
import com.android.server.backup.internal.BackupHandler;
import com.android.server.backup.internal.ClearDataObserver;
+import com.android.server.backup.internal.LifecycleOperationStorage;
import com.android.server.backup.internal.OnTaskFinishedListener;
-import com.android.server.backup.internal.Operation;
import com.android.server.backup.internal.PerformInitializeTask;
import com.android.server.backup.internal.RunInitializeReceiver;
import com.android.server.backup.internal.SetupObserver;
@@ -287,21 +289,6 @@
private static final String BACKUP_FINISHED_ACTION = "android.intent.action.BACKUP_FINISHED";
private static final String BACKUP_FINISHED_PACKAGE_EXTRA = "packageName";
- // Bookkeeping of in-flight operations. The operation token is the index of the entry in the
- // pending operations list.
- public static final int OP_PENDING = 0;
- private static final int OP_ACKNOWLEDGED = 1;
- private static final int OP_TIMEOUT = -1;
-
- // Waiting for backup agent to respond during backup operation.
- public static final int OP_TYPE_BACKUP_WAIT = 0;
-
- // Waiting for backup agent to respond during restore operation.
- public static final int OP_TYPE_RESTORE_WAIT = 1;
-
- // An entire backup operation spanning multiple packages.
- public static final int OP_TYPE_BACKUP = 2;
-
// Time delay for initialization operations that can be delayed so as not to consume too much
// CPU on bring-up and increase time-to-UI.
private static final long INITIALIZATION_DELAY_MILLIS = 3000;
@@ -400,30 +387,8 @@
private ActiveRestoreSession mActiveRestoreSession;
- /**
- * mCurrentOperations contains the list of currently active operations.
- *
- * If type of operation is OP_TYPE_WAIT, it are waiting for an ack or timeout.
- * An operation wraps a BackupRestoreTask within it.
- * It's the responsibility of this task to remove the operation from this array.
- *
- * A BackupRestore task gets notified of ack/timeout for the operation via
- * BackupRestoreTask#handleCancel, BackupRestoreTask#operationComplete and notifyAll called
- * on the mCurrentOpLock.
- * {@link UserBackupManagerService#waitUntilOperationComplete(int)} is
- * used in various places to 'wait' for notifyAll and detect change of pending state of an
- * operation. So typically, an operation will be removed from this array by:
- * - BackupRestoreTask#handleCancel and
- * - BackupRestoreTask#operationComplete OR waitUntilOperationComplete. Do not remove at both
- * these places because waitUntilOperationComplete relies on the operation being present to
- * determine its completion status.
- *
- * If type of operation is OP_BACKUP, it is a task running backups. It provides a handle to
- * cancel backup tasks.
- */
- @GuardedBy("mCurrentOpLock")
- private final SparseArray<Operation> mCurrentOperations = new SparseArray<>();
- private final Object mCurrentOpLock = new Object();
+ private final LifecycleOperationStorage mOperationStorage;
+
private final Random mTokenGenerator = new Random();
private final AtomicInteger mNextToken = new AtomicInteger();
@@ -542,12 +507,14 @@
}
@VisibleForTesting
- UserBackupManagerService(Context context, PackageManager packageManager) {
+ UserBackupManagerService(Context context, PackageManager packageManager,
+ LifecycleOperationStorage operationStorage) {
mContext = context;
mUserId = 0;
mRegisterTransportsRequestedTime = 0;
mPackageManager = packageManager;
+ mOperationStorage = operationStorage;
mBaseStateDir = null;
mDataDir = null;
@@ -600,8 +567,10 @@
BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
mAgentTimeoutParameters.start();
+ mOperationStorage = new LifecycleOperationStorage(mUserId);
+
Objects.requireNonNull(userBackupThread, "userBackupThread cannot be null");
- mBackupHandler = new BackupHandler(this, userBackupThread);
+ mBackupHandler = new BackupHandler(this, mOperationStorage, userBackupThread);
// Set up our bookkeeping
final ContentResolver resolver = context.getContentResolver();
@@ -756,6 +725,10 @@
return mTransportManager;
}
+ public OperationStorage getOperationStorage() {
+ return mOperationStorage;
+ }
+
public boolean isEnabled() {
return mEnabled;
}
@@ -838,14 +811,6 @@
return mActiveRestoreSession;
}
- public SparseArray<Operation> getCurrentOperations() {
- return mCurrentOperations;
- }
-
- public Object getCurrentOpLock() {
- return mCurrentOpLock;
- }
-
public SparseArray<AdbParams> getAdbBackupRestoreConfirmations() {
return mAdbBackupRestoreConfirmations;
}
@@ -1987,18 +1952,12 @@
}
final long oldToken = Binder.clearCallingIdentity();
try {
- List<Integer> operationsToCancel = new ArrayList<>();
- synchronized (mCurrentOpLock) {
- for (int i = 0; i < mCurrentOperations.size(); i++) {
- Operation op = mCurrentOperations.valueAt(i);
- int token = mCurrentOperations.keyAt(i);
- if (op.type == OP_TYPE_BACKUP) {
- operationsToCancel.add(token);
- }
- }
- }
+ Set<Integer> operationsToCancel =
+ mOperationStorage.operationTokensForOpType(OpType.BACKUP);
+
for (Integer token : operationsToCancel) {
- handleCancel(token, true /* cancelAll */);
+ mOperationStorage.cancelOperation(token, /* cancelAll */ true,
+ operationType -> { /* no callback needed here */ });
}
// We don't want the backup jobs to kick in any time soon.
// Reschedules them to run in the distant future.
@@ -2012,7 +1971,7 @@
/** Schedule a timeout message for the operation identified by {@code token}. */
public void prepareOperationTimeout(int token, long interval, BackupRestoreTask callback,
int operationType) {
- if (operationType != OP_TYPE_BACKUP_WAIT && operationType != OP_TYPE_RESTORE_WAIT) {
+ if (operationType != OpType.BACKUP_WAIT && operationType != OpType.RESTORE_WAIT) {
Slog.wtf(
TAG,
addUserIdToLogMessage(
@@ -2036,19 +1995,17 @@
+ callback));
}
- synchronized (mCurrentOpLock) {
- mCurrentOperations.put(token, new Operation(OP_PENDING, callback, operationType));
- Message msg = mBackupHandler.obtainMessage(getMessageIdForOperationType(operationType),
- token, 0, callback);
- mBackupHandler.sendMessageDelayed(msg, interval);
- }
+ mOperationStorage.registerOperation(token, OpState.PENDING, callback, operationType);
+ Message msg = mBackupHandler.obtainMessage(getMessageIdForOperationType(operationType),
+ token, 0, callback);
+ mBackupHandler.sendMessageDelayed(msg, interval);
}
private int getMessageIdForOperationType(int operationType) {
switch (operationType) {
- case OP_TYPE_BACKUP_WAIT:
+ case OpType.BACKUP_WAIT:
return MSG_BACKUP_OPERATION_TIMEOUT;
- case OP_TYPE_RESTORE_WAIT:
+ case OpType.RESTORE_WAIT:
return MSG_RESTORE_OPERATION_TIMEOUT;
default:
Slog.wtf(
@@ -2061,162 +2018,28 @@
}
}
- /**
- * Add an operation to the list of currently running operations. Used for cancellation,
- * completion and timeout callbacks that act on the operation via the {@code token}.
- */
- public void putOperation(int token, Operation operation) {
- if (MORE_DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Adding operation token="
- + Integer.toHexString(token)
- + ", operation type="
- + operation.type));
- }
- synchronized (mCurrentOpLock) {
- mCurrentOperations.put(token, operation);
- }
- }
-
- /**
- * Remove an operation from the list of currently running operations. An operation is removed
- * when it is completed, cancelled, or timed out, and thus no longer running.
- */
- public void removeOperation(int token) {
- if (MORE_DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Removing operation token=" + Integer.toHexString(token)));
- }
- synchronized (mCurrentOpLock) {
- if (mCurrentOperations.get(token) == null) {
- Slog.w(TAG, addUserIdToLogMessage(mUserId, "Duplicate remove for operation. token="
- + Integer.toHexString(token)));
- }
- mCurrentOperations.remove(token);
- }
- }
-
/** Block until we received an operation complete message (from the agent or cancellation). */
public boolean waitUntilOperationComplete(int token) {
- if (MORE_DEBUG) {
- Slog.i(TAG, addUserIdToLogMessage(mUserId, "Blocking until operation complete for "
- + Integer.toHexString(token)));
- }
- int finalState = OP_PENDING;
- Operation op = null;
- synchronized (mCurrentOpLock) {
- while (true) {
- op = mCurrentOperations.get(token);
- if (op == null) {
- // mysterious disappearance: treat as success with no callback
- break;
- } else {
- if (op.state == OP_PENDING) {
- try {
- mCurrentOpLock.wait();
- } catch (InterruptedException e) {
- }
- // When the wait is notified we loop around and recheck the current state
- } else {
- if (MORE_DEBUG) {
- Slog.d(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Unblocked waiting for operation token="
- + Integer.toHexString(token)));
- }
- // No longer pending; we're done
- finalState = op.state;
- break;
- }
- }
- }
- }
-
- removeOperation(token);
- if (op != null) {
- mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
- }
- if (MORE_DEBUG) {
- Slog.v(TAG, addUserIdToLogMessage(mUserId, "operation " + Integer.toHexString(token)
- + " complete: finalState=" + finalState));
- }
- return finalState == OP_ACKNOWLEDGED;
+ return mOperationStorage.waitUntilOperationComplete(token, operationType -> {
+ mBackupHandler.removeMessages(getMessageIdForOperationType(operationType));
+ });
}
/** Cancel the operation associated with {@code token}. */
public void handleCancel(int token, boolean cancelAll) {
- // Notify any synchronous waiters
- Operation op = null;
- synchronized (mCurrentOpLock) {
- op = mCurrentOperations.get(token);
- if (MORE_DEBUG) {
- if (op == null) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Cancel of token "
- + Integer.toHexString(token)
- + " but no op found"));
- }
+ // Remove all pending timeout messages of types OpType.BACKUP_WAIT and
+ // OpType.RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and
+ // doesn't require cancellation.
+ mOperationStorage.cancelOperation(token, cancelAll, operationType -> {
+ if (operationType == OpType.BACKUP_WAIT || operationType == OpType.RESTORE_WAIT) {
+ mBackupHandler.removeMessages(getMessageIdForOperationType(operationType));
}
- int state = (op != null) ? op.state : OP_TIMEOUT;
- if (state == OP_ACKNOWLEDGED) {
- // The operation finished cleanly, so we have nothing more to do.
- if (DEBUG) {
- Slog.w(TAG, addUserIdToLogMessage(mUserId, "Operation already got an ack."
- + "Should have been removed from mCurrentOperations."));
- }
- op = null;
- mCurrentOperations.delete(token);
- } else if (state == OP_PENDING) {
- if (DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId, "Cancel: token=" + Integer.toHexString(token)));
- }
- op.state = OP_TIMEOUT;
- // Can't delete op from mCurrentOperations here. waitUntilOperationComplete may be
- // called after we receive cancel here. We need this op's state there.
-
- // Remove all pending timeout messages of types OP_TYPE_BACKUP_WAIT and
- // OP_TYPE_RESTORE_WAIT. On the other hand, OP_TYPE_BACKUP cannot time out and
- // doesn't require cancellation.
- if (op.type == OP_TYPE_BACKUP_WAIT || op.type == OP_TYPE_RESTORE_WAIT) {
- mBackupHandler.removeMessages(getMessageIdForOperationType(op.type));
- }
- }
- mCurrentOpLock.notifyAll();
- }
-
- // If there's a TimeoutHandler for this event, call it
- if (op != null && op.callback != null) {
- if (MORE_DEBUG) {
- Slog.v(TAG, addUserIdToLogMessage(mUserId, " Invoking cancel on " + op.callback));
- }
- op.callback.handleCancel(cancelAll);
- }
+ });
}
/** Returns {@code true} if a backup is currently running, else returns {@code false}. */
public boolean isBackupOperationInProgress() {
- synchronized (mCurrentOpLock) {
- for (int i = 0; i < mCurrentOperations.size(); i++) {
- Operation op = mCurrentOperations.valueAt(i);
- if (op.type == OP_TYPE_BACKUP && op.state == OP_PENDING) {
- return true;
- }
- }
- }
- return false;
+ return mOperationStorage.isBackupOperationInProgress();
}
/** Unbind the backup agent and kill the app if it's a non-system app. */
@@ -2578,6 +2401,7 @@
String[] pkg = new String[]{entry.packageName};
mRunningFullBackupTask = PerformFullTransportBackupTask.newWithCurrentTransport(
this,
+ mOperationStorage,
/* observer */ null,
pkg,
/* updateSchedule */ true,
@@ -3107,6 +2931,7 @@
CountDownLatch latch = new CountDownLatch(1);
Runnable task = PerformFullTransportBackupTask.newWithCurrentTransport(
this,
+ mOperationStorage,
/* observer */ null,
pkgNames,
/* updateSchedule */ false,
@@ -4126,48 +3951,11 @@
* outstanding asynchronous backup/restore operation.
*/
public void opComplete(int token, long result) {
- if (MORE_DEBUG) {
- Slog.v(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "opComplete: " + Integer.toHexString(token) + " result=" + result));
- }
- Operation op = null;
- synchronized (mCurrentOpLock) {
- op = mCurrentOperations.get(token);
- if (op != null) {
- if (op.state == OP_TIMEOUT) {
- // The operation already timed out, and this is a late response. Tidy up
- // and ignore it; we've already dealt with the timeout.
- op = null;
- mCurrentOperations.delete(token);
- } else if (op.state == OP_ACKNOWLEDGED) {
- if (DEBUG) {
- Slog.w(
- TAG,
- addUserIdToLogMessage(
- mUserId,
- "Received duplicate ack for token="
- + Integer.toHexString(token)));
- }
- op = null;
- mCurrentOperations.remove(token);
- } else if (op.state == OP_PENDING) {
- // Can't delete op from mCurrentOperations. waitUntilOperationComplete can be
- // called after we we receive this call.
- op.state = OP_ACKNOWLEDGED;
- }
- }
- mCurrentOpLock.notifyAll();
- }
-
- // The completion callback, if any, is invoked on the handler
- if (op != null && op.callback != null) {
- Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(op.callback, result);
+ mOperationStorage.onOperationComplete(token, result, callback -> {
+ Pair<BackupRestoreTask, Long> callbackAndResult = Pair.create(callback, result);
Message msg = mBackupHandler.obtainMessage(MSG_OP_COMPLETE, callbackAndResult);
mBackupHandler.sendMessage(msg);
- }
+ });
}
/** Checks if the package is eligible for backup. */
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index fe5497f..1e1ca95 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -21,7 +21,6 @@
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
-import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import android.annotation.UserIdInt;
@@ -39,6 +38,7 @@
import com.android.server.AppWidgetBackupBridge;
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.remote.RemoteCall;
import com.android.server.backup.utils.BackupEligibilityRules;
@@ -147,7 +147,7 @@
mToken,
timeout,
mTimeoutMonitor /* in parent class */,
- OP_TYPE_BACKUP_WAIT);
+ OpType.BACKUP_WAIT);
mAgent.doFullBackup(
mPipe,
mQuota,
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
index aaf1f0a..be6ac26 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
@@ -18,7 +18,6 @@
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
import android.app.backup.IBackupManager;
import android.content.ComponentName;
@@ -33,6 +32,7 @@
import com.android.internal.backup.IObbBackupService;
import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.utils.FullBackupUtils;
@@ -83,7 +83,7 @@
long fullBackupAgentTimeoutMillis =
mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
backupManagerService.prepareOperationTimeout(
- token, fullBackupAgentTimeoutMillis, null, OP_TYPE_BACKUP_WAIT);
+ token, fullBackupAgentTimeoutMillis, null, OpType.BACKUP_WAIT);
mService.backupObbs(pkg.packageName, pipes[1], token,
backupManagerService.getBackupManagerBinder());
FullBackupUtils.routeSocketDataToOutput(pipes[0], out);
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
index 448e086..7ee307e 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformAdbBackupTask.java
@@ -37,6 +37,7 @@
import com.android.server.AppWidgetBackupBridge;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.KeyValueAdbBackupEngine;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.utils.BackupEligibilityRules;
import com.android.server.backup.utils.PasswordUtils;
@@ -67,6 +68,7 @@
public class PerformAdbBackupTask extends FullBackupTask implements BackupRestoreTask {
private final UserBackupManagerService mUserBackupManagerService;
+ private final OperationStorage mOperationStorage;
private final AtomicBoolean mLatch;
private final ParcelFileDescriptor mOutputFile;
@@ -85,7 +87,8 @@
private final int mCurrentOpToken;
private final BackupEligibilityRules mBackupEligibilityRules;
- public PerformAdbBackupTask(UserBackupManagerService backupManagerService,
+ public PerformAdbBackupTask(
+ UserBackupManagerService backupManagerService, OperationStorage operationStorage,
ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets,
String curPassword, String encryptPassword, boolean doAllApps, boolean doSystem,
@@ -93,6 +96,7 @@
BackupEligibilityRules backupEligibilityRules) {
super(observer);
mUserBackupManagerService = backupManagerService;
+ mOperationStorage = operationStorage;
mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
mLatch = latch;
@@ -505,6 +509,6 @@
if (target != null) {
mUserBackupManagerService.tearDownAgentAndKill(mCurrentTarget.applicationInfo);
}
- mUserBackupManagerService.removeOperation(mCurrentOpToken);
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
}
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 9ce4eab..0ca77d1 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -19,9 +19,6 @@
import static com.android.server.backup.BackupManagerService.DEBUG;
import static com.android.server.backup.BackupManagerService.DEBUG_SCHEDULING;
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.UserBackupManagerService.OP_PENDING;
-import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP;
-import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP_WAIT;
import android.annotation.Nullable;
import android.app.IBackupAgent;
@@ -45,10 +42,12 @@
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.FullBackupJob;
+import com.android.server.backup.OperationStorage;
+import com.android.server.backup.OperationStorage.OpState;
+import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
-import com.android.server.backup.internal.Operation;
import com.android.server.backup.remote.RemoteCall;
import com.android.server.backup.transport.BackupTransportClient;
import com.android.server.backup.transport.TransportConnection;
@@ -99,6 +98,7 @@
public class PerformFullTransportBackupTask extends FullBackupTask implements BackupRestoreTask {
public static PerformFullTransportBackupTask newWithCurrentTransport(
UserBackupManagerService backupManagerService,
+ OperationStorage operationStorage,
IFullBackupRestoreObserver observer,
String[] whichPackages,
boolean updateSchedule,
@@ -118,6 +118,7 @@
listenerCaller);
return new PerformFullTransportBackupTask(
backupManagerService,
+ operationStorage,
transportConnection,
observer,
whichPackages,
@@ -136,6 +137,7 @@
private UserBackupManagerService mUserBackupManagerService;
private final Object mCancelLock = new Object();
+ OperationStorage mOperationStorage;
List<PackageInfo> mPackages;
PackageInfo mCurrentPackage;
boolean mUpdateSchedule;
@@ -158,6 +160,7 @@
private final BackupEligibilityRules mBackupEligibilityRules;
public PerformFullTransportBackupTask(UserBackupManagerService backupManagerService,
+ OperationStorage operationStorage,
TransportConnection transportConnection,
IFullBackupRestoreObserver observer,
String[] whichPackages, boolean updateSchedule,
@@ -165,7 +168,8 @@
@Nullable IBackupManagerMonitor monitor, @Nullable OnTaskFinishedListener listener,
boolean userInitiated, BackupEligibilityRules backupEligibilityRules) {
super(observer);
- this.mUserBackupManagerService = backupManagerService;
+ mUserBackupManagerService = backupManagerService;
+ mOperationStorage = operationStorage;
mTransportConnection = transportConnection;
mUpdateSchedule = updateSchedule;
mLatch = latch;
@@ -261,16 +265,13 @@
}
private void registerTask() {
- synchronized (mUserBackupManagerService.getCurrentOpLock()) {
- Slog.d(TAG, "backupmanager pftbt token=" + Integer.toHexString(mCurrentOpToken));
- mUserBackupManagerService.getCurrentOperations().put(
- mCurrentOpToken,
- new Operation(OP_PENDING, this, OP_TYPE_BACKUP));
- }
+ Slog.d(TAG, "backupmanager pftbt token=" + Integer.toHexString(mCurrentOpToken));
+ mOperationStorage.registerOperation(mCurrentOpToken, OpState.PENDING, this, OpType.BACKUP);
}
+ // public, because called from KeyValueBackupTask.finishTask.
public void unregisterTask() {
- mUserBackupManagerService.removeOperation(mCurrentOpToken);
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
@Override
@@ -722,7 +723,7 @@
mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
try {
mUserBackupManagerService.prepareOperationTimeout(
- mCurrentOpToken, fullBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT);
+ mCurrentOpToken, fullBackupAgentTimeoutMillis, this, OpType.BACKUP_WAIT);
if (MORE_DEBUG) {
Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
}
@@ -777,7 +778,7 @@
}
mResult.set(result);
mLatch.countDown();
- mUserBackupManagerService.removeOperation(mCurrentOpToken);
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
@Override
@@ -787,7 +788,7 @@
}
mResult.set(BackupTransport.AGENT_ERROR);
mLatch.countDown();
- mUserBackupManagerService.removeOperation(mCurrentOpToken);
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
@Override
@@ -837,16 +838,12 @@
}
void registerTask() {
- synchronized (mUserBackupManagerService.getCurrentOpLock()) {
- mUserBackupManagerService.getCurrentOperations().put(
- mCurrentOpToken, new Operation(OP_PENDING, this, OP_TYPE_BACKUP_WAIT));
- }
+ mOperationStorage.registerOperation(mCurrentOpToken,
+ OpState.PENDING, this, OpType.BACKUP_WAIT);
}
void unregisterTask() {
- synchronized (mUserBackupManagerService.getCurrentOpLock()) {
- mUserBackupManagerService.getCurrentOperations().remove(mCurrentOpToken);
- }
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
@Override
@@ -956,7 +953,7 @@
mPreflightLatch.countDown();
mBackupLatch.countDown();
// We are done with this operation.
- mUserBackupManagerService.removeOperation(mCurrentOpToken);
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
}
}
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 5c24859..03796ea 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -35,6 +35,7 @@
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.PerformAdbBackupTask;
@@ -84,6 +85,7 @@
public static final int MSG_STOP = 22;
private final UserBackupManagerService backupManagerService;
+ private final OperationStorage mOperationStorage;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
private final HandlerThread mBackupThread;
@@ -92,10 +94,12 @@
volatile boolean mIsStopping = false;
public BackupHandler(
- UserBackupManagerService backupManagerService, HandlerThread backupThread) {
+ UserBackupManagerService backupManagerService, OperationStorage operationStorage,
+ HandlerThread backupThread) {
super(backupThread.getLooper());
mBackupThread = backupThread;
this.backupManagerService = backupManagerService;
+ mOperationStorage = operationStorage;
mAgentTimeoutParameters = Objects.requireNonNull(
backupManagerService.getAgentTimeoutParameters(),
"Timeout parameters cannot be null");
@@ -215,6 +219,7 @@
caller);
KeyValueBackupTask.start(
backupManagerService,
+ mOperationStorage,
transportConnection,
transport.transportDirName(),
queue,
@@ -278,8 +283,8 @@
// TODO: refactor full backup to be a looper-based state machine
// similar to normal backup/restore.
AdbBackupParams params = (AdbBackupParams) msg.obj;
- PerformAdbBackupTask task = new PerformAdbBackupTask(backupManagerService,
- params.fd,
+ PerformAdbBackupTask task = new PerformAdbBackupTask(
+ backupManagerService, mOperationStorage, params.fd,
params.observer, params.includeApks, params.includeObbs,
params.includeShared, params.doWidgets, params.curPassword,
params.encryptPassword, params.allApps, params.includeSystem,
@@ -296,6 +301,7 @@
PerformUnifiedRestoreTask task =
new PerformUnifiedRestoreTask(
backupManagerService,
+ mOperationStorage,
params.mTransportConnection,
params.observer,
params.monitor,
@@ -332,7 +338,7 @@
// similar to normal backup/restore.
AdbRestoreParams params = (AdbRestoreParams) msg.obj;
PerformAdbRestoreTask task = new PerformAdbRestoreTask(backupManagerService,
- params.fd,
+ mOperationStorage, params.fd,
params.curPassword, params.encryptPassword,
params.observer, params.latch);
(new Thread(task, "adb-restore")).start();
@@ -459,6 +465,7 @@
KeyValueBackupTask.start(
backupManagerService,
+ mOperationStorage,
params.mTransportConnection,
params.dirName,
params.kvPackages,
diff --git a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
index 30da8c1..16aa4eb 100644
--- a/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
+++ b/services/backup/java/com/android/server/backup/keyvalue/KeyValueBackupTask.java
@@ -23,8 +23,6 @@
import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
import static com.android.server.backup.UserBackupManagerService.KEY_WIDGET_STATE;
-import static com.android.server.backup.UserBackupManagerService.OP_PENDING;
-import static com.android.server.backup.UserBackupManagerService.OP_TYPE_BACKUP;
import android.annotation.IntDef;
import android.annotation.Nullable;
@@ -57,10 +55,12 @@
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.DataChangedJournal;
import com.android.server.backup.KeyValueBackupJob;
+import com.android.server.backup.OperationStorage;
+import com.android.server.backup.OperationStorage.OpState;
+import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.PerformFullTransportBackupTask;
import com.android.server.backup.internal.OnTaskFinishedListener;
-import com.android.server.backup.internal.Operation;
import com.android.server.backup.remote.RemoteCall;
import com.android.server.backup.remote.RemoteCallable;
import com.android.server.backup.remote.RemoteResult;
@@ -211,6 +211,7 @@
*/
public static KeyValueBackupTask start(
UserBackupManagerService backupManagerService,
+ OperationStorage operationStorage,
TransportConnection transportConnection,
String transportDirName,
List<String> queue,
@@ -227,6 +228,7 @@
KeyValueBackupTask task =
new KeyValueBackupTask(
backupManagerService,
+ operationStorage,
transportConnection,
transportDirName,
queue,
@@ -244,6 +246,7 @@
}
private final UserBackupManagerService mBackupManagerService;
+ private final OperationStorage mOperationStorage;
private final PackageManager mPackageManager;
private final TransportConnection mTransportConnection;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
@@ -302,6 +305,7 @@
@VisibleForTesting
public KeyValueBackupTask(
UserBackupManagerService backupManagerService,
+ OperationStorage operationStorage,
TransportConnection transportConnection,
String transportDirName,
List<String> queue,
@@ -313,6 +317,7 @@
boolean nonIncremental,
BackupEligibilityRules backupEligibilityRules) {
mBackupManagerService = backupManagerService;
+ mOperationStorage = operationStorage;
mPackageManager = backupManagerService.getPackageManager();
mTransportConnection = transportConnection;
mOriginalQueue = queue;
@@ -338,12 +343,11 @@
}
private void registerTask() {
- mBackupManagerService.putOperation(
- mCurrentOpToken, new Operation(OP_PENDING, this, OP_TYPE_BACKUP));
+ mOperationStorage.registerOperation(mCurrentOpToken, OpState.PENDING, this, OpType.BACKUP);
}
private void unregisterTask() {
- mBackupManagerService.removeOperation(mCurrentOpToken);
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
@Override
@@ -639,6 +643,7 @@
private PerformFullTransportBackupTask createFullBackupTask(List<String> packages) {
return new PerformFullTransportBackupTask(
mBackupManagerService,
+ mOperationStorage,
mTransportConnection,
/* fullBackupRestoreObserver */ null,
packages.toArray(new String[packages.size()]),
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
index 376b618..cfc0f20 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
@@ -23,6 +23,7 @@
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.UserBackupManagerService;
import java.util.Objects;
@@ -36,13 +37,16 @@
private static final String TAG = "AdbRestoreFinishedLatch";
private UserBackupManagerService backupManagerService;
+ private final OperationStorage mOperationStorage;
final CountDownLatch mLatch;
private final int mCurrentOpToken;
private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
public AdbRestoreFinishedLatch(UserBackupManagerService backupManagerService,
+ OperationStorage operationStorage,
int currentOpToken) {
this.backupManagerService = backupManagerService;
+ mOperationStorage = operationStorage;
mLatch = new CountDownLatch(1);
mCurrentOpToken = currentOpToken;
mAgentTimeoutParameters = Objects.requireNonNull(
@@ -72,7 +76,7 @@
Slog.w(TAG, "adb onRestoreFinished() complete");
}
mLatch.countDown();
- backupManagerService.removeOperation(mCurrentOpToken);
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
@Override
@@ -81,6 +85,6 @@
Slog.w(TAG, "adb onRestoreFinished() timed out");
}
mLatch.countDown();
- backupManagerService.removeOperation(mCurrentOpToken);
+ mOperationStorage.removeOperation(mCurrentOpToken);
}
}
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 5718bdf..76df8b9 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -21,7 +21,6 @@
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.UserBackupManagerService.BACKUP_MANIFEST_FILENAME;
import static com.android.server.backup.UserBackupManagerService.BACKUP_METADATA_FILENAME;
-import static com.android.server.backup.UserBackupManagerService.OP_TYPE_RESTORE_WAIT;
import static com.android.server.backup.UserBackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
@@ -48,6 +47,8 @@
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.FileMetadata;
import com.android.server.backup.KeyValueAdbRestoreEngine;
+import com.android.server.backup.OperationStorage;
+import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.FullBackupObbConnection;
import com.android.server.backup.utils.BackupEligibilityRules;
@@ -71,6 +72,7 @@
public class FullRestoreEngine extends RestoreEngine {
private final UserBackupManagerService mBackupManagerService;
+ private final OperationStorage mOperationStorage;
private final int mUserId;
// Task in charge of monitoring timeouts
@@ -133,12 +135,14 @@
private boolean mPipesClosed;
private final BackupEligibilityRules mBackupEligibilityRules;
- public FullRestoreEngine(UserBackupManagerService backupManagerService,
+ public FullRestoreEngine(
+ UserBackupManagerService backupManagerService, OperationStorage operationStorage,
BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
int ephemeralOpToken, boolean isAdbRestore,
BackupEligibilityRules backupEligibilityRules) {
mBackupManagerService = backupManagerService;
+ mOperationStorage = operationStorage;
mEphemeralOpToken = ephemeralOpToken;
mMonitorTask = monitorTask;
mObserver = observer;
@@ -409,7 +413,7 @@
mBackupManagerService.prepareOperationTimeout(token,
timeout,
mMonitorTask,
- OP_TYPE_RESTORE_WAIT);
+ OpType.RESTORE_WAIT);
if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) {
if (DEBUG) {
@@ -603,9 +607,9 @@
long fullBackupAgentTimeoutMillis =
mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(
- mBackupManagerService, token);
+ mBackupManagerService, mOperationStorage, token);
mBackupManagerService.prepareOperationTimeout(
- token, fullBackupAgentTimeoutMillis, latch, OP_TYPE_RESTORE_WAIT);
+ token, fullBackupAgentTimeoutMillis, latch, OpType.RESTORE_WAIT);
if (mTargetApp.processName.equals("system")) {
if (MORE_DEBUG) {
Slog.d(TAG, "system agent - restoreFinished on thread");
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index e03150e..22af19e 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -32,6 +32,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.fullbackup.FullBackupObbConnection;
import com.android.server.backup.utils.BackupEligibilityRules;
@@ -60,6 +61,7 @@
public class PerformAdbRestoreTask implements Runnable {
private final UserBackupManagerService mBackupManagerService;
+ private final OperationStorage mOperationStorage;
private final ParcelFileDescriptor mInputFile;
private final String mCurrentPassword;
private final String mDecryptPassword;
@@ -68,10 +70,12 @@
private IFullBackupRestoreObserver mObserver;
- public PerformAdbRestoreTask(UserBackupManagerService backupManagerService,
+ public PerformAdbRestoreTask(
+ UserBackupManagerService backupManagerService, OperationStorage operationStorage,
ParcelFileDescriptor fd, String curPassword, String decryptPassword,
IFullBackupRestoreObserver observer, AtomicBoolean latch) {
this.mBackupManagerService = backupManagerService;
+ mOperationStorage = operationStorage;
mInputFile = fd;
mCurrentPassword = curPassword;
mDecryptPassword = decryptPassword;
@@ -109,9 +113,9 @@
mBackupManagerService.getPackageManager(),
LocalServices.getService(PackageManagerInternal.class),
mBackupManagerService.getUserId(), BackupManager.OperationType.ADB_BACKUP);
- FullRestoreEngine mEngine = new FullRestoreEngine(mBackupManagerService, null,
- mObserver, null, null, true, 0 /*unused*/, true,
- eligibilityRules);
+ FullRestoreEngine mEngine = new FullRestoreEngine(mBackupManagerService,
+ mOperationStorage, null, mObserver, null, null,
+ true, 0 /*unused*/, true, eligibilityRules);
FullRestoreEngineThread mEngineThread = new FullRestoreEngineThread(mEngine,
tarInputStream);
mEngineThread.run();
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index ac831af..b48367d 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -20,7 +20,6 @@
import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
import static com.android.server.backup.BackupManagerService.TAG;
import static com.android.server.backup.UserBackupManagerService.KEY_WIDGET_STATE;
-import static com.android.server.backup.UserBackupManagerService.OP_TYPE_RESTORE_WAIT;
import static com.android.server.backup.UserBackupManagerService.PACKAGE_MANAGER_SENTINEL;
import static com.android.server.backup.UserBackupManagerService.SETTINGS_PACKAGE;
import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
@@ -60,6 +59,8 @@
import com.android.server.backup.BackupAgentTimeoutParameters;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.BackupUtils;
+import com.android.server.backup.OperationStorage;
+import com.android.server.backup.OperationStorage.OpType;
import com.android.server.backup.PackageManagerBackupAgent;
import com.android.server.backup.PackageManagerBackupAgent.Metadata;
import com.android.server.backup.TransportManager;
@@ -84,6 +85,7 @@
public class PerformUnifiedRestoreTask implements BackupRestoreTask {
private UserBackupManagerService backupManagerService;
+ private final OperationStorage mOperationStorage;
private final int mUserId;
private final TransportManager mTransportManager;
// Transport client we're working with to do the restore
@@ -169,6 +171,7 @@
PerformUnifiedRestoreTask(UserBackupManagerService backupManagerService) {
mListener = null;
mAgentTimeoutParameters = null;
+ mOperationStorage = null;
mTransportConnection = null;
mTransportManager = null;
mEphemeralOpToken = 0;
@@ -181,6 +184,7 @@
// about releasing it.
public PerformUnifiedRestoreTask(
UserBackupManagerService backupManagerService,
+ OperationStorage operationStorage,
TransportConnection transportConnection,
IRestoreObserver observer,
IBackupManagerMonitor monitor,
@@ -192,6 +196,7 @@
OnTaskFinishedListener listener,
BackupEligibilityRules backupEligibilityRules) {
this.backupManagerService = backupManagerService;
+ mOperationStorage = operationStorage;
mUserId = backupManagerService.getUserId();
mTransportManager = backupManagerService.getTransportManager();
mEphemeralOpToken = backupManagerService.generateRandomIntegerToken();
@@ -767,7 +772,7 @@
long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis(
app.applicationInfo.uid);
backupManagerService.prepareOperationTimeout(
- mEphemeralOpToken, restoreAgentTimeoutMillis, this, OP_TYPE_RESTORE_WAIT);
+ mEphemeralOpToken, restoreAgentTimeoutMillis, this, OpType.RESTORE_WAIT);
startedAgentRestore = true;
mAgent.doRestoreWithExcludedKeys(mBackupData, appVersionCode, mNewState,
mEphemeralOpToken, backupManagerService.getBackupManagerBinder(),
@@ -877,7 +882,7 @@
backupManagerService
.prepareOperationTimeout(mEphemeralOpToken,
restoreAgentFinishedTimeoutMillis, this,
- OP_TYPE_RESTORE_WAIT);
+ OpType.RESTORE_WAIT);
mAgent.doRestoreFinished(mEphemeralOpToken,
backupManagerService.getBackupManagerBinder());
// If we get this far, the callback or timeout will schedule the
@@ -921,7 +926,7 @@
EventLog.writeEvent(EventLogTags.FULL_RESTORE_PACKAGE,
mCurrentPackage.packageName);
- mEngine = new FullRestoreEngine(backupManagerService, this, null,
+ mEngine = new FullRestoreEngine(backupManagerService, mOperationStorage, this, null,
mMonitor, mCurrentPackage, false, mEphemeralOpToken, false,
mBackupEligibilityRules);
mEngineThread = new FullRestoreEngineThread(mEngine, mEnginePipes[0]);
@@ -1071,7 +1076,7 @@
// The app has timed out handling a restoring file
@Override
public void handleCancel(boolean cancelAll) {
- backupManagerService.removeOperation(mEphemeralOpToken);
+ mOperationStorage.removeOperation(mEphemeralOpToken);
if (DEBUG) {
Slog.w(TAG, "Full-data restore target timed out; shutting down");
}
@@ -1268,7 +1273,7 @@
@Override
public void operationComplete(long unusedResult) {
- backupManagerService.removeOperation(mEphemeralOpToken);
+ mOperationStorage.removeOperation(mEphemeralOpToken);
if (MORE_DEBUG) {
Slog.i(TAG, "operationComplete() during restore: target="
+ mCurrentPackage.packageName
@@ -1331,7 +1336,7 @@
// A call to agent.doRestore() or agent.doRestoreFinished() has timed out
@Override
public void handleCancel(boolean cancelAll) {
- backupManagerService.removeOperation(mEphemeralOpToken);
+ mOperationStorage.removeOperation(mEphemeralOpToken);
Slog.e(TAG, "Timeout restoring application " + mCurrentPackage.packageName);
mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
BackupManagerMonitor.LOG_EVENT_ID_KEY_VALUE_RESTORE_TIMEOUT,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a745e5a..4727b16 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -93,6 +93,7 @@
import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
@@ -307,6 +308,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
+import android.util.FeatureFlagUtils;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
@@ -14598,6 +14600,8 @@
private void checkExcessivePowerUsage() {
updateCpuStatsNow();
+ final boolean monitorPhantomProcs = mSystemReady && FeatureFlagUtils.isEnabled(mContext,
+ SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
synchronized (mProcLock) {
final boolean doCpuKills = mLastPowerCheckUptime != 0;
final long curUptime = SystemClock.uptimeMillis();
@@ -14623,9 +14627,11 @@
updateAppProcessCpuTimeLPr(uptimeSince, doCpuKills, checkDur, cpuLimit, app);
- // Also check the phantom processes if there is any
- updatePhantomProcessCpuTimeLPr(
- uptimeSince, doCpuKills, checkDur, cpuLimit, app);
+ if (monitorPhantomProcs) {
+ // Also check the phantom processes if there is any
+ updatePhantomProcessCpuTimeLPr(
+ uptimeSince, doCpuKills, checkDur, cpuLimit, app);
+ }
}
});
}
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 221de8d..d6a4cf6 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -19,6 +19,7 @@
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
import static android.os.Process.FIRST_APPLICATION_UID;
+import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_CRITICAL;
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
@@ -77,6 +78,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.DebugUtils;
+import android.util.FeatureFlagUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -1805,6 +1807,8 @@
}
void updateCpuStatsNow() {
+ final boolean monitorPhantomProcs = mService.mSystemReady && FeatureFlagUtils.isEnabled(
+ mService.mContext, SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
synchronized (mProcessCpuTracker) {
mProcessCpuMutexFree.set(false);
final long now = SystemClock.uptimeMillis();
@@ -1843,7 +1847,7 @@
}
}
- if (haveNewCpuStats) {
+ if (monitorPhantomProcs && haveNewCpuStats) {
mService.mPhantomProcessList.updateProcessCpuStatesLocked(mProcessCpuTracker);
}
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index b07684c..2ec1aed 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -18,6 +18,7 @@
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -28,6 +29,7 @@
import android.os.Handler;
import android.os.Process;
import android.os.StrictMode;
+import android.util.FeatureFlagUtils;
import android.util.Slog;
import android.util.SparseArray;
@@ -419,6 +421,10 @@
* order of the oom adjs of their parent process.
*/
void trimPhantomProcessesIfNecessary() {
+ if (!mService.mSystemReady || !FeatureFlagUtils.isEnabled(mService.mContext,
+ SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS)) {
+ return;
+ }
synchronized (mService.mProcLock) {
synchronized (mLock) {
mTrimPhantomProcessScheduled = false;
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 20606f0..8dce8e9 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -385,9 +385,8 @@
}
public boolean isValid() {
- return (mGameMode == GameManager.GAME_MODE_PERFORMANCE
- || mGameMode == GameManager.GAME_MODE_BATTERY)
- && (!mAllowDownscale || getCompatChangeId() != 0);
+ return mGameMode == GameManager.GAME_MODE_PERFORMANCE
+ || mGameMode == GameManager.GAME_MODE_BATTERY;
}
/**
@@ -839,7 +838,7 @@
}
long scaleId = modeConfig.getCompatChangeId();
if (scaleId == 0) {
- Slog.w(TAG, "Invalid downscaling change id " + scaleId + " for "
+ Slog.i(TAG, "Invalid downscaling change id " + scaleId + " for "
+ packageName);
return;
}
diff --git a/services/core/java/com/android/server/audio/AudioManagerShellCommand.java b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
new file mode 100644
index 0000000..241abaf
--- /dev/null
+++ b/services/core/java/com/android/server/audio/AudioManagerShellCommand.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.audio;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+
+class AudioManagerShellCommand extends ShellCommand {
+ private static final String TAG = "AudioManagerShellCommand";
+
+ private final AudioService mService;
+
+ AudioManagerShellCommand(AudioService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ final PrintWriter pw = getOutPrintWriter();
+ switch(cmd) {
+ case "set-surround-format-enabled":
+ return setSurroundFormatEnabled();
+ case "get-is-surround-format-enabled":
+ return getIsSurroundFormatEnabled();
+ case "set-encoded-surround-mode":
+ return setEncodedSurroundMode();
+ case "get-encoded-surround-mode":
+ return getEncodedSurroundMode();
+ }
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("Audio manager commands:");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println();
+ pw.println(" set-surround-format-enabled SURROUND_FORMAT IS_ENABLED");
+ pw.println(" Enables/disabled the SURROUND_FORMAT based on IS_ENABLED");
+ pw.println(" get-is-surround-format-enabled SURROUND_FORMAT");
+ pw.println(" Returns if the SURROUND_FORMAT is enabled");
+ pw.println(" set-encoded-surround-mode SURROUND_SOUND_MODE");
+ pw.println(" Sets the encoded surround sound mode to SURROUND_SOUND_MODE");
+ pw.println(" get-encoded-surround-mode");
+ pw.println(" Returns the encoded surround sound mode");
+ }
+
+ private int setSurroundFormatEnabled() {
+ String surroundFormatText = getNextArg();
+ String isSurroundFormatEnabledText = getNextArg();
+
+ if (surroundFormatText == null) {
+ getErrPrintWriter().println("Error: no surroundFormat specified");
+ return 1;
+ }
+
+ if (isSurroundFormatEnabledText == null) {
+ getErrPrintWriter().println("Error: no enabled value for surroundFormat specified");
+ return 1;
+ }
+
+ int surroundFormat = -1;
+ boolean isSurroundFormatEnabled = false;
+ try {
+ surroundFormat = Integer.parseInt(surroundFormatText);
+ isSurroundFormatEnabled = Boolean.parseBoolean(isSurroundFormatEnabledText);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: wrong format specified for surroundFormat");
+ return 1;
+ }
+ if (surroundFormat < 0) {
+ getErrPrintWriter().println("Error: invalid value of surroundFormat");
+ return 1;
+ }
+
+ final Context context = mService.mContext;
+ final AudioManager am = context.getSystemService(AudioManager.class);
+ am.setSurroundFormatEnabled(surroundFormat, isSurroundFormatEnabled);
+ return 0;
+ }
+
+ private int getIsSurroundFormatEnabled() {
+ String surroundFormatText = getNextArg();
+
+ if (surroundFormatText == null) {
+ getErrPrintWriter().println("Error: no surroundFormat specified");
+ return 1;
+ }
+
+ int surroundFormat = -1;
+ try {
+ surroundFormat = Integer.parseInt(surroundFormatText);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: wrong format specified for surroundFormat");
+ return 1;
+ }
+
+ if (surroundFormat < 0) {
+ getErrPrintWriter().println("Error: invalid value of surroundFormat");
+ return 1;
+ }
+ final Context context = mService.mContext;
+ final AudioManager am = context.getSystemService(AudioManager.class);
+ getOutPrintWriter().println("Value of enabled for " + surroundFormat + " is: "
+ + am.isSurroundFormatEnabled(surroundFormat));
+ return 0;
+ }
+
+ private int setEncodedSurroundMode() {
+ String encodedSurroundModeText = getNextArg();
+
+ if (encodedSurroundModeText == null) {
+ getErrPrintWriter().println("Error: no encodedSurroundMode specified");
+ return 1;
+ }
+
+ int encodedSurroundMode = -1;
+ try {
+ encodedSurroundMode = Integer.parseInt(encodedSurroundModeText);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: wrong format specified for encoded surround mode");
+ return 1;
+ }
+
+ if (encodedSurroundMode < 0) {
+ getErrPrintWriter().println("Error: invalid value of encodedSurroundMode");
+ return 1;
+ }
+
+ final Context context = mService.mContext;
+ final AudioManager am = context.getSystemService(AudioManager.class);
+ am.setEncodedSurroundMode(encodedSurroundMode);
+ return 0;
+ }
+
+ private int getEncodedSurroundMode() {
+ final Context context = mService.mContext;
+ final AudioManager am = context.getSystemService(AudioManager.class);
+ getOutPrintWriter().println("Encoded surround mode: " + am.getEncodedSurroundMode());
+ return 0;
+ }
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 6238198..8615393 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -130,7 +130,9 @@
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.ServiceManager;
+import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -1996,6 +1998,18 @@
}
}
+ @Override // Binder call
+ public void onShellCommand(FileDescriptor in, FileDescriptor out,
+ FileDescriptor err, String[] args, ShellCallback callback,
+ ResultReceiver resultReceiver) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.MANAGE_AUDIO_POLICY)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Missing MANAGE_AUDIO_POLICY permission");
+ }
+ new AudioManagerShellCommand(AudioService.this).exec(this, in, out, err,
+ args, callback, resultReceiver);
+ }
+
/** @see AudioManager#getSurroundFormats() */
@Override
public Map<Integer, Boolean> getSurroundFormats() {
@@ -10142,6 +10156,27 @@
return AudioManager.SUCCESS;
}
+ /** @see AudioPolicy#getFocusStack() */
+ public List<AudioFocusInfo> getFocusStack() {
+ enforceModifyAudioRoutingPermission();
+ return mMediaFocusControl.getFocusStack();
+ }
+
+ /** @see AudioPolicy#sendFocusLoss */
+ public boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser,
+ @NonNull IAudioPolicyCallback apcb) {
+ Objects.requireNonNull(focusLoser);
+ Objects.requireNonNull(apcb);
+ enforceModifyAudioRoutingPermission();
+ if (!mAudioPolicies.containsKey(apcb.asBinder())) {
+ throw new IllegalStateException("Only registered AudioPolicy can change focus");
+ }
+ if (!mAudioPolicies.get(apcb.asBinder()).mHasFocusListener) {
+ throw new IllegalStateException("AudioPolicy must have focus listener to change focus");
+ }
+ return mMediaFocusControl.sendFocusLoss(focusLoser);
+ }
+
/** see AudioManager.hasRegisteredDynamicPolicy */
public boolean hasRegisteredDynamicPolicy() {
synchronized (mAudioPolicies) {
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index 66f6235..69a4c23 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -27,6 +27,7 @@
import android.media.AudioSystem;
import android.media.IAudioFocusDispatcher;
import android.media.MediaMetrics;
+import android.media.audiopolicy.AudioPolicy;
import android.media.audiopolicy.IAudioPolicyCallback;
import android.os.Binder;
import android.os.Build;
@@ -221,6 +222,51 @@
}
}
+ /**
+ * Return a copy of the focus stack for external consumption (composed of AudioFocusInfo
+ * instead of FocusRequester instances)
+ * @return a SystemApi-friendly version of the focus stack, in the same order (last entry
+ * is top of focus stack, i.e. latest focus owner)
+ * @see AudioPolicy#getFocusStack()
+ */
+ @NonNull List<AudioFocusInfo> getFocusStack() {
+ synchronized (mAudioFocusLock) {
+ final ArrayList<AudioFocusInfo> stack = new ArrayList<>(mFocusStack.size());
+ for (FocusRequester fr : mFocusStack) {
+ stack.add(fr.toAudioFocusInfo());
+ }
+ return stack;
+ }
+ }
+
+ /**
+ * Send AUDIOFOCUS_LOSS to a specific stack entry.
+ * Note this method is supporting an external API, and is restricted to LOSS in order to
+ * prevent allowing the stack to be in an invalid state (e.g. entry inside stack has focus)
+ * @param focusLoser the stack entry that is exiting the stack through a focus loss
+ * @return false if the focusLoser wasn't found in the stack, true otherwise
+ * @see AudioPolicy#sendFocusLoss(AudioFocusInfo)
+ */
+ boolean sendFocusLoss(@NonNull AudioFocusInfo focusLoser) {
+ synchronized (mAudioFocusLock) {
+ FocusRequester loserToRemove = null;
+ for (FocusRequester fr : mFocusStack) {
+ if (fr.getClientId().equals(focusLoser.getClientId())) {
+ fr.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null,
+ false /*forceDuck*/);
+ loserToRemove = fr;
+ break;
+ }
+ }
+ if (loserToRemove != null) {
+ mFocusStack.remove(loserToRemove);
+ loserToRemove.release();
+ return true;
+ }
+ }
+ return false;
+ }
+
@GuardedBy("mAudioFocusLock")
private void notifyTopOfAudioFocusStack() {
// notify the top of the stack it gained focus
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 3f55848..4c56999 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1072,6 +1072,13 @@
+ "setUserDisabledHdrTypesInternal");
return;
}
+
+ // Verify if userDisabledHdrTypes contains expected HDR types
+ if (!isSubsetOf(Display.HdrCapabilities.HDR_TYPES, userDisabledHdrTypes)) {
+ Slog.e(TAG, "userDisabledHdrTypes contains unexpected types");
+ return;
+ }
+
Arrays.sort(userDisabledHdrTypes);
if (Arrays.equals(mUserDisabledHdrTypes, userDisabledHdrTypes)) {
return;
@@ -1094,6 +1101,15 @@
}
}
+ private boolean isSubsetOf(int[] sortedSuperset, int[] subset) {
+ for (int i : subset) {
+ if (Arrays.binarySearch(sortedSuperset, i) < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
private void setAreUserDisabledHdrTypesAllowedInternal(
boolean areUserDisabledHdrTypesAllowed) {
synchronized (mSyncRoot) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 9412c93..43a850c 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -20,9 +20,11 @@
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.os.ShellCommand;
+import android.util.Slog;
import android.view.Display;
import java.io.PrintWriter;
+import java.util.Arrays;
class DisplayManagerShellCommand extends ShellCommand {
private static final String TAG = "DisplayManagerShellCommand";
@@ -60,6 +62,20 @@
return setAmbientColorTemperatureOverride();
case "constrain-launcher-metrics":
return setConstrainLauncherMetrics();
+ case "set-user-preferred-display-mode":
+ return setUserPreferredDisplayMode();
+ case "clear-user-preferred-display-mode":
+ return clearUserPreferredDisplayMode();
+ case "get-user-preferred-display-mode":
+ return getUserPreferredDisplayMode();
+ case "set-match-content-frame-rate-pref":
+ return setMatchContentFrameRateUserPreference();
+ case "get-match-content-frame-rate-pref":
+ return getMatchContentFrameRateUserPreference();
+ case "set-user-disabled-hdr-types":
+ return setUserDisabledHdrTypes();
+ case "get-user-disabled-hdr-types":
+ return getUserDisabledHdrTypes();
default:
return handleDefaultCommands(cmd);
}
@@ -93,6 +109,21 @@
pw.println(" constrain-launcher-metrics [true|false]");
pw.println(" Sets if Display#getRealSize and getRealMetrics should be constrained for ");
pw.println(" Launcher.");
+ pw.println(" set-user-preferred-display-mode WIDTH HEIGHT REFRESH-RATE");
+ pw.println(" Sets the user preferred display mode which has fields WIDTH, HEIGHT and "
+ + "REFRESH-RATE");
+ pw.println(" clear-user-preferred-display-mode");
+ pw.println(" Clears the user preferred display mode");
+ pw.println(" get-user-preferred-display-mode");
+ pw.println(" Returns the user preferred display mode or null id no mode is set by user");
+ pw.println(" set-match-content-frame-rate-pref PREFERENCE");
+ pw.println(" Sets the match content frame rate preference as PREFERENCE ");
+ pw.println(" get-match-content-frame-rate-pref");
+ pw.println(" Returns the match content frame rate preference");
+ pw.println(" set-user-disabled-hdr-types TYPES...");
+ pw.println(" Sets the user disabled HDR types as TYPES");
+ pw.println(" get-user-disabled-hdr-types");
+ pw.println(" Returns the user disabled HDR types");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
@@ -166,4 +197,152 @@
mService.setShouldConstrainMetricsForLauncher(constrain);
return 0;
}
+
+ private int setUserPreferredDisplayMode() {
+ final String widthText = getNextArg();
+ if (widthText == null) {
+ getErrPrintWriter().println("Error: no width specified");
+ return 1;
+ }
+
+ final String heightText = getNextArg();
+ if (heightText == null) {
+ getErrPrintWriter().println("Error: no height specified");
+ return 1;
+ }
+
+ final String refreshRateText = getNextArg();
+ if (refreshRateText == null) {
+ getErrPrintWriter().println("Error: no refresh-rate specified");
+ return 1;
+ }
+
+ final int width, height;
+ final float refreshRate;
+ try {
+ width = Integer.parseInt(widthText);
+ height = Integer.parseInt(heightText);
+ refreshRate = Float.parseFloat(refreshRateText);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: invalid format of width, height or refresh rate");
+ return 1;
+ }
+ if (width < 0 || height < 0 || refreshRate <= 0.0f) {
+ getErrPrintWriter().println("Error: invalid value of width, height or refresh rate");
+ return 1;
+ }
+
+ final Context context = mService.getContext();
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ dm.setUserPreferredDisplayMode(new Display.Mode(width, height, refreshRate));
+ return 0;
+ }
+
+ private int clearUserPreferredDisplayMode() {
+ final Context context = mService.getContext();
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ dm.clearUserPreferredDisplayMode();
+ return 0;
+ }
+
+ private int getUserPreferredDisplayMode() {
+ final Context context = mService.getContext();
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ final Display.Mode mode = dm.getUserPreferredDisplayMode();
+ if (mode == null) {
+ getOutPrintWriter().println("User preferred display mode: null");
+ return 0;
+ }
+
+ getOutPrintWriter().println("User preferred display mode: " + mode.getPhysicalWidth() + " "
+ + mode.getPhysicalHeight() + " " + mode.getRefreshRate());
+ return 0;
+ }
+
+ private int setMatchContentFrameRateUserPreference() {
+ final String matchContentFrameRatePrefText = getNextArg();
+ if (matchContentFrameRatePrefText == null) {
+ getErrPrintWriter().println("Error: no matchContentFrameRatePref specified");
+ return 1;
+ }
+
+ final int matchContentFrameRatePreference;
+ try {
+ matchContentFrameRatePreference = Integer.parseInt(matchContentFrameRatePrefText);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: invalid format of matchContentFrameRatePreference");
+ return 1;
+ }
+ if (matchContentFrameRatePreference < 0) {
+ getErrPrintWriter().println("Error: invalid value of matchContentFrameRatePreference");
+ return 1;
+ }
+
+ final Context context = mService.getContext();
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+
+ final int refreshRateSwitchingType =
+ toRefreshRateSwitchingType(matchContentFrameRatePreference);
+ dm.setRefreshRateSwitchingType(refreshRateSwitchingType);
+ return 0;
+ }
+
+ private int getMatchContentFrameRateUserPreference() {
+ final Context context = mService.getContext();
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ getOutPrintWriter().println("Match content frame rate type: "
+ + dm.getMatchContentFrameRateUserPreference());
+ return 0;
+ }
+
+ private int setUserDisabledHdrTypes() {
+ final String[] userDisabledHdrTypesText = getAllArgs();
+ if (userDisabledHdrTypesText == null) {
+ getErrPrintWriter().println("Error: no userDisabledHdrTypes specified");
+ return 1;
+ }
+
+ int[] userDisabledHdrTypes = new int[userDisabledHdrTypesText.length];
+ try {
+ int index = 0;
+ for (String userDisabledHdrType : userDisabledHdrTypesText) {
+ userDisabledHdrTypes[index++] = Integer.parseInt(userDisabledHdrType);
+ }
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: invalid format of userDisabledHdrTypes");
+ return 1;
+ }
+
+ final Context context = mService.getContext();
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ dm.setUserDisabledHdrTypes(userDisabledHdrTypes);
+ return 0;
+ }
+
+ private int getUserDisabledHdrTypes() {
+ final Context context = mService.getContext();
+ final DisplayManager dm = context.getSystemService(DisplayManager.class);
+ final int[] userDisabledHdrTypes = dm.getUserDisabledHdrTypes();
+ getOutPrintWriter().println("User disabled HDR types: "
+ + Arrays.toString(userDisabledHdrTypes));
+ return 0;
+ }
+
+ @DisplayManager.SwitchingType
+ private int toRefreshRateSwitchingType(
+ @DisplayManager.MatchContentFrameRateType int matchContentFrameRateType) {
+ switch (matchContentFrameRateType) {
+ case DisplayManager.MATCH_CONTENT_FRAMERATE_NEVER:
+ return DisplayManager.SWITCHING_TYPE_NONE;
+ case DisplayManager.MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY:
+ return DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS;
+ case DisplayManager.MATCH_CONTENT_FRAMERATE_ALWAYS:
+ return DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS;
+ case DisplayManager.MATCH_CONTENT_FRAMERATE_UNKNOWN:
+ default:
+ Slog.e(TAG, matchContentFrameRateType + " is not a valid value of "
+ + "matchContentFrameRate type.");
+ return -1;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index 05e1bdd..3d91fee 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -39,6 +39,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.util.ArrayMap;
+import android.util.EventLog;
import android.util.Slog;
import android.view.IWindowManager;
import android.view.WindowManager;
@@ -49,6 +50,7 @@
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.UnbindReason;
import com.android.internal.view.IInputMethod;
+import com.android.server.EventLogTags;
import com.android.server.wm.WindowManagerInternal;
/**
@@ -58,6 +60,9 @@
static final boolean DEBUG = false;
private static final String TAG = InputMethodBindingController.class.getSimpleName();
+ /** Time in milliseconds that the IME service has to bind before it is reconnected. */
+ static final long TIME_TO_RECONNECT = 3 * 1000;
+
@NonNull private final InputMethodManagerService mService;
@NonNull private final Context mContext;
@NonNull private final ArrayMap<String, InputMethodInfo> mMethodMap;
@@ -239,7 +244,7 @@
}
/**
- * Indicates whether {@link #getVisibleConnection} is currently in use.
+ * Indicates whether {@link #mVisibleConnection} is currently in use.
*/
boolean isVisibleBound() {
return mVisibleBound;
@@ -248,11 +253,6 @@
/**
* Used to bring IME service up to visible adjustment while it is being shown.
*/
- @NonNull
- ServiceConnection getVisibleConnection() {
- return mVisibleConnection;
- }
-
private final ServiceConnection mVisibleConnection = new ServiceConnection() {
@Override public void onBindingDied(ComponentName name) {
synchronized (mMethodMap) {
@@ -334,7 +334,7 @@
// We consider this to be a new bind attempt, since the system
// should now try to restart the service for us.
mLastBindTime = SystemClock.uptimeMillis();
- mService.clearClientSessionsLocked();
+ clearCurMethodAndSessionsLocked();
mService.clearInputShowRequestLocked();
mService.unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME);
}
@@ -358,11 +358,12 @@
}
mCurId = null;
- mService.clearClientSessionsLocked();
+ clearCurMethodAndSessionsLocked();
}
@GuardedBy("mMethodMap")
- void clearCurMethodLocked() {
+ private void clearCurMethodAndSessionsLocked() {
+ mService.clearClientSessionsLocked();
mCurMethod = null;
mCurMethodUid = Process.INVALID_UID;
}
@@ -382,7 +383,7 @@
@GuardedBy("mMethodMap")
@NonNull
- InputBindResult bindCurrentMethodLocked(int displayIdToShowIme) {
+ InputBindResult bindCurrentMethodLocked() {
InputMethodInfo info = mMethodMap.get(mSelectedMethodId);
if (info == null) {
throw new IllegalArgumentException("Unknown id: " + mSelectedMethodId);
@@ -394,7 +395,7 @@
mCurId = info.getId();
mLastBindTime = SystemClock.uptimeMillis();
- addFreshWindowTokenLocked(displayIdToShowIme);
+ addFreshWindowTokenLocked();
return new InputBindResult(
InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
null, null, mCurId, mCurSeq, false);
@@ -419,7 +420,8 @@
}
@GuardedBy("mMethodMap")
- private void addFreshWindowTokenLocked(int displayIdToShowIme) {
+ private void addFreshWindowTokenLocked() {
+ int displayIdToShowIme = mService.getDisplayIdToShowIme();
mCurToken = new Binder();
mService.setCurTokenDisplayId(displayIdToShowIme);
@@ -438,7 +440,7 @@
}
@GuardedBy("mMethodMap")
- void unbindMainConnectionLocked() {
+ private void unbindMainConnectionLocked() {
mContext.unbindService(mMainConnection);
mHasConnection = false;
}
@@ -460,17 +462,61 @@
}
@GuardedBy("mMethodMap")
- boolean bindCurrentInputMethodServiceVisibleConnectionLocked() {
+ private boolean bindCurrentInputMethodServiceVisibleConnectionLocked() {
mVisibleBound = bindCurrentInputMethodServiceLocked(mVisibleConnection,
IME_VISIBLE_BIND_FLAGS);
return mVisibleBound;
}
@GuardedBy("mMethodMap")
- boolean bindCurrentInputMethodServiceMainConnectionLocked() {
+ private boolean bindCurrentInputMethodServiceMainConnectionLocked() {
mHasConnection = bindCurrentInputMethodServiceLocked(mMainConnection,
mImeConnectionBindFlags);
return mHasConnection;
}
+ /**
+ * Bind the IME so that it can be shown.
+ *
+ * <p>
+ * Performs a rebind if no binding is achieved in {@link #TIME_TO_RECONNECT} milliseconds.
+ */
+ @GuardedBy("mMethodMap")
+ void setCurrentMethodVisibleLocked() {
+ if (mCurMethod != null) {
+ if (DEBUG) Slog.d(TAG, "setCurrentMethodVisibleLocked: mCurToken=" + mCurToken);
+ if (mHasConnection && !mVisibleBound) {
+ bindCurrentInputMethodServiceVisibleConnectionLocked();
+ }
+ return;
+ }
+
+ long bindingDuration = SystemClock.uptimeMillis() - mLastBindTime;
+ if (mHasConnection && bindingDuration >= TIME_TO_RECONNECT) {
+ // The client has asked to have the input method shown, but
+ // we have been sitting here too long with a connection to the
+ // service and no interface received, so let's disconnect/connect
+ // to try to prod things along.
+ EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, getSelectedMethodId(),
+ bindingDuration, 1);
+ Slog.w(TAG, "Force disconnect/connect to the IME in setCurrentMethodVisibleLocked()");
+ unbindMainConnectionLocked();
+ bindCurrentInputMethodServiceMainConnectionLocked();
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Can't show input: connection = " + mHasConnection + ", time = "
+ + (TIME_TO_RECONNECT - bindingDuration));
+ }
+ }
+ }
+
+ /**
+ * Remove the binding needed for the IME to be shown.
+ */
+ @GuardedBy("mMethodMap")
+ void setCurrentMethodNotVisibleLocked() {
+ if (mVisibleBound) {
+ unbindVisibleConnectionLocked();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 3c6b096..bff4f27 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -49,6 +49,8 @@
import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE;
import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
+import static com.android.server.inputmethod.InputMethodBindingController.TIME_TO_RECONNECT;
+
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.Manifest;
@@ -111,12 +113,10 @@
import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
-import android.text.style.SuggestionSpan;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
-import android.util.LruCache;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Printer;
@@ -250,10 +250,6 @@
static final int MSG_NOTIFY_IME_UID_TO_AUDIO_SERVICE = 7000;
- static final long TIME_TO_RECONNECT = 3 * 1000;
-
- static final int SECURE_SUGGESTION_SPANS_MAX_SIZE = 20;
-
private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
private static final String HANDLER_THREAD_NAME = "android.imms";
@@ -301,8 +297,6 @@
// lock for this class.
final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
- private final LruCache<SuggestionSpan, InputMethodInfo> mSecureSuggestionSpans =
- new LruCache<>(SECURE_SUGGESTION_SPANS_MAX_SIZE);
final InputMethodSubtypeSwitchingController mSwitchingController;
/**
@@ -312,13 +306,16 @@
private int mMethodMapUpdateCount = 0;
/**
- * Indicates whether {@link InputMethodBindingController#getVisibleConnection} is currently
- * in use.
+ * The display id for which the latest startInput was called.
*/
- private boolean isVisibleBound() {
- return mBindingController.isVisibleBound();
+ @GuardedBy("mMethodMap")
+ int getDisplayIdToShowIme() {
+ return mDisplayIdToShowIme;
}
+ @GuardedBy("mMethodMap")
+ private int mDisplayIdToShowIme = INVALID_DISPLAY;
+
// Ongoing notification
private NotificationManager mNotificationManager;
KeyguardManager mKeyguardManager;
@@ -2354,10 +2351,10 @@
}
// Compute the final shown display ID with validated cs.selfReportedDisplayId for this
// session & other conditions.
- final int displayIdToShowIme = computeImeDisplayIdForTarget(cs.selfReportedDisplayId,
+ mDisplayIdToShowIme = computeImeDisplayIdForTarget(cs.selfReportedDisplayId,
mImeDisplayValidator);
- if (displayIdToShowIme == INVALID_DISPLAY) {
+ if (mDisplayIdToShowIme == INVALID_DISPLAY) {
mImeHiddenByDisplayPolicy = true;
hideCurrentInputLocked(mCurFocusedWindow, 0, null,
SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE);
@@ -2378,7 +2375,7 @@
// Check if the input method is changing.
// We expect the caller has already verified that the client is allowed to access this
// display ID.
- if (isSelectedMethodBound(displayIdToShowIme)) {
+ if (isSelectedMethodBound()) {
if (cs.curSession != null) {
// Fast case: if we are already connected to the input method,
// then just return it.
@@ -2394,13 +2391,13 @@
mBindingController.unbindCurrentMethodLocked();
- return mBindingController.bindCurrentMethodLocked(displayIdToShowIme);
+ return mBindingController.bindCurrentMethodLocked();
}
- private boolean isSelectedMethodBound(int displayIdToShowIme) {
+ private boolean isSelectedMethodBound() {
String curId = getCurId();
return curId != null && curId.equals(getSelectedMethodId())
- && displayIdToShowIme == mCurTokenDisplayId;
+ && mDisplayIdToShowIme == mCurTokenDisplayId;
}
@GuardedBy("mMethodMap")
@@ -2590,7 +2587,6 @@
finishSessionLocked(mEnabledSession);
mEnabledSession = null;
- mBindingController.clearCurMethodLocked();
scheduleNotifyImeUidToAudioService(Process.INVALID_UID);
}
hideStatusBarIconLocked();
@@ -3047,42 +3043,20 @@
return false;
}
- boolean res = false;
- IInputMethod curMethod = getCurMethod();
- if (curMethod != null) {
- if (DEBUG) Slog.d(TAG, "showCurrentInputLocked: mCurToken=" + getCurToken());
+ mBindingController.setCurrentMethodVisibleLocked();
+ if (getCurMethod() != null) {
// create a placeholder token for IMS so that IMS cannot inject windows into client app.
Binder showInputToken = new Binder();
mShowRequestWindowMap.put(showInputToken, windowToken);
+ IInputMethod curMethod = getCurMethod();
executeOrSendMessage(curMethod, mCaller.obtainMessageIIOOO(MSG_SHOW_SOFT_INPUT,
getImeShowFlagsLocked(), reason, curMethod, resultReceiver,
showInputToken));
mInputShown = true;
- if (hasConnection() && !isVisibleBound()) {
- mBindingController.bindCurrentInputMethodServiceVisibleConnectionLocked();
- }
- res = true;
- } else {
- long bindingDuration = SystemClock.uptimeMillis() - getLastBindTime();
- if (hasConnection() && bindingDuration >= TIME_TO_RECONNECT) {
- // The client has asked to have the input method shown, but
- // we have been sitting here too long with a connection to the
- // service and no interface received, so let's disconnect/connect
- // to try to prod things along.
- EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, getSelectedMethodId(),
- bindingDuration, 1);
- Slog.w(TAG, "Force disconnect/connect to the IME in showCurrentInputLocked()");
- mBindingController.unbindMainConnectionLocked();
- mBindingController.bindCurrentInputMethodServiceMainConnectionLocked();
- } else {
- if (DEBUG) {
- Slog.d(TAG, "Can't show input: connection = " + hasConnection() + ", time = "
- + (TIME_TO_RECONNECT - bindingDuration));
- }
- }
+ return true;
}
- return res;
+ return false;
}
@Override
@@ -3166,9 +3140,7 @@
} else {
res = false;
}
- if (hasConnection() && isVisibleBound()) {
- mBindingController.unbindVisibleConnectionLocked();
- }
+ mBindingController.setCurrentMethodNotVisibleLocked();
mInputShown = false;
mShowRequested = false;
mShowExplicitlyRequested = false;
@@ -3522,10 +3494,6 @@
return mWindowManagerInternal.shouldRestoreImeVisibility(windowToken);
}
- private boolean isImeVisible() {
- return (mImeWindowVis & InputMethodService.IME_VISIBLE) != 0;
- }
-
@GuardedBy("mMethodMap")
private boolean canShowInputMethodPickerLocked(IInputMethodClient client) {
// TODO(yukawa): multi-display support.
@@ -5114,7 +5082,8 @@
+ " client=" + mCurFocusedWindowClient);
focusedWindowClient = mCurFocusedWindowClient;
p.println(" mCurId=" + getCurId() + " mHaveConnection=" + hasConnection()
- + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound=" + isVisibleBound());
+ + " mBoundToMethod=" + mBoundToMethod + " mVisibleBound="
+ + mBindingController.isVisibleBound());
p.println(" mCurToken=" + getCurToken());
p.println(" mCurTokenDisplayId=" + mCurTokenDisplayId);
p.println(" mCurHostInputToken=" + mCurHostInputToken);
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 0ce24dd..ede8b32 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -177,7 +177,7 @@
protected interface LocationTransport {
void deliverOnLocationChanged(LocationResult locationResult,
- @Nullable Runnable onCompleteCallback) throws Exception;
+ @Nullable IRemoteCallback onCompleteCallback) throws Exception;
void deliverOnFlushComplete(int requestCode) throws Exception;
}
@@ -197,9 +197,8 @@
@Override
public void deliverOnLocationChanged(LocationResult locationResult,
- @Nullable Runnable onCompleteCallback) throws RemoteException {
- mListener.onLocationChanged(locationResult.asList(),
- SingleUseCallback.wrap(onCompleteCallback));
+ @Nullable IRemoteCallback onCompleteCallback) throws RemoteException {
+ mListener.onLocationChanged(locationResult.asList(), onCompleteCallback);
}
@Override
@@ -227,7 +226,7 @@
@Override
public void deliverOnLocationChanged(LocationResult locationResult,
- @Nullable Runnable onCompleteCallback)
+ @Nullable IRemoteCallback onCompleteCallback)
throws PendingIntent.CanceledException {
BroadcastOptions options = BroadcastOptions.makeBasic();
options.setDontSendToRestrictedApps(true);
@@ -243,20 +242,34 @@
intent.putExtra(KEY_LOCATIONS, locationResult.asList().toArray(new Location[0]));
}
+ PendingIntent.OnFinished onFinished = null;
+
// send() SHOULD only run the completion callback if it completes successfully. however,
- // b/199464864 (which could not be fixed in the S timeframe) means that it's possible
+ // b/201299281 (which could not be fixed in the S timeframe) means that it's possible
// for send() to throw an exception AND run the completion callback. if this happens, we
// would over-release the wakelock... we take matters into our own hands to ensure that
// the completion callback can only be run if send() completes successfully. this means
// the completion callback may be run inline - but as we've never specified what thread
// the callback is run on, this is fine.
- GatedCallback gatedCallback = new GatedCallback(onCompleteCallback);
+ GatedCallback gatedCallback;
+ if (onCompleteCallback != null) {
+ gatedCallback = new GatedCallback(() -> {
+ try {
+ onCompleteCallback.sendResult(null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ });
+ onFinished = (pI, i, rC, rD, rE) -> gatedCallback.run();
+ } else {
+ gatedCallback = new GatedCallback(null);
+ }
mPendingIntent.send(
mContext,
0,
intent,
- (pI, i, rC, rD, rE) -> gatedCallback.run(),
+ onFinished,
null,
null,
options.toBundle());
@@ -293,7 +306,7 @@
@Override
public void deliverOnLocationChanged(@Nullable LocationResult locationResult,
- @Nullable Runnable onCompleteCallback)
+ @Nullable IRemoteCallback onCompleteCallback)
throws RemoteException {
// ILocationCallback doesn't currently support completion callbacks
Preconditions.checkState(onCompleteCallback == null);
@@ -714,6 +727,13 @@
final PowerManager.WakeLock mWakeLock;
+ // b/206340085 - if we allocate a new wakelock releaser object for every delivery we
+ // increase the risk of resource starvation. if a client stops processing deliveries the
+ // system server binder allocation pool will be starved as we continue to queue up
+ // deliveries, each with a new allocation. in order to mitigate this, we use a single
+ // releaser object per registration rather than per delivery.
+ final ExternalWakeLockReleaser mWakeLockReleaser;
+
private volatile ProviderTransport mProviderTransport;
private int mNumLocationsDelivered = 0;
private long mExpirationRealtimeMs = Long.MAX_VALUE;
@@ -727,6 +747,7 @@
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG);
mWakeLock.setReferenceCounted(true);
mWakeLock.setWorkSource(request.getWorkSource());
+ mWakeLockReleaser = new ExternalWakeLockReleaser(identity, mWakeLock);
}
@Override
@@ -943,7 +964,7 @@
}
listener.deliverOnLocationChanged(deliverLocationResult,
- mUseWakeLock ? mWakeLock::release : null);
+ mUseWakeLock ? mWakeLockReleaser : null);
EVENT_LOG.logProviderDeliveredLocations(mName, locationResult.size(),
getIdentity());
}
@@ -2761,7 +2782,7 @@
@GuardedBy("this")
private boolean mRun;
- GatedCallback(Runnable callback) {
+ GatedCallback(@Nullable Runnable callback) {
mCallback = callback;
}
@@ -2796,4 +2817,24 @@
}
}
}
+
+ private static class ExternalWakeLockReleaser extends IRemoteCallback.Stub {
+
+ private final CallerIdentity mIdentity;
+ private final PowerManager.WakeLock mWakeLock;
+
+ ExternalWakeLockReleaser(CallerIdentity identity, PowerManager.WakeLock wakeLock) {
+ mIdentity = identity;
+ mWakeLock = Objects.requireNonNull(wakeLock);
+ }
+
+ @Override
+ public void sendResult(Bundle data) {
+ try {
+ mWakeLock.release();
+ } catch (RuntimeException e) {
+ Log.e(TAG, "wakelock over-released by " + mIdentity, e);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 0564e85..fdcf1fc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -18,6 +18,7 @@
import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
+import android.Manifest;
import android.accounts.IAccountManager;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -170,6 +171,20 @@
@Override
public int onCommand(String cmd) {
+ switch (Binder.getCallingUid()) {
+ case Process.ROOT_UID:
+ case Process.SHELL_UID:
+ break;
+ default:
+ // This is called from a test and is allowed as non-shell with the right permission
+ if ("install-incremental".equals(cmd)) {
+ mContext.enforceCallingPermission(Manifest.permission.USE_SYSTEM_DATA_LOADERS,
+ "Caller missing USE_SYSTEM_DATA_LOADERS permission to use " + cmd);
+ } else {
+ throw new IllegalArgumentException("Caller must be root or shell");
+ }
+ }
+
if (cmd == null) {
return handleDefaultCommands(cmd);
}
diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
index 111087f..a3b0e3e 100644
--- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
+++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationService.java
@@ -1197,6 +1197,7 @@
@Nullable @UserIdInt Integer userId,
@NonNull Function<String, PackageStateInternal> pkgSettingFunction)
throws NameNotFoundException {
+ mEnforcer.assertApprovedQuerent(mConnection.getCallingUid(), mProxy);
synchronized (mLock) {
mDebug.printState(writer, packageName, userId, pkgSettingFunction, mAttachedPkgStates);
}
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
index 122b3f3..1aa7598 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -42,6 +42,7 @@
import android.media.tv.interactive.TvIAppService;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -652,6 +653,9 @@
userState.mServiceStateMap.put(componentName, serviceState);
} else if (serviceState.mService != null) {
serviceState.mService.prepare(type);
+ } else {
+ serviceState.mPendingPrepare = true;
+ serviceState.mPendingPrepareType = type;
}
}
} catch (RemoteException e) {
@@ -662,6 +666,40 @@
}
@Override
+ public void notifyAppLinkInfo(String tiasId, Bundle appLinkInfo, int userId) {
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, "notifyAppLinkInfo");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ UserState userState = getOrCreateUserStateLocked(resolvedUserId);
+ TvIAppState iAppState = userState.mIAppMap.get(tiasId);
+ if (iAppState == null) {
+ Slogf.e(TAG, "failed to notifyAppLinkInfo - unknown TIAS id "
+ + tiasId);
+ return;
+ }
+ ComponentName componentName = iAppState.mInfo.getComponent();
+ ServiceState serviceState = userState.mServiceStateMap.get(componentName);
+ if (serviceState == null) {
+ serviceState = new ServiceState(
+ componentName, tiasId, resolvedUserId);
+ serviceState.addPendingAppLink(appLinkInfo);
+ userState.mServiceStateMap.put(componentName, serviceState);
+ } else if (serviceState.mService != null) {
+ serviceState.mService.notifyAppLinkInfo(appLinkInfo);
+ } else {
+ serviceState.addPendingAppLink(appLinkInfo);
+ }
+ }
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in notifyAppLinkInfo", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void createSession(final ITvIAppClient client, final String iAppServiceId, int type,
int seq, int userId) {
final int callingUid = Binder.getCallingUid();
@@ -1237,6 +1275,7 @@
private final ServiceConnection mConnection;
private final ComponentName mComponent;
private final String mIAppSeriviceId;
+ private final List<Bundle> mPendingAppLinkInfo = new ArrayList<>();
private boolean mPendingPrepare = false;
private Integer mPendingPrepareType = null;
@@ -1257,6 +1296,10 @@
mConnection = new IAppServiceConnection(component, userId);
mIAppSeriviceId = tias;
}
+
+ private void addPendingAppLink(Bundle info) {
+ mPendingAppLinkInfo.add(info);
+ }
}
private final class IAppServiceConnection implements ServiceConnection {
@@ -1296,6 +1339,23 @@
}
}
+ if (!serviceState.mPendingAppLinkInfo.isEmpty()) {
+ for (Iterator<Bundle> it = serviceState.mPendingAppLinkInfo.iterator();
+ it.hasNext(); ) {
+ Bundle appLinkInfo = it.next();
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ serviceState.mService.notifyAppLinkInfo(appLinkInfo);
+ it.remove();
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in notifyAppLinkInfo(" + appLinkInfo
+ + ") when onServiceConnected", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
List<IBinder> tokensToBeRemoved = new ArrayList<>();
// And create sessions, if any.
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index eb68952..44d3623 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -167,7 +167,6 @@
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.ColorSpace;
-import android.graphics.GraphicBuffer;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
@@ -4092,8 +4091,7 @@
// Make IME snapshot as trusted overlay
InputMonitor.setTrustedOverlayInputInfo(imeSurface, t, getDisplayId(),
"IME-snapshot-surface");
- GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(buffer);
- t.setBuffer(imeSurface, graphicBuffer);
+ t.setBuffer(imeSurface, buffer);
t.setColorSpace(mSurfaceControl, ColorSpace.get(ColorSpace.Named.SRGB));
t.setRelativeLayer(imeSurface, activity.getSurfaceControl(), 1);
t.setPosition(imeSurface, mInputMethodWindow.getDisplayFrame().left,
diff --git a/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp b/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
index ccb4f59..1c574fb 100644
--- a/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
+++ b/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
@@ -151,13 +151,8 @@
return -1;
}
- if (!jniRegisterNativeMethods(env, "com/android/server/usb/UsbAlsaJackDetector",
- method_table, NELEM(method_table))) {
- ALOGE("Can't register UsbAlsaJackDetector native methods");
- return -1;
- }
-
- return 0;
+ return jniRegisterNativeMethods(env, "com/android/server/usb/UsbAlsaJackDetector",
+ method_table, NELEM(method_table));
}
}
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 06f5aed..f0f779d 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -997,7 +997,7 @@
}
}
- if (gnssHalAidl != nullptr) {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
sp<IAGnssAidl> agnssAidl;
auto status = gnssHalAidl->getExtensionAGnss(&agnssAidl);
if (checkAidlStatus(status, "Unable to get a handle to AGnss interface.")) {
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index fd295c0..9e83f8e 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -278,7 +278,7 @@
assertThat(mBackupManagerService.getPendingInits()).isEmpty();
assertThat(mBackupManagerService.isBackupRunning()).isFalse();
- assertThat(mBackupManagerService.getCurrentOperations().size()).isEqualTo(0);
+ assertThat(mBackupManagerService.getOperationStorage().numOperations()).isEqualTo(0);
verify(mOldJournal).delete();
}
@@ -449,7 +449,7 @@
assertThat(mBackupManagerService.getPendingInits()).isEmpty();
assertThat(mBackupManagerService.isBackupRunning()).isFalse();
- assertThat(mBackupManagerService.getCurrentOperations().size()).isEqualTo(0);
+ assertThat(mBackupManagerService.getOperationStorage().numOperations()).isEqualTo(0);
assertThat(mBackupManagerService.getCurrentToken()).isEqualTo(1234L);
verify(mBackupManagerService).writeRestoreTokens();
verify(mOldJournal).delete();
@@ -2665,6 +2665,7 @@
KeyValueBackupTask task =
new KeyValueBackupTask(
mBackupManagerService,
+ mBackupManagerService.getOperationStorage(),
transportMock.mTransportConnection,
transportMock.transportData.transportDirName,
queue,
diff --git a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 9eb99ae..e0812d6 100644
--- a/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -51,6 +51,7 @@
import com.android.server.EventLogTags;
import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.BackupHandler;
@@ -98,6 +99,7 @@
@Mock private IRestoreObserver mObserver;
@Mock private IBackupManagerMonitor mMonitor;
@Mock private BackupEligibilityRules mBackupEligibilityRules;
+ @Mock private OperationStorage mOperationStorage;
private ShadowLooper mShadowBackupLooper;
private ShadowApplication mShadowApplication;
private UserBackupManagerService.BackupWakeLock mWakeLock;
@@ -132,7 +134,9 @@
// We need to mock BMS timeout parameters before initializing the BackupHandler since
// the constructor of BackupHandler relies on it.
when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
- BackupHandler backupHandler = new BackupHandler(mBackupManagerService, handlerThread);
+
+ BackupHandler backupHandler =
+ new BackupHandler(mBackupManagerService, mOperationStorage, handlerThread);
mWakeLock = createBackupWakeLock(application);
diff --git a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 77b5b61..fc3ec7b 100644
--- a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -120,7 +120,6 @@
when(backupManagerService.getTransportManager()).thenReturn(transportManager);
when(backupManagerService.getPackageManager()).thenReturn(packageManager);
when(backupManagerService.getBackupHandler()).thenReturn(backupHandler);
- when(backupManagerService.getCurrentOpLock()).thenReturn(new Object());
when(backupManagerService.getQueueLock()).thenReturn(new Object());
when(backupManagerService.getActivityManager()).thenReturn(mock(IActivityManager.class));
when(backupManagerService.getWakelock()).thenReturn(wakeLock);
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
index 06b7fb7..6a7d031 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowKeyValueBackupTask.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import com.android.server.backup.DataChangedJournal;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.keyvalue.KeyValueBackupReporter;
@@ -56,6 +57,7 @@
@Implementation
protected void __constructor__(
UserBackupManagerService backupManagerService,
+ OperationStorage operationStorage,
TransportConnection transportConnection,
String transportDirName,
List<String> queue,
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
index 71010a9..d985e1b 100644
--- a/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
+++ b/services/robotests/src/com/android/server/testing/shadows/ShadowPerformUnifiedRestoreTask.java
@@ -21,6 +21,7 @@
import android.app.backup.IRestoreObserver;
import android.content.pm.PackageInfo;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.UserBackupManagerService;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.restore.PerformUnifiedRestoreTask;
@@ -57,6 +58,7 @@
@Implementation
protected void __constructor__(
UserBackupManagerService backupManagerService,
+ OperationStorage operationStorage,
TransportConnection transportConnection,
IRestoreObserver observer,
IBackupManagerMonitor monitor,
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
index d1d7cc6..d9dbf48 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/verify/domain/DomainVerificationEnforcerTest.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
+import android.content.pm.SigningDetails
import android.content.pm.parsing.component.ParsedActivityImpl
import android.content.pm.parsing.component.ParsedIntentInfoImpl
import android.content.pm.verify.domain.DomainVerificationManager
@@ -26,6 +27,7 @@
import android.os.Build
import android.os.Process
import android.util.ArraySet
+import android.util.IndentingPrintWriter
import android.util.SparseArray
import androidx.test.platform.app.InstrumentationRegistry
import com.android.server.pm.parsing.pkg.AndroidPackage
@@ -46,6 +48,7 @@
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.anyString
import org.mockito.Mockito.eq
+import org.mockito.Mockito.mock
import org.mockito.Mockito.verifyNoMoreInteractions
import java.util.UUID
import java.util.concurrent.atomic.AtomicBoolean
@@ -204,6 +207,14 @@
service(Type.QUERENT, "getInfo") {
getDomainVerificationInfo(it.targetPackageName)
},
+ service(Type.QUERENT, "printState") {
+ printState(mock(IndentingPrintWriter::class.java), null, null)
+ },
+ service(Type.QUERENT, "printStateInternal") {
+ printState(mock(IndentingPrintWriter::class.java), null, null) {
+ mockPkgState(it, UUID.randomUUID())
+ }
+ },
service(Type.VERIFIER, "setStatus") {
setDomainVerificationStatus(
it.targetDomainSetId,
@@ -311,6 +322,7 @@
}
)
}
+ whenever(signingDetails) { SigningDetails.UNKNOWN }
}
fun mockPkgState(packageName: String, domainSetId: UUID) =
@@ -327,6 +339,7 @@
}
}
whenever(isSystem) { false }
+ whenever(signingDetails) { SigningDetails.UNKNOWN }
}
}
@@ -794,8 +807,12 @@
}
val valueAsInt = value as? Int
- if (valueAsInt != null && valueAsInt == DomainVerificationManager.STATUS_OK) {
- throw AssertionError("Expected call to return false, was $value")
+ if (valueAsInt != null) {
+ if (valueAsInt == DomainVerificationManager.STATUS_OK) {
+ throw AssertionError("Expected call to return false, was $value")
+ }
+ } else {
+ throw AssertionError("Expected call to fail")
}
} catch (e: SecurityException) {
} catch (e: PackageManager.NameNotFoundException) {
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 f92b872..28c1c81 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -585,8 +585,8 @@
doAnswer((invocation) -> {
((Region) invocation.getArguments()[1]).set(region);
return null;
- }).when(mMockMagnificationProcessor).getMagnificationRegion(eq(displayId),
- any(), anyBoolean());
+ }).when(mMockMagnificationProcessor).getFullscreenMagnificationRegion(eq(displayId), any(),
+ anyBoolean());
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final Region result = mServiceConnection.getMagnificationRegion(displayId);
@@ -620,7 +620,8 @@
@Test
public void resetMagnification() {
final int displayId = 1;
- when(mMockMagnificationProcessor.reset(displayId, true)).thenReturn(true);
+ when(mMockMagnificationProcessor.resetFullscreenMagnification(displayId, true)).thenReturn(
+ true);
final boolean result = mServiceConnection.resetMagnification(displayId, true);
assertThat(result, is(true));
@@ -629,7 +630,8 @@
@Test
public void resetMagnification_cantControlMagnification_returnFalse() {
final int displayId = 1;
- when(mMockMagnificationProcessor.reset(displayId, true)).thenReturn(true);
+ when(mMockMagnificationProcessor.resetFullscreenMagnification(displayId, true)).thenReturn(
+ true);
when(mMockSecurityPolicy.canControlMagnification(mServiceConnection)).thenReturn(false);
final boolean result = mServiceConnection.resetMagnification(displayId, true);
@@ -639,7 +641,8 @@
@Test
public void resetMagnification_serviceNotBelongCurrentUser_returnFalse() {
final int displayId = 1;
- when(mMockMagnificationProcessor.reset(displayId, true)).thenReturn(true);
+ when(mMockMagnificationProcessor.resetFullscreenMagnification(displayId, true)).thenReturn(
+ true);
when(mMockSystemSupport.getCurrentUserIdLocked()).thenReturn(USER_ID2);
final boolean result = mServiceConnection.resetMagnification(displayId, true);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
index 9ebec98..621507e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
@@ -98,7 +98,7 @@
.setScale(TEST_SCALE).build();
setMagnificationActivated(TEST_DISPLAY, config);
- float scale = mMagnificationProcessor.getScale(TEST_DISPLAY);
+ float scale = mMagnificationProcessor.getMagnificationConfig(TEST_DISPLAY).getScale();
assertEquals(scale, TEST_SCALE, 0);
}
@@ -111,20 +111,19 @@
setMagnificationActivated(TEST_DISPLAY, config);
float centerX = mMagnificationProcessor.getCenterX(
- TEST_DISPLAY, /* canControlMagnification= */true);
+ TEST_DISPLAY, /* canControlMagnification= */true);
assertEquals(centerX, TEST_CENTER_X, 0);
}
@Test
- public void getCenterX_canControlWindowMagnification_returnCenterX() {
+ public void getCenterX_controlWindowMagnification_returnCenterX() {
final MagnificationConfig config = new MagnificationConfig.Builder()
.setMode(MAGNIFICATION_MODE_WINDOW)
.setCenterX(TEST_CENTER_X).build();
setMagnificationActivated(TEST_DISPLAY, config);
- float centerX = mMagnificationProcessor.getCenterX(
- TEST_DISPLAY, /* canControlMagnification= */true);
+ float centerX = mMagnificationProcessor.getMagnificationConfig(TEST_DISPLAY).getCenterX();
assertEquals(centerX, TEST_CENTER_X, 0);
}
@@ -143,14 +142,13 @@
}
@Test
- public void getCenterY_canControlWindowMagnification_returnCenterY() {
+ public void getCenterY_controlWindowMagnification_returnCenterY() {
final MagnificationConfig config = new MagnificationConfig.Builder()
.setMode(MAGNIFICATION_MODE_WINDOW)
.setCenterY(TEST_CENTER_Y).build();
setMagnificationActivated(TEST_DISPLAY, config);
- float centerY = mMagnificationProcessor.getCenterY(
- TEST_DISPLAY, /* canControlMagnification= */false);
+ float centerY = mMagnificationProcessor.getMagnificationConfig(TEST_DISPLAY).getCenterY();
assertEquals(centerY, TEST_CENTER_Y, 0);
}
@@ -159,7 +157,7 @@
public void getMagnificationRegion_canControlFullscreenMagnification_returnRegion() {
final Region region = new Region(10, 20, 100, 200);
setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
- mMagnificationProcessor.getMagnificationRegion(TEST_DISPLAY,
+ mMagnificationProcessor.getFullscreenMagnificationRegion(TEST_DISPLAY,
region, /* canControlMagnification= */true);
verify(mMockFullScreenMagnificationController).getMagnificationRegion(eq(TEST_DISPLAY),
@@ -167,17 +165,6 @@
}
@Test
- public void getMagnificationRegion_canControlWindowMagnification_returnRegion() {
- final Region region = new Region(10, 20, 100, 200);
- setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_WINDOW);
- mMagnificationProcessor.getMagnificationRegion(TEST_DISPLAY,
- region, /* canControlMagnification= */true);
-
- verify(mMockWindowMagnificationManager).getMagnificationSourceBounds(eq(TEST_DISPLAY),
- eq(region));
- }
-
- @Test
public void getMagnificationRegion_fullscreenModeNotRegistered_shouldRegisterThenUnregister() {
final Region region = new Region(10, 20, 100, 200);
setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
@@ -188,7 +175,7 @@
any());
final Region result = new Region();
- mMagnificationProcessor.getMagnificationRegion(TEST_DISPLAY,
+ mMagnificationProcessor.getFullscreenMagnificationRegion(TEST_DISPLAY,
result, /* canControlMagnification= */true);
assertEquals(region, result);
verify(mMockFullScreenMagnificationController).register(TEST_DISPLAY);
@@ -237,7 +224,7 @@
public void reset_fullscreenMagnificationActivated() {
setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
- mMagnificationProcessor.reset(TEST_DISPLAY, /* animate= */false);
+ mMagnificationProcessor.resetFullscreenMagnification(TEST_DISPLAY, /* animate= */false);
verify(mMockFullScreenMagnificationController).reset(TEST_DISPLAY, false);
}
@@ -246,7 +233,7 @@
public void reset_windowMagnificationActivated() {
setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_WINDOW);
- mMagnificationProcessor.reset(TEST_DISPLAY, /* animate= */false);
+ mMagnificationProcessor.resetCurrentMagnification(TEST_DISPLAY, /* animate= */false);
verify(mMockWindowMagnificationManager).reset(TEST_DISPLAY);
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
index c36e1a8..bc95341 100644
--- a/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -35,6 +35,7 @@
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.backup.internal.LifecycleOperationStorage;
import com.android.server.backup.internal.OnTaskFinishedListener;
import com.android.server.backup.params.BackupParams;
import com.android.server.backup.transport.BackupTransportClient;
@@ -60,7 +61,7 @@
@Mock TransportConnection mTransportConnection;
@Mock BackupTransportClient mBackupTransport;
@Mock BackupEligibilityRules mBackupEligibilityRules;
-
+ @Mock LifecycleOperationStorage mOperationStorage;
private TestBackupService mService;
@@ -68,7 +69,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mService = new TestBackupService(mContext, mPackageManager);
+ mService = new TestBackupService(mContext, mPackageManager, mOperationStorage);
mService.setEnabled(true);
mService.setSetupComplete(true);
}
@@ -173,8 +174,9 @@
boolean isEnabledStatePersisted = false;
boolean shouldUseNewBackupEligibilityRules = false;
- TestBackupService(Context context, PackageManager packageManager) {
- super(context, packageManager);
+ TestBackupService(Context context, PackageManager packageManager,
+ LifecycleOperationStorage operationStorage) {
+ super(context, packageManager, operationStorage);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java b/services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java
index fa35e3f..3c79d8b 100644
--- a/services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/internal/BackupHandlerTest.java
@@ -18,7 +18,6 @@
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertFalse;
-import static org.testng.Assert.assertThrows;
import static org.testng.Assert.assertTrue;
import android.os.HandlerThread;
@@ -28,6 +27,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.OperationStorage;
import com.android.server.backup.UserBackupManagerService;
import org.junit.After;
@@ -46,6 +46,7 @@
private static final int MESSAGE_TIMEOUT_MINUTES = 1;
@Mock private UserBackupManagerService mUserBackupManagerService;
+ @Mock private OperationStorage mOperationStorage;
@Mock private BackupAgentTimeoutParameters mTimeoutParameters;
private HandlerThread mHandlerThread;
@@ -114,7 +115,7 @@
private final boolean mShouldStop;
TestBackupHandler(boolean shouldStop) {
- super(mUserBackupManagerService, mHandlerThread);
+ super(mUserBackupManagerService, mOperationStorage, mHandlerThread);
mShouldStop = shouldStop;
}
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index 1d7a476..4469ffc 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -17,6 +17,7 @@
package android.telephony;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.hardware.radio.V1_5.AccessNetwork;
@@ -28,6 +29,8 @@
*/
public final class AccessNetworkConstants {
+ private static final String TAG = AccessNetworkConstants.class.getSimpleName();
+
/**
* Wireless transportation type
*
@@ -108,6 +111,21 @@
default: return Integer.toString(type);
}
}
+
+ /** @hide */
+ public static @RadioAccessNetworkType int fromString(@NonNull String str) {
+ switch (str.toUpperCase()) {
+ case "GERAN" : return GERAN;
+ case "UTRAN" : return UTRAN;
+ case "EUTRAN" : return EUTRAN;
+ case "CDMA2000" : return CDMA2000;
+ case "IWLAN" : return IWLAN;
+ case "NGRAN" : return NGRAN;
+ default:
+ Rlog.e(TAG, "Invalid access network type " + str);
+ return UNKNOWN;
+ }
+ }
}
/**
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 7d24b76..c80d35b 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -5381,6 +5381,34 @@
public static final String KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL =
"unthrottle_data_retry_when_tac_changes_bool";
+ /**
+ * IWLAN handover rules that determine whether handover is allowed or disallowed between
+ * cellular and IWLAN.
+ *
+ * The handover rules will be matched in the order. Here are some sample rules.
+ * <string-array name="iwlan_handover_rules" num="5">
+ * <!-- Handover from IWLAN to 2G/3G is not allowed -->
+ * <item value="source=IWLAN, target=GERAN|UTRAN, type=disallowed"/>
+ * <!-- Handover from 2G/3G to IWLAN is not allowed -->
+ * <item value="source=GERAN|UTRAN, target:IWLAN, type=disallowed"/>
+ * <!-- Handover from IWLAN to 3G/4G/5G is not allowed if the device is roaming. -->
+ * <item value="source=IWLAN, target=UTRAN|EUTRAN|NGRAN, roaming=true, type=disallowed"/>
+ * <!-- Handover from 4G to IWLAN is not allowed -->
+ * <item value="source=EUTRAN, target=IWLAN, type=disallowed"/>
+ * <!-- Handover is always allowed in any condition. -->
+ * <item value="source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN,
+ * target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"/>
+ * </string-array>
+ *
+ * When handover is not allowed, frameworks will tear down the data network on source transport,
+ * and then setup a new one on the target transport when Qualified Network Service changes the
+ * preferred access networks for particular APN types.
+ *
+ * @hide
+ */
+ public static final String KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY =
+ "iwlan_handover_policy_string_array";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -5953,6 +5981,7 @@
sDefaults.putInt(
KEY_OPPORTUNISTIC_TIME_TO_SCAN_AFTER_CAPABILITY_SWITCH_TO_PRIMARY_LONG,
120000);
+ sDefaults.putAll(ImsServiceEntitlement.getDefaults());
sDefaults.putAll(Gps.getDefaults());
sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
new int[] {
@@ -6043,6 +6072,9 @@
sDefaults.putBoolean(KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false);
sDefaults.putBoolean(KEY_VONR_SETTING_VISIBILITY_BOOL, true);
sDefaults.putBoolean(KEY_VONR_ENABLED_BOOL, false);
+ sDefaults.putStringArray(KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY, new String[]{
+ "source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, "
+ + "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"});
}
/**
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 866fd2c..ba95841 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -530,6 +530,8 @@
int RIL_REQUEST_GET_SLICING_CONFIG = 224;
int RIL_REQUEST_ENABLE_VONR = 225;
int RIL_REQUEST_IS_VONR_ENABLED = 226;
+ int RIL_REQUEST_SET_USAGE_SETTING = 227;
+ int RIL_REQUEST_GET_USAGE_SETTING = 228;
/* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;