diff options
187 files changed, 4575 insertions, 1488 deletions
diff --git a/Android.bp b/Android.bp index 54983d615913..3b8ef617630b 100644 --- a/Android.bp +++ b/Android.bp @@ -156,7 +156,6 @@ java_library { "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 @@ java_library { "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 92c63c9ab841..3b11036495c8 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -250,7 +250,6 @@ modules_public_stubs = [ "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 @@ modules_system_stubs = [ "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 005c447c7220..a6a007f46b58 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.IndentingPrintWriter; 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 @@ class Agent { 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 @@ class Agent { 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 20a300abe72a..36895a5c3a25 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 @@ public class InternalResourceService extends SystemService { 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 a234ae6142fc..f4917ad82761 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 @@ class Ledger { 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 154c42f1565f..ee44198dcc26 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -17888,6 +17888,7 @@ package android.hardware { 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 @@ package android.view { 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 @@ package android.view { 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 c9b8ba2f7fe2..a33d0a2c0879 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -70,6 +70,7 @@ package android { 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 @@ package android.media.audiopolicy { 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 @@ package android.media.tv.tuner.dvr { 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 @@ package android.media.tv.tuner.filter { 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 @@ package android.media.tv.tuner.filter { 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 @@ package android.media.tv.tuner.filter { 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 @@ package android.media.tv.tuner.filter { } 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 @@ package android.media.tv.tuner.frontend { 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 @@ package android.media.tv.tuner.frontend { 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 9cb9ddcc6280..2c5acf182d51 100644 --- a/core/api/system-removed.txt +++ b/core/api/system-removed.txt @@ -60,14 +60,6 @@ package android.app.search { } -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 a642696b25ec..3573a5609bc6 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -1394,6 +1394,12 @@ public abstract class AccessibilityService extends Service { * {@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 @@ public abstract class AccessibilityService extends Service { * {@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 @@ public abstract class AccessibilityService extends Service { * {@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 @@ public abstract class AccessibilityService extends Service { * {@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 @@ public abstract class AccessibilityService extends Service { * {@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 af907af2ce0d..779552f1bbed 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -5808,6 +5808,7 @@ public class Notification implements Parcelable 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 c0463243f41d..8212eaa4e47a 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -601,49 +601,6 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * 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 24c6a5a12178..bfd9fd0a4ef9 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -792,6 +792,21 @@ public final class AssetManager implements AutoCloseable { } /** + * 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 @@ public final class AssetManager implements AutoCloseable { 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 b5d4f09144ee..cb53a2a7a7b8 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1508,6 +1508,12 @@ public class Resources { * 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 @@ public class Resources { } } + @StyleRes + /*package*/ int getParentThemeIdentifier(@StyleRes int resId) { + synchronized (mLock) { + return mThemeImpl.getParentThemeIdentifier(resId); + } + } + /** * @hide */ @@ -1948,8 +1961,27 @@ public class Resources { 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 b9f93b85f0bf..4d850b0ccfd5 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -1310,6 +1310,14 @@ public class ResourcesImpl { 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 cad30dda9034..a4a8f313e3ba 100644 --- a/core/java/android/hardware/HardwareBuffer.java +++ b/core/java/android/hardware/HardwareBuffer.java @@ -25,6 +25,7 @@ import android.graphics.GraphicBuffer; 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 final class HardwareBuffer implements Parcelable, AutoCloseable { 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 8292f26a8c6b..aa4b83a5c361 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -192,14 +192,15 @@ public class GraphicsEnvironment { // 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 83a6bc0ec99d..73ffd66486d2 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -44,7 +44,6 @@ import android.content.res.TypedArray; 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 @@ public abstract class WallpaperService extends Service { 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 6984e4dfccc4..4789231b0404 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -874,6 +874,18 @@ public class StaticLayout extends Layout { ? 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 7e2792c9664f..8124510718ae 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -58,6 +58,10 @@ public class FeatureFlagUtils { */ 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 @@ public class FeatureFlagUtils { 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 @@ public class FeatureFlagUtils { 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 af7d86cdd1d9..3b5270960c99 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -120,6 +120,8 @@ public final class SurfaceControl implements Parcelable { 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 @@ public final class SurfaceControl implements Parcelable { 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 @@ public final class SurfaceControl implements Parcelable { * 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 @@ public final class SurfaceControl implements Parcelable { } /** - * @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 @@ public final class SurfaceControl implements Parcelable { * @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 @@ public final class SurfaceControl implements Parcelable { } /** + * 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 @@ public final class SurfaceControl implements Parcelable { } /** - * 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 @@ public final class SurfaceControl implements Parcelable { * 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 3b15db2ded70..b85fe7c3aae2 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -836,20 +836,25 @@ public interface InputConnection { boolean beginBatchEdit(); /** - * 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>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> + * 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>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 410e68bf3581..29bb3111d83e 100644 --- a/core/java/com/android/internal/inputmethod/EditableInputConnection.java +++ b/core/java/com/android/internal/inputmethod/EditableInputConnection.java @@ -89,7 +89,7 @@ public final class EditableInputConnection extends BaseInputConnection // 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 86d781033e5e..8c23b214b8fe 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -881,6 +881,12 @@ static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jin 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 @@ static const JNINativeMethod gAssetManagerMethods[] = { {"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 fb5fded923f2..67d0c52960e0 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -597,6 +597,14 @@ static void nativeSetPosition(JNIEnv* env, jclass clazz, jlong transactionObj, 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 @@ static void nativeSetBuffer(JNIEnv* env, jclass clazz, jlong transactionObj, jlo 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 nativeSetTransparentRegionHint(JNIEnv* env, jclass clazz, jlong tran } } +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 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (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 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (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 4c367326b670..08948773f599 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 d92e2ccc77bd..686fbbfd6f7c 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 @@ import java.util.EnumSet; * 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 @@ public class BadgedImageView extends ImageView { 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 @@ public class BadgedImageView extends ImageView { 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 class BadgedImageView extends ImageView { 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 class BadgedImageView extends ImageView { public void showDotAndBadge(boolean onLeft) { removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.BEHIND_STACK); animateDotBadgePositions(onLeft); - } public void hideDotAndBadge(boolean onLeft) { @@ -149,6 +160,7 @@ public class BadgedImageView extends ImageView { */ public void setRenderedBubble(BubbleViewProvider bubble) { mBubble = bubble; + mBubbleIcon.setImageBitmap(bubble.getBubbleIcon()); if (mDotSuppressionFlags.contains(SuppressionFlag.BEHIND_STACK)) { hideBadge(); } else { @@ -176,6 +188,20 @@ public class BadgedImageView extends ImageView { 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 @@ public class BadgedImageView extends ImageView { 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 @@ public class BadgedImageView extends ImageView { 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 519a856538c7..cd635c10fd8e 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 @@ public class BubbleData { 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 5161092fb96c..a175929cf498 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 @@ class BubbleOverflow( 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 @@ class BubbleOverflow( 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 eea6e3cb35db..c4bd73ba1b4a 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 @@ public class ScreenshotUtils { 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 @@ public class ScreenshotUtils { .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 040fffae3e84..b8ac87fa5d4c 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 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange 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 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange 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 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange break; default: flingDividePosition(currentPosition, snapTarget.position, - () -> setDividePosition(snapTarget.position)); + () -> setDividePosition(snapTarget.position, true /* applyLayoutChange */)); break; } } @@ -389,7 +391,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange @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 ed5de0d698f3..cdaa54c7266d 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 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, 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 6643ca176280..4ecc0b685626 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 @@ public class TaskSnapshotWindow { } 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 @@ public class TaskSnapshotWindow { // 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 bc701d0c70bc..8bc1223cfd64 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.NotificationListenerService; 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 @@ public class BubbleDataTest extends ShellTestCase { 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 @@ public class BubbleDataTest extends ShellTestCase { } 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 453050fcfab4..83d5f04b7cdb 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 com.google.common.truth.Truth.assertThat; 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 @@ public class SplitLayoutTests extends ShellTestCase { @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 22904a0528d5..136fc6ca4e2a 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -902,6 +902,27 @@ std::string AssetManager2::GetLastResourceResolution() const { 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 9ebc9969a730..8abe79d01642 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 a3b42df12da9..1bde792da2ba 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -192,6 +192,12 @@ class AssetManager2 { 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 147e73599cad..7f6fb90297b3 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -460,4 +460,8 @@ interface IAudioService { 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 0f08d79bf3bb..3ba1d1f0eca2 100644 --- a/media/java/android/media/audiopolicy/AudioPolicy.java +++ b/media/java/android/media/audiopolicy/AudioPolicy.java @@ -19,6 +19,7 @@ package android.media.audiopolicy; 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 @@ public class AudioPolicy { * 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 @@ public class AudioPolicy { } /** + * 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 cd87a09b91e0..9859a5fbdc4b 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.ITvIAppClient; 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 @@ import android.view.Surface; 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 af15dd8f5a3f..72db3fa8f3fe 100644 --- a/media/java/android/media/tv/interactive/ITvIAppService.aidl +++ b/media/java/android/media/tv/interactive/ITvIAppService.aidl @@ -18,6 +18,7 @@ package android.media.tv.interactive; import android.media.tv.interactive.ITvIAppServiceCallback; import android.media.tv.interactive.ITvIAppSessionCallback; +import android.os.Bundle; import android.view.InputChannel; /** @@ -31,4 +32,5 @@ oneway interface ITvIAppService { 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 2272084a4017..87660559cff5 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.BroadcastInfoRequest; 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 final class TvIAppManager { */ 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 @@ public final class TvIAppManager { } /** + * 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 f93b59784c7e..6bf80284aaf5 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.BroadcastInfoRequest; 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 abstract class TvIAppService extends Service { public void prepare(int type) { onPrepare(type); } + + @Override + public void notifyAppLinkInfo(Bundle appLinkInfo) { + onAppLinkInfo(appLinkInfo); + } }; return tvIAppServiceBinder; } @@ -135,6 +141,14 @@ public abstract class TvIAppService extends Service { // 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 94de7fa17ae7..255b391b2259 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -285,7 +285,7 @@ public class Tuner implements AutoCloseable { @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 @@ public class Tuner implements AutoCloseable { 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 @@ public class Tuner implements AutoCloseable { 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 @@ public class Tuner implements AutoCloseable { } } - 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 @@ public class Tuner implements AutoCloseable { } 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 @@ public class Tuner implements AutoCloseable { */ 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 @@ public class Tuner implements AutoCloseable { } 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 cfd85834048c..6f3ab032a75c 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 @@ public class DvrPlayback implements AutoCloseable { 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 @@ public class DvrPlayback implements AutoCloseable { * * @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 @@ public class DvrPlayback implements AutoCloseable { } /** - * 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 212a71343a49..e72026aab992 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 @@ public class DvrRecorder implements AutoCloseable { * * @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 @@ public class DvrRecorder implements AutoCloseable { /** * 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 394211be646b..25989db2fb97 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 @@ import android.annotation.SystemApi; @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 @@ public class DownloadEvent extends FilterEvent { } /** + * 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 @@ public class DownloadEvent extends FilterEvent { 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 7ba923e23c70..e2cfd7c173a2 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 @@ package android.media.tv.tuner.filter; 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 @@ import android.media.tv.tuner.TunerUtils; */ @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 @@ public class DownloadSettings extends Settings { } /** + * 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 class DownloadSettings extends Settings { */ public static class Builder { private final int mMainType; + private boolean mUseDownloadId = false; private int mDownloadId; private Builder(int mainType) { @@ -63,6 +76,24 @@ public class DownloadSettings extends Settings { } /** + * 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 @@ public class DownloadSettings extends Settings { */ @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 dbd85e9997d8..79d4062cfbbf 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 @@ public class MediaEvent extends FilterEvent { 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 @@ public class MediaEvent extends FilterEvent { 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 @@ public class MediaEvent extends FilterEvent { } /** + * 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 ff1249296efd..182bb9463bd6 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 @@ public class SectionEvent extends FilterEvent { 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 @@ public class SectionEvent extends FilterEvent { /** * 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 31f1a63e12ba..582e4f54e2c3 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 @@ package android.media.tv.tuner.frontend; 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 @@ public class FrontendStatus { 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 class FrontendStatus { 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 @@ public class FrontendStatus { 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 @@ public class FrontendStatus { } /** + * 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 ded9652622a7..e91e2381bd42 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -588,13 +588,13 @@ void FilterClientCallbackImpl::getSectionEvent(jobjectArray &arr, const int size 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 @@ void FilterClientCallbackImpl::getMediaEvent(jobjectArray &arr, const int size, 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 @@ void FilterClientCallbackImpl::getMediaEvent(jobjectArray &arr, const int size, 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 @@ void FilterClientCallbackImpl::getDownloadEvent(jobjectArray &arr, const int siz 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 @@ FilterClientCallbackImpl::~FilterClientCallbackImpl() { } /////////////// 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 @@ void FrontendClientCallbackImpl::onScanMessage( 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 @@ void FrontendClientCallbackImpl::onScanMessage( } 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 @@ JTuner::JTuner(JNIEnv *env, jobject thiz) : mClass(nullptr) { mSharedFeId = (int)Constant::INVALID_FRONTEND_ID; } +jweak JTuner::getObject() { + return mObject; +} + JTuner::~JTuner() { if (mFeClient != nullptr) { mFeClient->close(); @@ -1192,6 +1239,7 @@ JTuner::~JTuner() { env->DeleteWeakGlobalRef(mObject); env->DeleteGlobalRef(mClass); mFeClient = nullptr; + mFeClientCb = nullptr; mDemuxClient = nullptr; mClass = nullptr; mObject = nullptr; @@ -1247,9 +1295,8 @@ jobject JTuner::openFrontendByHandle(int feHandle) { 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 @@ int JTuner::shareFrontend(int feId) { 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 @@ jobject JTuner::getFrontendStatus(jintArray types) { 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 @@ static int android_media_tv_Tuner_share_frontend( 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 DemuxFilterRecordSettings getFilterRecordSettings(JNIEnv *env, const jobj 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 @@ static jlong android_media_tv_Tuner_read_dvr(JNIEnv *env, jobject dvr, jlong siz 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 @@ static const JNINativeMethod gTunerMethods[] = { (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 @@ static const JNINativeMethod gTunerMethods[] = { { "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 gDvrRecorderMethods[] = { 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 31d24ee13cf7..06e249212444 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -149,14 +149,21 @@ private: 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 @@ struct JTuner : public RefBase { 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 @@ struct JTuner : public RefBase { jint closeFrontend(); jint closeDemux(); + jweak getObject(); + protected: virtual ~JTuner(); @@ -200,6 +211,7 @@ private: 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 30278382c8ec..05683b661716 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 @@ int64_t DvrClient::writeToBuffer(int8_t* buffer, int64_t size) { 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 9080c7288f76..61c0325813a4 100644 --- a/media/jni/tuner/DvrClient.h +++ b/media/jni/tuner/DvrClient.h @@ -82,6 +82,11 @@ public: 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 ceba4d60bed0..f76811ee1062 100644 --- a/native/android/surface_control.cpp +++ b/native/android/surface_control.cpp @@ -364,7 +364,7 @@ void ASurfaceTransaction_setBuffer(ASurfaceTransaction* aSurfaceTransaction, 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 4862377852ac..e267d19ad7e2 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 3e82b28ce67e..931a55b27ddb 100644 --- a/packages/ConnectivityT/framework-t/Android.bp +++ b/packages/ConnectivityT/framework-t/Android.bp @@ -100,6 +100,20 @@ filegroup { ], } +// 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 { 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 index 933256a3b475..933256a3b475 100644 --- a/core/java/android/net/IIpSecService.aidl +++ b/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 index 86052484eaf6..86052484eaf6 100644 --- a/core/java/android/net/IpSecAlgorithm.java +++ b/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 index eaefca74d3a0..eaefca74d3a0 100644 --- a/core/java/android/net/IpSecConfig.aidl +++ b/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 index 575c5ed968f8..575c5ed968f8 100644 --- a/core/java/android/net/IpSecConfig.java +++ b/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 index c10680761ff1..c10680761ff1 100644 --- a/core/java/android/net/IpSecManager.java +++ b/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 index 6484a0013c53..6484a0013c53 100644 --- a/core/java/android/net/IpSecSpiResponse.aidl +++ b/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 index f99e570fb761..f99e570fb761 100644 --- a/core/java/android/net/IpSecSpiResponse.java +++ b/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 index b48c1fdaf1b2..b48c1fdaf1b2 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/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 index 546230d5b888..546230d5b888 100644 --- a/core/java/android/net/IpSecTransformResponse.aidl +++ b/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 index a38488954fc0..a38488954fc0 100644 --- a/core/java/android/net/IpSecTransformResponse.java +++ b/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 index 7239221415ce..7239221415ce 100644 --- a/core/java/android/net/IpSecTunnelInterfaceResponse.aidl +++ b/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 index e3411e003d6b..e3411e003d6b 100644 --- a/core/java/android/net/IpSecTunnelInterfaceResponse.java +++ b/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 index 5e451f3651f1..5e451f3651f1 100644 --- a/core/java/android/net/IpSecUdpEncapResponse.aidl +++ b/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 index 4e7ba9b515d0..4e7ba9b515d0 100644 --- a/core/java/android/net/IpSecUdpEncapResponse.java +++ b/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 6c597e26e042..0f21e55b9f27 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.HandlerThread; 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 final class NsdManager { */ public static final int NSD_STATE_ENABLED = 2; - private static final int BASE = Protocol.BASE_NSD_MANAGER; - /** @hide */ - public static final int DISCOVER_SERVICES = BASE + 1; + public static final int DISCOVER_SERVICES = 1; /** @hide */ - public static final int DISCOVER_SERVICES_STARTED = BASE + 2; + public static final int DISCOVER_SERVICES_STARTED = 2; /** @hide */ - public static final int DISCOVER_SERVICES_FAILED = BASE + 3; + public static final int DISCOVER_SERVICES_FAILED = 3; /** @hide */ - public static final int SERVICE_FOUND = BASE + 4; + public static final int SERVICE_FOUND = 4; /** @hide */ - public static final int SERVICE_LOST = BASE + 5; + public static final int SERVICE_LOST = 5; /** @hide */ - public static final int STOP_DISCOVERY = BASE + 6; + public static final int STOP_DISCOVERY = 6; /** @hide */ - public static final int STOP_DISCOVERY_FAILED = BASE + 7; + public static final int STOP_DISCOVERY_FAILED = 7; /** @hide */ - public static final int STOP_DISCOVERY_SUCCEEDED = BASE + 8; + public static final int STOP_DISCOVERY_SUCCEEDED = 8; /** @hide */ - public static final int REGISTER_SERVICE = BASE + 9; + public static final int REGISTER_SERVICE = 9; /** @hide */ - public static final int REGISTER_SERVICE_FAILED = BASE + 10; + public static final int REGISTER_SERVICE_FAILED = 10; /** @hide */ - public static final int REGISTER_SERVICE_SUCCEEDED = BASE + 11; + public static final int REGISTER_SERVICE_SUCCEEDED = 11; /** @hide */ - public static final int UNREGISTER_SERVICE = BASE + 12; + public static final int UNREGISTER_SERVICE = 12; /** @hide */ - public static final int UNREGISTER_SERVICE_FAILED = BASE + 13; + public static final int UNREGISTER_SERVICE_FAILED = 13; /** @hide */ - public static final int UNREGISTER_SERVICE_SUCCEEDED = BASE + 14; + public static final int UNREGISTER_SERVICE_SUCCEEDED = 14; /** @hide */ - public static final int RESOLVE_SERVICE = BASE + 18; + public static final int RESOLVE_SERVICE = 15; /** @hide */ - public static final int RESOLVE_SERVICE_FAILED = BASE + 19; + public static final int RESOLVE_SERVICE_FAILED = 16; /** @hide */ - public static final int RESOLVE_SERVICE_SUCCEEDED = BASE + 20; + public static final int RESOLVE_SERVICE_SUCCEEDED = 17; /** @hide */ - public static final int DAEMON_CLEANUP = BASE + 21; + public static final int DAEMON_CLEANUP = 18; /** @hide */ - public static final int DAEMON_STARTUP = BASE + 22; + public static final int DAEMON_STARTUP = 19; /** @hide */ - public static final int ENABLE = BASE + 24; + public static final int ENABLE = 20; /** @hide */ - public static final int DISABLE = BASE + 25; + public static final int DISABLE = 21; /** @hide */ - public static final int NATIVE_DAEMON_EVENT = BASE + 26; + public static final int NATIVE_DAEMON_EVENT = 22; /** @hide */ - public static final int REGISTER_CLIENT = BASE + 27; + public static final int REGISTER_CLIENT = 23; /** @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 @@ public final class NsdManager { 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 @@ public final class NsdManager { 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 final class NsdManager { */ 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 @@ public final class NsdManager { * 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 @@ public final class NsdManager { } 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 6a64910609ff..7b8817692b74 100644 --- a/packages/ConnectivityT/service/Android.bp +++ b/packages/ConnectivityT/service/Android.bp @@ -48,16 +48,28 @@ filegroup { ], } +// 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 index aeb814327e66..aeb814327e66 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/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 67a70bb39964..b3987f1aeeda 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 000000000000..b6d76e01ad95 --- /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 a2b8bf6d3d4c..4f0925f3bfbb 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 95330400145f..2819dc9c27b7 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 000000000000..a93abb71c9d7 --- /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 000000000000..b611ffa6d84e --- /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 f057603e2cd0..1d6f279afc66 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 c7350a1eee8b..d4398a8d35a2 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 ac463ebc1c97..157191302010 100644 --- a/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java +++ b/packages/SystemUI/src/com/android/keyguard/AnimatableClockController.java @@ -168,6 +168,12 @@ public class AnimatableClockController extends ViewController<AnimatableClockVie 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 2a0c2855c3b2..000000000000 --- 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 000000000000..357be2503a3a --- /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 9238b8226bbc..25dcdf9aa561 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -190,11 +190,15 @@ public class KeyguardClockSwitch extends RelativeLayout { } } - 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 @@ public class KeyguardClockSwitch extends RelativeLayout { 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 @@ public class KeyguardClockSwitch extends RelativeLayout { * * @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 @@ public class KeyguardClockSwitch extends RelativeLayout { // 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 @@ public class KeyguardClockSwitch extends RelativeLayout { 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 032da789518c..b7a5aae38515 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -275,11 +275,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } 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 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS * 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 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS 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 67ef02a6029b..b84cb19b9468 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -866,7 +866,6 @@ public class KeyguardSecurityContainer extends FrameLayout { ((LayerDrawable) mUserSwitcher.getBackground()).getDrawable(1).setAlpha(255); } - anchor.setClickable(true); anchor.setOnClickListener((v) -> { if (mFalsingManager.isFalseTap(LOW_PENALTY)) return; @@ -877,8 +876,7 @@ public class KeyguardSecurityContainer extends FrameLayout { 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 @@ public class KeyguardSecurityContainer extends FrameLayout { == 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 986d0debb56a..8bf890d7df50 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -123,8 +123,17 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV * 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 dfb466788ca7..7b6ce3e1c951 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherPopupMenu.java @@ -18,12 +18,10 @@ package com.android.keyguard; 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 @@ public class KeyguardUserSwitcherPopupMenu extends ListPopupWindow { // 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 cd57af4ae97b..6626f59aae8c 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -278,10 +278,6 @@ public class LockIconViewController extends ViewController<LockIconView> impleme 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 4aa46f1813dc..58cf35f2917c 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.Iterator; 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 @@ public class CommunalSourceMonitor { // 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 @@ public class CommunalSourceMonitor { // 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 @@ public class CommunalSourceMonitor { @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/dagger/CommunalModule.java b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java index f27ae344eb24..e1f1ac42884d 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.AmbientLightModeMonitor; 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 @@ public interface CommunalModule { 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 471a327ad987..b2fe3bb94dd3 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.plugins.statusbar.StatusBarStateController; 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 @@ import javax.inject.Inject; */ @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 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable, 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 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable, DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor, DozeLog dozeLog, TunerService tunerService, StatusBarStateController statusBarStateController, + Optional<SysUIUnfoldComponent> sysUiUnfoldComponent, ConfigurationController configurationController) { mContext = context; mWakeLock = wakeLock; @@ -118,12 +125,23 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable, 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 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable, && (mKeyguardShowing || mDozeParameters.shouldControlUnlockedScreenOff()) && !mHost.isPowerSaveActive(); mDozeParameters.setControlScreenOffAnimation(controlScreenOff); - mHost.setAnimateScreenOff(controlScreenOff); + mHost.setAnimateScreenOff(controlScreenOff + && mDozeParameters.shouldAnimateDozingChange()); } } @@ -299,4 +318,9 @@ public class DozeUi implements DozeMachine.Part, TunerService.Tunable, 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 000000000000..bc1f772e14bb --- /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 8f0ea2fb2f87..393f039d91ea 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.WindowInsets; 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 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ 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 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ } 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 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ * 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 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ (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 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ }); } - @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 000000000000..9847ef633bc1 --- /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 000000000000..5674b9f3f9fd --- /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 7bf2361e471c..ff5beb54bd89 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 @@ import dagger.Module; /** * 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 000000000000..a3a446a0dbca --- /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 000000000000..d0a8fad9b1e5 --- /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 c592431d360e..0c9e3157f4d1 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.StatusBar; 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.io.PrintWriter; 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, private boolean mInGestureNavigationMode; private boolean mWakeAndUnlocking; - private IKeyguardDrawnCallback mDrawnCallback; + private Runnable mWakeAndUnlockingDrawnCallback; private CharSequence mCustomMessage; /** @@ -817,7 +817,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, mDeviceInteractive = false; mGoingToSleep = false; mWakeAndUnlocking = false; - mAnimatingScreenOff = mDozeParameters.shouldControlUnlockedScreenOff(); + mAnimatingScreenOff = mDozeParameters.shouldAnimateDozingChange(); resetKeyguardDonePendingLocked(); mHideAnimationRun = false; @@ -2230,14 +2235,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, Trace.beginSection("KeyguardViewMediator#handleNotifyScreenTurnedOn"); synchronized (this) { if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOn"); + + mPendingDrawnTasks.reset(); mKeyguardViewControllerLazy.get().onScreenTurnedOn(); } Trace.endSection(); @@ -2614,18 +2617,18 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, 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 000000000000..bccd106db836 --- /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 5bd02cc207b9..10efec309971 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.NonNull; 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 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener 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 ac95bf50bb98..c2a9e3adb0b8 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 @@ public class QSFactoryImpl implements QSFactory { 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 @@ public class QSFactoryImpl implements QSFactory { 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 75cf4d1ace8e..939a29711f45 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 @@ class FgsManagerTile @Inject constructor( 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 4908d4fb214e..dd742b86a483 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.ArrayList; 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 @@ public class InternetDialogController implements AccessPointController.AccessPoi 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 @@ public class InternetDialogController implements AccessPointController.AccessPoi // 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 @@ public class InternetDialogController implements AccessPointController.AccessPoi 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 f8d6c6d8ec10..b2e15f48004c 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.plugins.statusbar.StatusBarStateController 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 @@ class BypassHeadsUpNotifier @Inject constructor( 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 @@ class BypassHeadsUpNotifier @Inject constructor( 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 @@ class BypassHeadsUpNotifier @Inject constructor( // 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 e1dbf4efa4de..518788b5a6dd 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 class NotificationStackScrollLayout extends ViewGroup implements Dumpable 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 e3a4bf0170fb..2dc92764a4af 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 class StackStateAnimator { 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 4b8b580eba0b..d87a02493a23 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 @@ public class DozeParameters implements 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 @@ public class DozeParameters implements 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 097c0d1677f9..3b7063e6662f 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 androidx.constraintlayout.widget.ConstraintSet.TOP; 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.KEYGUARD; 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 @@ public class NotificationPanelViewController extends PanelViewController { .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 @@ public class NotificationPanelViewController extends PanelViewController { 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 @@ public class NotificationPanelViewController extends PanelViewController { } } + /** + * 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 @@ public class NotificationPanelViewController extends PanelViewController { 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 f67d18183c10..1e71ceb8cae8 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.Dependency; 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 @@ public class PhoneStatusBarView extends FrameLayout { @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 497e7d78ac4e..e806ca0d9005 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 android.view.View 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 @@ class ScreenOffAnimationController @Inject constructor( } /** + * 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 @@ class ScreenOffAnimationController @Inject constructor( 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 @@ class ScreenOffAnimationController @Inject constructor( */ 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 @@ interface ScreenOffAnimation { 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 f6e19bff8f9b..8cf7288c9cd5 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 @@ class SplitShadeHeaderController @Inject constructor( } 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 @@ class SplitShadeHeaderController @Inject constructor( 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 @@ class SplitShadeHeaderController @Inject constructor( 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 6c0b717fb85c..331299628f61 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 @@ public class StatusBar extends CoreStartable implements 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 @@ public class StatusBar extends CoreStartable implements // 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 @@ public class StatusBar extends CoreStartable implements 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 @@ public class StatusBar extends CoreStartable implements Trace.endSection(); } - protected PhoneStatusBarView getStatusBarView() { - return mStatusBarView; - } - public NotificationShadeWindowView getNotificationShadeWindowView() { return mNotificationShadeWindowView; } @@ -2915,7 +2908,17 @@ public class StatusBar extends CoreStartable implements 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 @@ public class StatusBar extends CoreStartable implements 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 b84e6e6f37cc..a4aeae9c0307 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 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb 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 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb KeyguardStateController keyguardStateController, NotificationMediaManager notificationMediaManager, KeyguardBouncer.Factory keyguardBouncerFactory, - WakefulnessLifecycle wakefulnessLifecycle, - UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, KeyguardMessageAreaController.Factory keyguardMessageAreaFactory, Lazy<ShadeController> shadeController) { mContext = context; @@ -260,8 +256,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mStatusBarStateController = sysuiStatusBarStateController; mDockManager = dockManager; mKeyguardBouncerFactory = keyguardBouncerFactory; - mWakefulnessLifecycle = wakefulnessLifecycle; - mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; mKeyguardMessageAreaFactory = keyguardMessageAreaFactory; mShadeController = shadeController; } @@ -1155,7 +1149,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @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 000000000000..fb9df01a0251 --- /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 b53ab210424f..ccde3162b177 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 @@ interface SysUIUnfoldComponent { 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 a7e9cdbf1a18..8b6e982be55b 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 com.android.systemui.statusbar.policy.CallbackController; 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 @@ import javax.inject.Inject; 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 @@ public class Monitor implements CallbackController<Monitor.Callback> { 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 @@ public class Monitor implements CallbackController<Monitor.Callback> { }; @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 @@ public class Monitor implements CallbackController<Monitor.Callback> { // 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 @@ public class Monitor implements CallbackController<Monitor.Callback> { @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/communal/conditions/CommunalConditionsMonitor.java b/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java index 1197816e24d2..fc67973fe278 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java @@ -14,29 +14,33 @@ * limitations under the License. */ -package com.android.systemui.communal.conditions; +package com.android.systemui.util.condition.dagger; - -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; +import dagger.BindsInstance; +import dagger.Subcomponent; /** - * A concrete implementation of {@Monitor} with conditions for monitoring when communal mode should - * be enabled. + * Component for {@link Monitor}. */ -@SysUISingleton -public class CommunalConditionsMonitor extends Monitor { - @Inject - public CommunalConditionsMonitor( - @Named(COMMUNAL_CONDITIONS) Set<Condition> communalConditions) { - super(communalConditions); +@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 981bf01164e3..7892d6eec98d 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 @@ package com.android.systemui.util.dagger; 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 @@ import dagger.Module; /** 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 e967033b69a2..74e0f4002026 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 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { 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 e4336fe07dbb..8717a0eaf57f 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 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { } @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 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { } @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 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { } @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 77762859de18..24b01e079b42 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 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { 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 9a9b7c4f62eb..4a29ada8a998 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 android.testing.AndroidTestingRunner; 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 @@ public class CommunalManagerUpdaterTest extends SysuiTestCase { @Mock private CommunalManager mCommunalManager; @Mock - private CommunalConditionsMonitor mCommunalConditionsMonitor; + private Monitor mCommunalConditionsMonitor; private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); @@ -55,7 +55,7 @@ public class CommunalManagerUpdaterTest extends SysuiTestCase { 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 409dd940ceb6..df1cc766d162 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 android.testing.TestableLooper; 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 @@ import java.lang.ref.WeakReference; @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 @@ public class CommunalSourceMonitorTest extends SysuiTestCase { 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 873f7a45571b..55af51d3fddf 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.plugins.statusbar.StatusBarStateController; 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.junit.runner.RunWith; 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 @@ public class DozeUiTest extends SysuiTestCase { @Mock private StatusBarStateController mStatusBarStateController; @Mock + private FoldAodAnimationController mFoldAodAnimationController; + @Mock + private SysUIUnfoldComponent mSysUIUnfoldComponent; + @Mock private ConfigurationController mConfigurationController; @Before @@ -90,9 +98,13 @@ public class DozeUiTest extends SysuiTestCase { 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 @@ public class DozeUiTest extends SysuiTestCase { 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 @@ public class DozeUiTest extends SysuiTestCase { 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 @@ public class DozeUiTest extends SysuiTestCase { } @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 @@ public class DozeUiTest extends SysuiTestCase { 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 53bfeee9135a..904ee91c2642 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 androidx.test.filters.SmallTest; 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 @@ public class DreamOverlayServiceTest extends SysuiTestCase { @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 @@ public class DreamOverlayServiceTest extends SysuiTestCase { @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 @@ public class DreamOverlayServiceTest extends SysuiTestCase { // 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 000000000000..7f72dda441d2 --- /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 8d329c56af36..27fcb11b8dfc 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.ScreenOffAnimationController; 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.Before; 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 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { 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 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { 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 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { @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 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { @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 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { 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 000000000000..af33dafb6107 --- /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 9bcdcc96767a..1cd9b9e77423 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 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { .thenReturn(mKeyguardUserSwitcherComponent); when(mKeyguardUserSwitcherComponent.getKeyguardUserSwitcherController()) .thenReturn(mKeyguardUserSwitcherController); + when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(true); doAnswer((Answer<Void>) invocation -> { mTouchHandler = invocation.getArgument(0); @@ -880,11 +881,11 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { 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 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { 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 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { 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 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { // 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 2d548e96e598..a8544a9a15e4 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 @@ class SplitShadeHeaderControllerTest : SysuiTestCase() { var viewVisibility = View.GONE private lateinit var splitShadeHeaderController: SplitShadeHeaderController + private lateinit var carrierIconSlots: List<String> @Before fun setup() { @@ -67,12 +68,13 @@ class SplitShadeHeaderControllerTest : SysuiTestCase() { 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 @@ class SplitShadeHeaderControllerTest : SysuiTestCase() { @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 c5bdfed6082b..cbaa460d711b 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.KeyguardUpdateMonitor; 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 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Mock private NotificationPanelViewController mNotificationPanelView; @Mock - private BiometricUnlockController mBiometrucUnlockController; - @Mock - private DismissCallbackRegistry mDismissCallbackRegistry; + private BiometricUnlockController mBiometricUnlockController; @Mock private SysuiStatusBarStateController mStatusBarStateController; @Mock @@ -97,15 +92,12 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @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 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { .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 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { 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 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @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 878bdeac43c9..d6454490e62a 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 @@ public class ConditionMonitorTest extends SysuiTestCase { 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 @@ public class ConditionMonitorTest extends SysuiTestCase { @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 1f5834d72dd0..a03dcbd3941d 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 ac1f0226be52..c5d0c9e3f348 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 ac1f0226be52..c5d0c9e3f348 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 ac1f0226be52..c5d0c9e3f348 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 ac1f0226be52..c5d0c9e3f348 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 881c910b399b..f050b6622a5d 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 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ 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 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ 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 d0b989582ebe..7a525ee23d27 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 @@ public class MagnificationProcessor { 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 @@ public class MagnificationProcessor { } /** - * 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 @@ public class MagnificationProcessor { * @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 @@ public class MagnificationProcessor { * @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 @@ public class MagnificationProcessor { } } - 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 @@ public class MagnificationProcessor { * @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 @@ public class MagnificationProcessor { } /** + * 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 0855b9d8f675..0fe90b14c9bb 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_READ_WRITE; 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.RemoteException; 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 @@ public class KeyValueAdbBackupEngine { 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 @@ public class KeyValueAdbBackupEngine { 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 98ea03e35296..81d6381a2273 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.internal.util.Preconditions; 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 @@ public class UserBackupManagerService { 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 @@ public class UserBackupManagerService { 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 @@ public class UserBackupManagerService { } @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 @@ public class UserBackupManagerService { 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 @@ public class UserBackupManagerService { return mTransportManager; } + public OperationStorage getOperationStorage() { + return mOperationStorage; + } + public boolean isEnabled() { return mEnabled; } @@ -838,14 +811,6 @@ public class UserBackupManagerService { return mActiveRestoreSession; } - public SparseArray<Operation> getCurrentOperations() { - return mCurrentOperations; - } - - public Object getCurrentOpLock() { - return mCurrentOpLock; - } - public SparseArray<AdbParams> getAdbBackupRestoreConfirmations() { return mAdbBackupRestoreConfirmations; } @@ -1987,18 +1952,12 @@ public class UserBackupManagerService { } 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 @@ public class UserBackupManagerService { /** 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 @@ public class UserBackupManagerService { + 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 @@ public class UserBackupManagerService { } } - /** - * 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")); - } - } - 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)); - } + // 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)); } - 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 @@ public class UserBackupManagerService { String[] pkg = new String[]{entry.packageName}; mRunningFullBackupTask = PerformFullTransportBackupTask.newWithCurrentTransport( this, + mOperationStorage, /* observer */ null, pkg, /* updateSchedule */ true, @@ -3107,6 +2931,7 @@ public class UserBackupManagerService { CountDownLatch latch = new CountDownLatch(1); Runnable task = PerformFullTransportBackupTask.newWithCurrentTransport( this, + mOperationStorage, /* observer */ null, pkgNames, /* updateSchedule */ false, @@ -4126,48 +3951,11 @@ public class UserBackupManagerService { * 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 fe5497f3eb94..1e1ca95d69db 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.MORE_DEBUG; 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 android.util.Slog; 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 @@ public class FullBackupEngine { 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 aaf1f0a65dc8..be6ac26ba92a 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 @@ package com.android.server.backup.fullbackup; 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 android.util.Slog; 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 @@ public class FullBackupObbConnection implements ServiceConnection { 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 448e0860b88d..7ee307e30dce 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 android.util.Slog; 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 @@ import javax.crypto.spec.SecretKeySpec; 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 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor 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 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor BackupEligibilityRules backupEligibilityRules) { super(observer); mUserBackupManagerService = backupManagerService; + mOperationStorage = operationStorage; mCurrentOpToken = backupManagerService.generateRandomIntegerToken(); mLatch = latch; @@ -505,6 +509,6 @@ public class PerformAdbBackupTask extends FullBackupTask implements BackupRestor 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 9ce4eabb0fa1..0ca77d1552aa 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 @@ package com.android.server.backup.fullbackup; 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.EventLogTags; 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 @@ import java.util.concurrent.atomic.AtomicLong; 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 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba listenerCaller); return new PerformFullTransportBackupTask( backupManagerService, + operationStorage, transportConnection, observer, whichPackages, @@ -136,6 +137,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba private UserBackupManagerService mUserBackupManagerService; private final Object mCancelLock = new Object(); + OperationStorage mOperationStorage; List<PackageInfo> mPackages; PackageInfo mCurrentPackage; boolean mUpdateSchedule; @@ -158,6 +160,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba private final BackupEligibilityRules mBackupEligibilityRules; public PerformFullTransportBackupTask(UserBackupManagerService backupManagerService, + OperationStorage operationStorage, TransportConnection transportConnection, IFullBackupRestoreObserver observer, String[] whichPackages, boolean updateSchedule, @@ -165,7 +168,8 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba @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 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba } 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 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba 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 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba } mResult.set(result); mLatch.countDown(); - mUserBackupManagerService.removeOperation(mCurrentOpToken); + mOperationStorage.removeOperation(mCurrentOpToken); } @Override @@ -787,7 +788,7 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba } mResult.set(BackupTransport.AGENT_ERROR); mLatch.countDown(); - mUserBackupManagerService.removeOperation(mCurrentOpToken); + mOperationStorage.removeOperation(mCurrentOpToken); } @Override @@ -837,16 +838,12 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba } 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 @@ public class PerformFullTransportBackupTask extends FullBackupTask implements Ba 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 5c2485905814..03796eae3b8e 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.EventLogTags; 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 class BackupHandler extends Handler { 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 @@ public class BackupHandler extends Handler { 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 @@ public class BackupHandler extends Handler { caller); KeyValueBackupTask.start( backupManagerService, + mOperationStorage, transportConnection, transport.transportDirName(), queue, @@ -278,8 +283,8 @@ public class BackupHandler extends Handler { // 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 @@ public class BackupHandler extends Handler { PerformUnifiedRestoreTask task = new PerformUnifiedRestoreTask( backupManagerService, + mOperationStorage, params.mTransportConnection, params.observer, params.monitor, @@ -332,7 +338,7 @@ public class BackupHandler extends Handler { // 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 @@ public class BackupHandler extends Handler { 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 30da8c1d5e76..16aa4ebb0656 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_READ_WRITE; 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.BackupAgentTimeoutParameters; 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 class KeyValueBackupTask implements BackupRestoreTask, Runnable { */ public static KeyValueBackupTask start( UserBackupManagerService backupManagerService, + OperationStorage operationStorage, TransportConnection transportConnection, String transportDirName, List<String> queue, @@ -227,6 +228,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { KeyValueBackupTask task = new KeyValueBackupTask( backupManagerService, + operationStorage, transportConnection, transportDirName, queue, @@ -244,6 +246,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { } private final UserBackupManagerService mBackupManagerService; + private final OperationStorage mOperationStorage; private final PackageManager mPackageManager; private final TransportConnection mTransportConnection; private final BackupAgentTimeoutParameters mAgentTimeoutParameters; @@ -302,6 +305,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { @VisibleForTesting public KeyValueBackupTask( UserBackupManagerService backupManagerService, + OperationStorage operationStorage, TransportConnection transportConnection, String transportDirName, List<String> queue, @@ -313,6 +317,7 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { boolean nonIncremental, BackupEligibilityRules backupEligibilityRules) { mBackupManagerService = backupManagerService; + mOperationStorage = operationStorage; mPackageManager = backupManagerService.getPackageManager(); mTransportConnection = transportConnection; mOriginalQueue = queue; @@ -338,12 +343,11 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { } 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 @@ public class KeyValueBackupTask implements BackupRestoreTask, Runnable { 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 376b618935c5..cfc0f203e6ec 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 android.util.Slog; 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 @@ public class AdbRestoreFinishedLatch implements BackupRestoreTask { 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 @@ public class AdbRestoreFinishedLatch implements BackupRestoreTask { Slog.w(TAG, "adb onRestoreFinished() complete"); } mLatch.countDown(); - backupManagerService.removeOperation(mCurrentOpToken); + mOperationStorage.removeOperation(mCurrentOpToken); } @Override @@ -81,6 +85,6 @@ public class AdbRestoreFinishedLatch implements BackupRestoreTask { 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 5718bdfc4971..76df8b9f84e8 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.MORE_DEBUG; 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.BackupAgentTimeoutParameters; 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 @@ import java.util.Objects; 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 @@ public class FullRestoreEngine extends RestoreEngine { 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 @@ public class FullRestoreEngine extends RestoreEngine { 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 @@ public class FullRestoreEngine extends RestoreEngine { 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 e03150eb824f..22af19e8d2cb 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 android.util.Slog; 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 @@ import javax.crypto.spec.SecretKeySpec; 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 @@ public class PerformAdbRestoreTask implements Runnable { 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 @@ public class PerformAdbRestoreTask implements Runnable { 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 ac831af7612b..b48367db17c2 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.DEBUG; 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.LocalServices; 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 @@ import java.util.Set; 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 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { PerformUnifiedRestoreTask(UserBackupManagerService backupManagerService) { mListener = null; mAgentTimeoutParameters = null; + mOperationStorage = null; mTransportConnection = null; mTransportManager = null; mEphemeralOpToken = 0; @@ -181,6 +184,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // about releasing it. public PerformUnifiedRestoreTask( UserBackupManagerService backupManagerService, + OperationStorage operationStorage, TransportConnection transportConnection, IRestoreObserver observer, IBackupManagerMonitor monitor, @@ -192,6 +196,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { OnTaskFinishedListener listener, BackupEligibilityRules backupEligibilityRules) { this.backupManagerService = backupManagerService; + mOperationStorage = operationStorage; mUserId = backupManagerService.getUserId(); mTransportManager = backupManagerService.getTransportManager(); mEphemeralOpToken = backupManagerService.generateRandomIntegerToken(); @@ -767,7 +772,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { 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 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { 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 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { 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 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // 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 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { @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 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { // 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 a745e5afc6f0..4727b162dfdd 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.DEBUG_APP; 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.text.style.SuggestionSpan; 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 221de8d003e5..d6a4cf650cba 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 @@ package com.android.server.am; 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.provider.DeviceConfig.Properties; 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 @@ public class AppProfiler { } 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 @@ public class AppProfiler { } } - 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 b07684c9a004..2ec1aedd18f9 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 @@ package com.android.server.am; 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.app.ApplicationExitInfo.SubReason; 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 @@ public final class PhantomProcessList { * 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 20606f0e9f4d..8dce8e9e90ef 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 final class GameManagerService extends IGameManagerService.Stub { } 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 @@ public final class GameManagerService extends IGameManagerService.Stub { } 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 000000000000..241abafe6179 --- /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 6238198e9488..8615393dbfec 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.PowerManager; 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 @@ public class AudioService extends IAudioService.Stub } } + @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 @@ public class AudioService extends IAudioService.Stub 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 66f62355c7f0..69a4c23cf867 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.AudioManager; 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 @@ public class MediaFocusControl implements PlayerFocusEnforcer { } } + /** + * 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 3f55848e26a8..4c569996817d 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 @@ public final class DisplayManagerService extends SystemService { + "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 @@ public final class DisplayManagerService extends SystemService { } } + 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 9412c938f934..43a850c5d626 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.Context; 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 @@ class DisplayManagerShellCommand extends ShellCommand { 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 @@ class DisplayManagerShellCommand extends ShellCommand { 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 @@ class DisplayManagerShellCommand extends ShellCommand { 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 05e1bdd11db6..3d91feef7043 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.Trace; 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.annotations.GuardedBy; 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 @@ final class InputMethodBindingController { 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 @@ final class InputMethodBindingController { } /** - * Indicates whether {@link #getVisibleConnection} is currently in use. + * Indicates whether {@link #mVisibleConnection} is currently in use. */ boolean isVisibleBound() { return mVisibleBound; @@ -248,11 +253,6 @@ final class InputMethodBindingController { /** * 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 @@ final class InputMethodBindingController { // 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 @@ final class InputMethodBindingController { } mCurId = null; - mService.clearClientSessionsLocked(); + clearCurMethodAndSessionsLocked(); } @GuardedBy("mMethodMap") - void clearCurMethodLocked() { + private void clearCurMethodAndSessionsLocked() { + mService.clearClientSessionsLocked(); mCurMethod = null; mCurMethodUid = Process.INVALID_UID; } @@ -382,7 +383,7 @@ final class InputMethodBindingController { @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 @@ final class InputMethodBindingController { 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 @@ final class InputMethodBindingController { } @GuardedBy("mMethodMap") - private void addFreshWindowTokenLocked(int displayIdToShowIme) { + private void addFreshWindowTokenLocked() { + int displayIdToShowIme = mService.getDisplayIdToShowIme(); mCurToken = new Binder(); mService.setCurTokenDisplayId(displayIdToShowIme); @@ -438,7 +440,7 @@ final class InputMethodBindingController { } @GuardedBy("mMethodMap") - void unbindMainConnectionLocked() { + private void unbindMainConnectionLocked() { mContext.unbindService(mMainConnection); mHasConnection = false; } @@ -460,17 +462,61 @@ final class InputMethodBindingController { } @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 3c6b0966dfc3..bff4f273e195 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.Display.INVALID_DISPLAY; 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.UserHandle; 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } // 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub finishSessionLocked(mEnabledSession); mEnabledSession = null; - mBindingController.clearCurMethodLocked(); scheduleNotifyImeUidToAudioService(Process.INVALID_UID); } hideStatusBarIconLocked(); @@ -3047,42 +3043,20 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } else { res = false; } - if (hasConnection() && isVisibleBound()) { - mBindingController.unbindVisibleConnectionLocked(); - } + mBindingController.setCurrentMethodNotVisibleLocked(); mInputShown = false; mShowRequested = false; mShowExplicitlyRequested = false; @@ -3522,10 +3494,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub + " 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 0ce24dda666e..ede8b32c9b11 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 @@ public class LocationProviderManager extends 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 @@ public class LocationProviderManager extends @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 @@ public class LocationProviderManager extends @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 @@ public class LocationProviderManager extends 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 @@ public class LocationProviderManager extends @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 @@ public class LocationProviderManager extends 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 @@ public class LocationProviderManager extends .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_TAG); mWakeLock.setReferenceCounted(true); mWakeLock.setWorkSource(request.getWorkSource()); + mWakeLockReleaser = new ExternalWakeLockReleaser(identity, mWakeLock); } @Override @@ -943,7 +964,7 @@ public class LocationProviderManager extends } listener.deliverOnLocationChanged(deliverLocationResult, - mUseWakeLock ? mWakeLock::release : null); + mUseWakeLock ? mWakeLockReleaser : null); EVENT_LOG.logProviderDeliveredLocations(mName, locationResult.size(), getIdentity()); } @@ -2761,7 +2782,7 @@ public class LocationProviderManager extends @GuardedBy("this") private boolean mRun; - GatedCallback(Runnable callback) { + GatedCallback(@Nullable Runnable callback) { mCallback = callback; } @@ -2796,4 +2817,24 @@ public class LocationProviderManager extends } } } + + 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 0564e858c84c..fdcf1fcfd260 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 @@ package com.android.server.pm; 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 @@ class PackageManagerShellCommand extends ShellCommand { @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 111087f0baf2..a3b0e3e7d02d 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 @@ public class DomainVerificationService extends SystemService @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 122b3f35d8d1..1aa75985e4bc 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.TvIAppInfo; 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 @@ public class TvIAppManagerService extends SystemService { 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 @@ public class TvIAppManagerService extends SystemService { } @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 @@ public class TvIAppManagerService extends SystemService { 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 @@ public class TvIAppManagerService extends SystemService { 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 @@ public class TvIAppManagerService extends SystemService { } } + 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 eb6895264c60..44d36237b555 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.CompatibilityInfo; 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 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // 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 ccb4f5995330..1c574fbb8df7 100644 --- a/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp +++ b/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp @@ -151,13 +151,8 @@ int register_android_server_UsbAlsaJackDetector(JNIEnv *env) 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 06f5aed026ce..f0f779dc9ac9 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 @@ static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject } } - 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 fd295c0739cf..9e83f8e7bda8 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 @@ public class KeyValueBackupTaskTest { 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 @@ public class KeyValueBackupTaskTest { 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 @@ public class KeyValueBackupTaskTest { 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 9eb99aed2ba8..e0812d6a77ea 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 android.platform.test.annotations.Presubmit; 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 @@ public class ActiveRestoreSessionTest { @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 @@ public class ActiveRestoreSessionTest { // 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 77b5b61b8f01..fc3ec7b44f4f 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 @@ public class BackupManagerServiceTestUtils { 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 06b7fb7e6ae3..6a7d03176b1c 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 @@ package com.android.server.testing.shadows; 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 @@ public class ShadowKeyValueBackupTask { @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 71010a9fe935..d985e1b0f341 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.IBackupManagerMonitor; 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 @@ public class ShadowPerformUnifiedRestoreTask { @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 d1d7cc6422c3..d9dbf48f969f 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 @@ package com.android.server.pm.test.verify.domain 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.content.pm.verify.domain.DomainVerificationState 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.anyInt 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 @@ class DomainVerificationEnforcerTest { 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 @@ class DomainVerificationEnforcerTest { } ) } + whenever(signingDetails) { SigningDetails.UNKNOWN } } fun mockPkgState(packageName: String, domainSetId: UUID) = @@ -327,6 +339,7 @@ class DomainVerificationEnforcerTest { } } whenever(isSystem) { false } + whenever(signingDetails) { SigningDetails.UNKNOWN } } } @@ -794,8 +807,12 @@ class DomainVerificationEnforcerTest { } 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 f92b872e1d26..28c1c815508f 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 @@ public class AbstractAccessibilityServiceConnectionTest { 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 @@ public class AbstractAccessibilityServiceConnectionTest { @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 @@ public class AbstractAccessibilityServiceConnectionTest { @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 @@ public class AbstractAccessibilityServiceConnectionTest { @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 9ebec981fa21..621507e2bfc8 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 @@ public class MagnificationProcessorTest { .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 @@ public class MagnificationProcessorTest { 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 @@ public class MagnificationProcessorTest { } @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 class MagnificationProcessorTest { 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 @@ public class MagnificationProcessorTest { } @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 @@ public class MagnificationProcessorTest { 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 class MagnificationProcessorTest { 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 class MagnificationProcessorTest { 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 c36e1a841a7d..bc953412a36e 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 android.platform.test.annotations.Presubmit; 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 @@ public class UserBackupManagerServiceTest { @Mock TransportConnection mTransportConnection; @Mock BackupTransportClient mBackupTransport; @Mock BackupEligibilityRules mBackupEligibilityRules; - + @Mock LifecycleOperationStorage mOperationStorage; private TestBackupService mService; @@ -68,7 +69,7 @@ public class UserBackupManagerServiceTest { 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 @@ public class UserBackupManagerServiceTest { 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 fa35e3f8646e..3c79d8bde37b 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 @@ package com.android.server.backup.internal; 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 android.platform.test.annotations.Presubmit; 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 @@ public class BackupHandlerTest { 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 @@ public class BackupHandlerTest { 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 1d7a4761dec6..4469ffc14447 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 @@ import java.lang.annotation.RetentionPolicy; */ public final class AccessNetworkConstants { + private static final String TAG = AccessNetworkConstants.class.getSimpleName(); + /** * Wireless transportation type * @@ -108,6 +111,21 @@ public final class AccessNetworkConstants { 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 7d24b761b814..c80d35b9e772 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -5381,6 +5381,34 @@ public class CarrierConfigManager { 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 @@ public class CarrierConfigManager { 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 @@ public class CarrierConfigManager { 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 866fd2c7394e..ba9584112b75 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -530,6 +530,8 @@ public interface RILConstants { 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; |