Merge "Temporarily disable trusted network condition."
diff --git a/Android.bp b/Android.bp
index 778aa55..95cdea0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -69,6 +69,7 @@
// Java/AIDL sources under frameworks/base
":framework-annotations",
":framework-blobstore-sources",
+ ":framework-connectivity-nsd-sources",
":framework-core-sources",
":framework-drm-sources",
":framework-graphics-nonupdatable-sources",
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
index d729019..e1e6e47 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/AlarmManagerEconomicPolicy.java
@@ -357,7 +357,7 @@
pw.print("Other", narcToString(mMinSatiatedBalanceOther)).println();
pw.decreaseIndent();
pw.print("Max satiated balance", narcToString(mMaxSatiatedBalance)).println();
- pw.print("Min satiated circulation", narcToString(mMaxSatiatedCirculation)).println();
+ pw.print("Max satiated circulation", narcToString(mMaxSatiatedCirculation)).println();
pw.println();
pw.println("Actions:");
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
index 7bf0e6d..a4e7b80 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/CompleteEconomicPolicy.java
@@ -33,8 +33,8 @@
/** Lazily populated set of rewards covered by this policy. */
private final SparseArray<Reward> mRewards = new SparseArray<>();
private final int[] mCostModifiers;
- private final long mMaxSatiatedBalance;
- private final long mMaxSatiatedCirculation;
+ private long mMaxSatiatedBalance;
+ private long mMaxSatiatedCirculation;
CompleteEconomicPolicy(@NonNull InternalResourceService irs) {
super(irs);
@@ -53,6 +53,19 @@
mCostModifiers[i] = costModifiers.valueAt(i);
}
+ updateMaxBalances();
+ }
+
+ @Override
+ void setup() {
+ super.setup();
+ for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
+ mEnabledEconomicPolicies.valueAt(i).setup();
+ }
+ updateMaxBalances();
+ }
+
+ private void updateMaxBalances() {
long max = 0;
for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
max += mEnabledEconomicPolicies.valueAt(i).getMaxSatiatedBalance();
@@ -67,14 +80,6 @@
}
@Override
- void setup() {
- super.setup();
- for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
- mEnabledEconomicPolicies.valueAt(i).setup();
- }
- }
-
- @Override
long getMinSatiatedBalance(final int userId, @NonNull final String pkgName) {
long min = 0;
for (int i = 0; i < mEnabledEconomicPolicies.size(); ++i) {
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 437a101..20a300a 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -847,15 +847,16 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
+ boolean dumpAll = true;
if (!ArrayUtils.isEmpty(args)) {
String arg = args[0];
if ("-h".equals(arg) || "--help".equals(arg)) {
dumpHelp(pw);
return;
} else if ("-a".equals(arg)) {
- // -a is passed when dumping a bug report so we have to acknowledge the
- // argument. However, we currently don't do anything differently for bug
- // reports.
+ // -a is passed when dumping a bug report. Bug reports have a time limit for
+ // each service dump, so we can't dump everything.
+ dumpAll = false;
} else if (arg.length() > 0 && arg.charAt(0) == '-') {
pw.println("Unknown option: " + arg);
return;
@@ -864,7 +865,7 @@
final long identityToken = Binder.clearCallingIdentity();
try {
- dumpInternal(new IndentingPrintWriter(pw, " "));
+ dumpInternal(new IndentingPrintWriter(pw, " "), dumpAll);
} finally {
Binder.restoreCallingIdentity(identityToken);
}
@@ -1098,7 +1099,7 @@
pw.println(" [package] is an optional package name to limit the output to.");
}
- private void dumpInternal(final IndentingPrintWriter pw) {
+ private void dumpInternal(final IndentingPrintWriter pw, final boolean dumpAll) {
synchronized (mLock) {
pw.print("Is enabled: ");
pw.println(mIsEnabled);
@@ -1127,7 +1128,7 @@
mCompleteEconomicPolicy.dump(pw);
pw.println();
- mScribe.dumpLocked(pw);
+ mScribe.dumpLocked(pw, dumpAll);
pw.println();
mAgent.dumpLocked(pw);
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
index 2318026..1f8ce26 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -332,7 +332,7 @@
pw.print("Other", narcToString(mMinSatiatedBalanceOther)).println();
pw.decreaseIndent();
pw.print("Max satiated balance", narcToString(mMaxSatiatedBalance)).println();
- pw.print("Min satiated circulation", narcToString(mMaxSatiatedCirculation)).println();
+ pw.print("Max satiated circulation", narcToString(mMaxSatiatedCirculation)).println();
pw.println();
pw.println("Actions:");
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
index 42f3d9a..86968ef 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Scribe.java
@@ -509,7 +509,7 @@
}
@GuardedBy("mIrs.getLock()")
- void dumpLocked(IndentingPrintWriter pw) {
+ void dumpLocked(IndentingPrintWriter pw, boolean dumpAll) {
pw.println("Ledgers:");
pw.increaseIndent();
mLedgers.forEach((userId, pkgName, ledger) -> {
@@ -519,7 +519,7 @@
}
pw.println();
pw.increaseIndent();
- ledger.dump(pw, MAX_NUM_TRANSACTION_DUMP);
+ ledger.dump(pw, dumpAll ? Integer.MAX_VALUE : MAX_NUM_TRANSACTION_DUMP);
pw.decreaseIndent();
});
pw.decreaseIndent();
diff --git a/core/api/current.txt b/core/api/current.txt
index 2501c02..fd429f0 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -3264,9 +3264,9 @@
method public float getScale();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.MagnificationConfig> CREATOR;
- field public static final int DEFAULT_MODE = 0; // 0x0
- field public static final int FULLSCREEN_MODE = 1; // 0x1
- field public static final int WINDOW_MODE = 2; // 0x2
+ field public static final int MAGNIFICATION_MODE_DEFAULT = 0; // 0x0
+ field public static final int MAGNIFICATION_MODE_FULLSCREEN = 1; // 0x1
+ field public static final int MAGNIFICATION_MODE_WINDOW = 2; // 0x2
}
public static final class MagnificationConfig.Builder {
@@ -3275,7 +3275,7 @@
method @NonNull public android.accessibilityservice.MagnificationConfig.Builder setCenterX(float);
method @NonNull public android.accessibilityservice.MagnificationConfig.Builder setCenterY(float);
method @NonNull public android.accessibilityservice.MagnificationConfig.Builder setMode(int);
- method @NonNull public android.accessibilityservice.MagnificationConfig.Builder setScale(float);
+ method @NonNull public android.accessibilityservice.MagnificationConfig.Builder setScale(@FloatRange(from=1.0f, to=8.0f) float);
}
public final class TouchInteractionController {
@@ -9561,6 +9561,20 @@
field @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = "android.bluetooth.action.LE_AUDIO_CONNECTION_STATE_CHANGED";
}
+ public final class BluetoothLeAudioCodecConfig {
+ method @NonNull public String getCodecName();
+ method public int getCodecType();
+ method public static int getMaxCodecType();
+ field public static final int SOURCE_CODEC_TYPE_INVALID = 1000000; // 0xf4240
+ field public static final int SOURCE_CODEC_TYPE_LC3 = 0; // 0x0
+ }
+
+ public static final class BluetoothLeAudioCodecConfig.Builder {
+ ctor public BluetoothLeAudioCodecConfig.Builder();
+ method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig build();
+ method @NonNull public android.bluetooth.BluetoothLeAudioCodecConfig.Builder setCodecType(int);
+ }
+
public final class BluetoothManager {
method public android.bluetooth.BluetoothAdapter getAdapter();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int);
@@ -9967,6 +9981,9 @@
public final class AssociationRequest implements android.os.Parcelable {
method public int describeContents();
+ method @Nullable public String getDeviceProfile();
+ method @Nullable public CharSequence getDisplayName();
+ method public boolean isSingleDevice();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.companion.AssociationRequest> CREATOR;
field public static final String DEVICE_PROFILE_WATCH = "android.app.role.COMPANION_DEVICE_WATCH";
@@ -16443,6 +16460,7 @@
ctor public SurfaceTexture(boolean);
method public void attachToGLContext(int);
method public void detachFromGLContext();
+ method public long getDataSpace();
method public long getTimestamp();
method public void getTransformMatrix(float[]);
method public boolean isReleased();
@@ -17660,6 +17678,52 @@
method public int getMinFrequency();
}
+ public final class DataSpace {
+ method public static long getRange(long);
+ method public static long getStandard(long);
+ method public static long getTransfer(long);
+ method public static long pack(long, long, long);
+ field public static final long DATASPACE_ADOBE_RGB = 151715840L; // 0x90b0000L
+ field public static final long DATASPACE_BT2020 = 147193856L; // 0x8c60000L
+ field public static final long DATASPACE_BT2020_PQ = 163971072L; // 0x9c60000L
+ field public static final long DATASPACE_BT601_525 = 281280512L; // 0x10c40000L
+ field public static final long DATASPACE_BT601_625 = 281149440L; // 0x10c20000L
+ field public static final long DATASPACE_BT709 = 281083904L; // 0x10c10000L
+ field public static final long DATASPACE_DCI_P3 = 155844608L; // 0x94a0000L
+ field public static final long DATASPACE_DISPLAY_P3 = 143261696L; // 0x88a0000L
+ field public static final long DATASPACE_JFIF = 146931712L; // 0x8c20000L
+ field public static final long DATASPACE_SCRGB = 411107328L; // 0x18810000L
+ field public static final long DATASPACE_SCRGB_LINEAR = 406913024L; // 0x18410000L
+ field public static final long DATASPACE_SRGB = 142671872L; // 0x8810000L
+ field public static final long DATASPACE_SRGB_LINEAR = 138477568L; // 0x8410000L
+ field public static final long DATASPACE_UNKNOWN = 0L; // 0x0L
+ field public static final long RANGE_EXTENDED = 402653184L; // 0x18000000L
+ field public static final long RANGE_FULL = 134217728L; // 0x8000000L
+ field public static final long RANGE_LIMITED = 268435456L; // 0x10000000L
+ field public static final long RANGE_UNSPECIFIED = 0L; // 0x0L
+ field public static final long STANDARD_ADOBE_RGB = 720896L; // 0xb0000L
+ field public static final long STANDARD_BT2020 = 393216L; // 0x60000L
+ field public static final long STANDARD_BT2020_CONSTANT_LUMINANCE = 458752L; // 0x70000L
+ field public static final long STANDARD_BT470M = 524288L; // 0x80000L
+ field public static final long STANDARD_BT601_525 = 262144L; // 0x40000L
+ field public static final long STANDARD_BT601_525_UNADJUSTED = 327680L; // 0x50000L
+ field public static final long STANDARD_BT601_625 = 131072L; // 0x20000L
+ field public static final long STANDARD_BT601_625_UNADJUSTED = 196608L; // 0x30000L
+ field public static final long STANDARD_BT709 = 65536L; // 0x10000L
+ field public static final long STANDARD_DCI_P3 = 655360L; // 0xa0000L
+ field public static final long STANDARD_FILM = 589824L; // 0x90000L
+ field public static final long STANDARD_UNSPECIFIED = 0L; // 0x0L
+ field public static final long TRANSFER_GAMMA2_2 = 16777216L; // 0x1000000L
+ field public static final long TRANSFER_GAMMA2_6 = 20971520L; // 0x1400000L
+ field public static final long TRANSFER_GAMMA2_8 = 25165824L; // 0x1800000L
+ field public static final long TRANSFER_HLG = 33554432L; // 0x2000000L
+ field public static final long TRANSFER_LINEAR = 4194304L; // 0x400000L
+ field public static final long TRANSFER_SMPTE_170M = 12582912L; // 0xc00000L
+ field public static final long TRANSFER_SRGB = 8388608L; // 0x800000L
+ field public static final long TRANSFER_ST2084 = 29360128L; // 0x1c00000L
+ field public static final long TRANSFER_UNSPECIFIED = 0L; // 0x0L
+ }
+
public class GeomagneticField {
ctor public GeomagneticField(float, float, float, long);
method public float getDeclination();
@@ -21598,6 +21662,7 @@
public abstract class Image implements java.lang.AutoCloseable {
method public abstract void close();
method public android.graphics.Rect getCropRect();
+ method public long getDataSpace();
method public abstract int getFormat();
method @Nullable public android.hardware.HardwareBuffer getHardwareBuffer();
method public abstract int getHeight();
@@ -21605,6 +21670,7 @@
method public abstract long getTimestamp();
method public abstract int getWidth();
method public void setCropRect(android.graphics.Rect);
+ method public void setDataSpace(long);
method public void setTimestamp(long);
}
@@ -31790,7 +31856,7 @@
method @Nullable public <T> T[] readParcelableArray(@Nullable ClassLoader, @NonNull Class<T>);
method @Deprecated @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
method @Nullable public <T> android.os.Parcelable.Creator<T> readParcelableCreator(@Nullable ClassLoader, @NonNull Class<T>);
- method @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader);
+ method @Deprecated @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader);
method @NonNull public <T> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader, @NonNull Class<T>);
method @Nullable public android.os.PersistableBundle readPersistableBundle();
method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);
@@ -43120,7 +43186,7 @@
method public boolean isDataCapable();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isDataConnectionAllowed();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabled();
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledForReason(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabledForReason(int);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
method public boolean isEmergencyNumber(@NonNull String);
method public boolean isHearingAidCompatibilitySupported();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index fb8e0c7..c4aec0e 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -83,6 +83,7 @@
field public static final String CAPTURE_TV_INPUT = "android.permission.CAPTURE_TV_INPUT";
field public static final String CAPTURE_VOICE_COMMUNICATION_OUTPUT = "android.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT";
field public static final String CHANGE_APP_IDLE_STATE = "android.permission.CHANGE_APP_IDLE_STATE";
+ field public static final String CHANGE_APP_LAUNCH_TIME_ESTIMATE = "android.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE";
field public static final String CHANGE_DEVICE_IDLE_TEMP_WHITELIST = "android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST";
field public static final String CLEAR_APP_USER_DATA = "android.permission.CLEAR_APP_USER_DATA";
field public static final String COMPANION_APPROVE_WIFI_CONNECTIONS = "android.permission.COMPANION_APPROVE_WIFI_CONNECTIONS";
@@ -1923,6 +1924,8 @@
method public void reportUsageStop(@NonNull android.app.Activity, @NonNull String);
method @RequiresPermission(android.Manifest.permission.CHANGE_APP_IDLE_STATE) public void setAppStandbyBucket(String, int);
method @RequiresPermission(android.Manifest.permission.CHANGE_APP_IDLE_STATE) public void setAppStandbyBuckets(java.util.Map<java.lang.String,java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE) public void setEstimatedLaunchTime(@NonNull String, long);
+ method @RequiresPermission(android.Manifest.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE) public void setEstimatedLaunchTimes(@NonNull java.util.Map<java.lang.String,java.lang.Long>);
method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void unregisterAppUsageLimitObserver(int);
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void unregisterAppUsageObserver(int);
method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void unregisterUsageSessionObserver(int);
@@ -9199,6 +9202,7 @@
public final class StorageVolume implements android.os.Parcelable {
method @NonNull public String getId();
+ method public boolean isStub();
}
}
@@ -9762,7 +9766,9 @@
field public static final int MATCH_ALL_APN_SET_ID = -1; // 0xffffffff
field public static final String MAX_CONNECTIONS = "max_conns";
field public static final String MODEM_PERSIST = "modem_cognitive";
- field public static final String MTU = "mtu";
+ field @Deprecated public static final String MTU = "mtu";
+ field public static final String MTU_V4 = "mtu_v4";
+ field public static final String MTU_V6 = "mtu_v6";
field public static final int NO_APN_SET_ID = 0; // 0x0
field public static final String TIME_LIMIT_FOR_MAX_CONNECTIONS = "max_conns_time";
field public static final int UNEDITED = 0; // 0x0
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index c1ab070..6b4d773 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -3298,7 +3298,7 @@
method @NonNull public android.window.WindowContainerTransaction reparentTasks(@Nullable android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, @Nullable int[], @Nullable int[], boolean);
method @NonNull public android.window.WindowContainerTransaction scheduleFinishEnterPip(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setActivityWindowingMode(@NonNull android.window.WindowContainerToken, int);
- method @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken);
+ method @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken, boolean);
method @NonNull public android.window.WindowContainerTransaction setAdjacentTaskFragments(@NonNull android.os.IBinder, @Nullable android.os.IBinder, @Nullable android.window.WindowContainerTransaction.TaskFragmentAdjacentParams);
method @NonNull public android.window.WindowContainerTransaction setAppBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
method @NonNull public android.window.WindowContainerTransaction setBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect);
diff --git a/core/java/android/accessibilityservice/MagnificationConfig.java b/core/java/android/accessibilityservice/MagnificationConfig.java
index 8884508..74c91d6 100644
--- a/core/java/android/accessibilityservice/MagnificationConfig.java
+++ b/core/java/android/accessibilityservice/MagnificationConfig.java
@@ -16,6 +16,7 @@
package android.accessibilityservice;
+import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.Parcel;
@@ -29,20 +30,21 @@
* magnification.
*
* <p>
- * When the magnification config uses {@link #DEFAULT_MODE},
+ * When the magnification config uses {@link #MAGNIFICATION_MODE_DEFAULT},
* {@link AccessibilityService} will be able to control the activated magnifier on the display.
* If there is no magnifier activated, it controls the last-activated magnification mode.
* If there is no magnifier activated before, it controls full-screen magnifier by default.
* </p>
*
* <p>
- * When the magnification config uses {@link #FULLSCREEN_MODE}. {@link AccessibilityService} will
- * be able to control full-screen magnifier on the display.
+ * When the magnification config uses {@link #MAGNIFICATION_MODE_FULLSCREEN}.
+ * {@link AccessibilityService} will be able to control full-screen magnifier on the display.
* </p>
*
* <p>
- * When the magnification config uses {@link #WINDOW_MODE}. {@link AccessibilityService} will be
- * able to control the activated window magnifier on the display.
+ * When the magnification config uses {@link #MAGNIFICATION_MODE_WINDOW}.
+ * {@link AccessibilityService} will be able to control the activated window magnifier
+ * on the display.
* </p>
*
* <p>
@@ -54,22 +56,23 @@
public final class MagnificationConfig implements Parcelable {
/** The controlling magnification mode. It controls the activated magnifier. */
- public static final int DEFAULT_MODE = 0;
+ public static final int MAGNIFICATION_MODE_DEFAULT = 0;
/** The controlling magnification mode. It controls fullscreen magnifier. */
- public static final int FULLSCREEN_MODE = 1;
+ public static final int MAGNIFICATION_MODE_FULLSCREEN = 1;
/** The controlling magnification mode. It controls window magnifier. */
- public static final int WINDOW_MODE = 2;
+ public static final int MAGNIFICATION_MODE_WINDOW = 2;
+ /** @hide */
@IntDef(prefix = {"MAGNIFICATION_MODE"}, value = {
- DEFAULT_MODE,
- FULLSCREEN_MODE,
- WINDOW_MODE,
+ MAGNIFICATION_MODE_DEFAULT,
+ MAGNIFICATION_MODE_FULLSCREEN,
+ MAGNIFICATION_MODE_WINDOW,
})
@Retention(RetentionPolicy.SOURCE)
- @interface MAGNIFICATION_MODE {
+ @interface MagnificationMode {
}
- private int mMode = DEFAULT_MODE;
+ private int mMode = MAGNIFICATION_MODE_DEFAULT;
private float mScale = Float.NaN;
private float mCenterX = Float.NaN;
private float mCenterY = Float.NaN;
@@ -107,9 +110,9 @@
/**
* Returns the screen-relative X coordinate of the center of the magnification viewport.
*
- * @return the X coordinate. If the controlling magnifier is {@link #WINDOW_MODE} but not
- * enabled, it returns {@link Float#NaN}. If the controlling magnifier is {@link
- * #FULLSCREEN_MODE} but not enabled, it returns 0
+ * @return the X coordinate. If the controlling magnifier is {@link #MAGNIFICATION_MODE_WINDOW}
+ * but not enabled, it returns {@link Float#NaN}. If the controlling magnifier is {@link
+ * #MAGNIFICATION_MODE_FULLSCREEN} but not enabled, it returns 0
*/
public float getCenterX() {
return mCenterX;
@@ -118,9 +121,9 @@
/**
* Returns the screen-relative Y coordinate of the center of the magnification viewport.
*
- * @return the Y coordinate If the controlling magnifier is {@link #WINDOW_MODE} but not
- * enabled, it returns {@link Float#NaN}. If the controlling magnifier is {@link
- * #FULLSCREEN_MODE} but not enabled, it returns 0
+ * @return the Y coordinate If the controlling magnifier is {@link #MAGNIFICATION_MODE_WINDOW}
+ * but not enabled, it returns {@link Float#NaN}. If the controlling magnifier is {@link
+ * #MAGNIFICATION_MODE_FULLSCREEN} but not enabled, it returns 0
*/
public float getCenterY() {
return mCenterY;
@@ -159,7 +162,7 @@
*/
public static final class Builder {
- private int mMode = DEFAULT_MODE;
+ private int mMode = MAGNIFICATION_MODE_DEFAULT;
private float mScale = Float.NaN;
private float mCenterX = Float.NaN;
private float mCenterY = Float.NaN;
@@ -177,7 +180,7 @@
* @return This builder
*/
@NonNull
- public MagnificationConfig.Builder setMode(@MAGNIFICATION_MODE int mode) {
+ public MagnificationConfig.Builder setMode(@MagnificationMode int mode) {
mMode = mode;
return this;
}
@@ -185,20 +188,22 @@
/**
* Sets the magnification scale.
*
- * @param scale The magnification scale
+ * @param scale The magnification scale, in the range [1, 8]
* @return This builder
*/
@NonNull
- public MagnificationConfig.Builder setScale(float scale) {
+ public MagnificationConfig.Builder setScale(@FloatRange(from = 1f, to = 8f) float scale) {
mScale = scale;
return this;
}
/**
* Sets the X coordinate of the center of the magnification viewport.
+ * The controlling magnifier will apply the given position.
*
* @param centerX the screen-relative X coordinate around which to
- * center and scale, or {@link Float#NaN} to leave unchanged
+ * center and scale that is in the range [0, screenWidth],
+ * or {@link Float#NaN} to leave unchanged
* @return This builder
*/
@NonNull
@@ -209,9 +214,11 @@
/**
* Sets the Y coordinate of the center of the magnification viewport.
+ * The controlling magnifier will apply the given position.
*
* @param centerY the screen-relative Y coordinate around which to
- * center and scale, or {@link Float#NaN} to leave unchanged
+ * center and scale that is in the range [0, screenHeight],
+ * or {@link Float#NaN} to leave unchanged
* @return This builder
*/
@NonNull
diff --git a/core/java/android/app/usage/AppLaunchEstimateInfo.java b/core/java/android/app/usage/AppLaunchEstimateInfo.java
new file mode 100644
index 0000000..2085d00
--- /dev/null
+++ b/core/java/android/app/usage/AppLaunchEstimateInfo.java
@@ -0,0 +1,72 @@
+/*
+ * 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 android.app.usage;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A pair of {package, estimated launch time} to denote the estimated launch time for a given
+ * package.
+ * Used as a vehicle of data across the binder IPC.
+ *
+ * @hide
+ */
+public final class AppLaunchEstimateInfo implements Parcelable {
+
+ public final String packageName;
+ @CurrentTimeMillisLong
+ public final long estimatedLaunchTime;
+
+ private AppLaunchEstimateInfo(Parcel in) {
+ packageName = in.readString();
+ estimatedLaunchTime = in.readLong();
+ }
+
+ public AppLaunchEstimateInfo(String packageName,
+ @CurrentTimeMillisLong long estimatedLaunchTime) {
+ this.packageName = packageName;
+ this.estimatedLaunchTime = estimatedLaunchTime;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(packageName);
+ dest.writeLong(estimatedLaunchTime);
+ }
+
+ @NonNull
+ public static final Creator<AppLaunchEstimateInfo> CREATOR =
+ new Creator<AppLaunchEstimateInfo>() {
+ @Override
+ public AppLaunchEstimateInfo createFromParcel(Parcel source) {
+ return new AppLaunchEstimateInfo(source);
+ }
+
+ @Override
+ public AppLaunchEstimateInfo[] newArray(int size) {
+ return new AppLaunchEstimateInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index 585eb61..170d766 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -51,6 +51,8 @@
void setAppStandbyBucket(String packageName, int bucket, int userId);
ParceledListSlice getAppStandbyBuckets(String callingPackage, int userId);
void setAppStandbyBuckets(in ParceledListSlice appBuckets, int userId);
+ void setEstimatedLaunchTime(String packageName, long estimatedLaunchTime, int userId);
+ void setEstimatedLaunchTimes(in ParceledListSlice appLaunchTimes, int userId);
void registerAppUsageObserver(int observerId, in String[] packages, long timeLimitMs,
in PendingIntent callback, String callingPackage);
void unregisterAppUsageObserver(int observerId, String callingPackage);
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index ac7a318..3cbb24b 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -16,6 +16,7 @@
package android.app.usage;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -772,6 +773,72 @@
}
/**
+ * Changes an app's estimated launch time. An app is considered "launched" when a user opens
+ * one of its {@link android.app.Activity Activities}. The provided time is persisted across
+ * reboots and is used unless 1) the time is more than a week in the future and the platform
+ * thinks the app will be launched sooner, 2) the estimated time has passed. Passing in
+ * {@link Long#MAX_VALUE} effectively clears the previously set launch time for the app.
+ *
+ * @param packageName The package name of the app to set the bucket for.
+ * @param estimatedLaunchTime The next time the app is expected to be launched. Units are in
+ * milliseconds since epoch (the same as
+ * {@link System#currentTimeMillis()}).
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE)
+ public void setEstimatedLaunchTime(@NonNull String packageName,
+ @CurrentTimeMillisLong long estimatedLaunchTime) {
+ if (packageName == null) {
+ throw new NullPointerException("package name cannot be null");
+ }
+ if (estimatedLaunchTime <= 0) {
+ throw new IllegalArgumentException("estimated launch time must be positive");
+ }
+ try {
+ mService.setEstimatedLaunchTime(packageName, estimatedLaunchTime, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Changes the estimated launch times for multiple apps at once. The map is keyed by the
+ * package name and the value is the estimated launch time.
+ *
+ * @param estimatedLaunchTimes A map of package name to estimated launch time.
+ * @see #setEstimatedLaunchTime(String, long)
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE)
+ public void setEstimatedLaunchTimes(@NonNull Map<String, Long> estimatedLaunchTimes) {
+ if (estimatedLaunchTimes == null) {
+ throw new NullPointerException("estimatedLaunchTimes cannot be null");
+ }
+ final List<AppLaunchEstimateInfo> estimateList =
+ new ArrayList<>(estimatedLaunchTimes.size());
+ for (Map.Entry<String, Long> estimateEntry : estimatedLaunchTimes.entrySet()) {
+ final String pkgName = estimateEntry.getKey();
+ if (pkgName == null) {
+ throw new NullPointerException("package name cannot be null");
+ }
+ final Long estimatedLaunchTime = estimateEntry.getValue();
+ if (estimatedLaunchTime == null || estimatedLaunchTime <= 0) {
+ throw new IllegalArgumentException("estimated launch time must be positive");
+ }
+ estimateList.add(new AppLaunchEstimateInfo(pkgName, estimatedLaunchTime));
+ }
+ final ParceledListSlice<AppLaunchEstimateInfo> slice =
+ new ParceledListSlice<>(estimateList);
+ try {
+ mService.setEstimatedLaunchTimes(slice, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* @hide
* Register an app usage limit observer that receives a callback on the provided intent when
* the sum of usages of apps and tokens in the {@code observed} array exceeds the
diff --git a/core/java/android/bluetooth/BluetoothLeAudioCodecConfig.java b/core/java/android/bluetooth/BluetoothLeAudioCodecConfig.java
new file mode 100644
index 0000000..dcaf4b6
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothLeAudioCodecConfig.java
@@ -0,0 +1,129 @@
+/*
+ * 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 android.bluetooth;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Represents the codec configuration for a Bluetooth LE Audio source device.
+ * <p>Contains the source codec type.
+ * <p>The source codec type values are the same as those supported by the
+ * device hardware.
+ *
+ * {@see BluetoothLeAudioCodecConfig}
+ */
+public final class BluetoothLeAudioCodecConfig {
+ // Add an entry for each source codec here.
+
+ /** @hide */
+ @IntDef(prefix = "SOURCE_CODEC_TYPE_", value = {
+ SOURCE_CODEC_TYPE_LC3,
+ SOURCE_CODEC_TYPE_INVALID
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SourceCodecType {};
+
+ public static final int SOURCE_CODEC_TYPE_LC3 = 0;
+ public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;
+
+ /**
+ * Represents the count of valid source codec types. Can be accessed via
+ * {@link #getMaxCodecType}.
+ */
+ private static final int SOURCE_CODEC_TYPE_MAX = 1;
+
+ private final @SourceCodecType int mCodecType;
+
+ /**
+ * Creates a new BluetoothLeAudioCodecConfig.
+ *
+ * @param codecType the source codec type
+ */
+ private BluetoothLeAudioCodecConfig(@SourceCodecType int codecType) {
+ mCodecType = codecType;
+ }
+
+ @Override
+ public String toString() {
+ return "{codecName:" + getCodecName() + "}";
+ }
+
+ /**
+ * Gets the codec type.
+ *
+ * @return the codec type
+ */
+ public @SourceCodecType int getCodecType() {
+ return mCodecType;
+ }
+
+ /**
+ * Returns the valid codec types count.
+ */
+ public static int getMaxCodecType() {
+ return SOURCE_CODEC_TYPE_MAX;
+ }
+
+ /**
+ * Gets the codec name.
+ *
+ * @return the codec name
+ */
+ public @NonNull String getCodecName() {
+ switch (mCodecType) {
+ case SOURCE_CODEC_TYPE_LC3:
+ return "LC3";
+ case SOURCE_CODEC_TYPE_INVALID:
+ return "INVALID CODEC";
+ default:
+ break;
+ }
+ return "UNKNOWN CODEC(" + mCodecType + ")";
+ }
+
+ /**
+ * Builder for {@link BluetoothLeAudioCodecConfig}.
+ * <p> By default, the codec type will be set to
+ * {@link BluetoothLeAudioCodecConfig#SOURCE_CODEC_TYPE_INVALID}
+ */
+ public static final class Builder {
+ private int mCodecType = BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID;
+
+ /**
+ * Set codec type for Bluetooth codec config.
+ *
+ * @param codecType of this codec
+ * @return the same Builder instance
+ */
+ public @NonNull Builder setCodecType(@SourceCodecType int codecType) {
+ mCodecType = codecType;
+ return this;
+ }
+
+ /**
+ * Build {@link BluetoothLeAudioCodecConfig}.
+ * @return new BluetoothLeAudioCodecConfig built
+ */
+ public @NonNull BluetoothLeAudioCodecConfig build() {
+ return new BluetoothLeAudioCodecConfig(mCodecType);
+ }
+ }
+}
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index 1dc161c..6e1f8b5 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -119,18 +119,18 @@
* address, bonded devices are also searched among. This allows to obtain the necessary app
* privileges even if the device is already paired.
*/
- private boolean mSingleDevice = false;
+ private final boolean mSingleDevice;
/**
* If set, only devices matching either of the given filters will be shown to the user
*/
@DataClass.PluralOf("deviceFilter")
- private @NonNull List<DeviceFilter<?>> mDeviceFilters = new ArrayList<>();
+ private final @NonNull List<DeviceFilter<?>> mDeviceFilters;
/**
- * If set, association will be requested as a corresponding kind of device
+ * Profile of the device.
*/
- private @Nullable @DeviceProfile String mDeviceProfile = null;
+ private final @Nullable @DeviceProfile String mDeviceProfile;
/**
* The Display name of the device to be shown in the CDM confirmation UI. Must be non-null for
@@ -157,7 +157,7 @@
*
* @hide
*/
- private @Nullable String mCallingPackage = null;
+ private @Nullable String mCallingPackage;
/**
* The user-readable description of the device profile's privileges.
@@ -166,7 +166,7 @@
*
* @hide
*/
- private @Nullable String mDeviceProfilePrivilegesDescription = null;
+ private @Nullable String mDeviceProfilePrivilegesDescription;
/**
* The time at which his request was created
@@ -182,7 +182,22 @@
*
* @hide
*/
- private boolean mSkipPrompt = false;
+ private boolean mSkipPrompt;
+
+ /**
+ * @return profile of the companion device.
+ */
+ public @Nullable @DeviceProfile String getDeviceProfile() {
+ return mDeviceProfile;
+ }
+
+ /**
+ * The Display name of the device to be shown in the CDM confirmation UI. Must be non-null for
+ * "self-managed" association.
+ */
+ public @Nullable CharSequence getDisplayName() {
+ return mDisplayName;
+ }
/**
* Whether the association is to be managed by the companion application.
@@ -210,6 +225,17 @@
return mForceConfirmation;
}
+ /**
+ * Whether only a single device should match the provided filter.
+ *
+ * When scanning for a single device with a specific {@link BluetoothDeviceFilter} mac
+ * address, bonded devices are also searched among. This allows to obtain the necessary app
+ * privileges even if the device is already paired.
+ */
+ public boolean isSingleDevice() {
+ return mSingleDevice;
+ }
+
/** @hide */
public void setCallingPackage(@NonNull String pkg) {
mCallingPackage = pkg;
@@ -226,12 +252,6 @@
}
/** @hide */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public boolean isSingleDevice() {
- return mSingleDevice;
- }
-
- /** @hide */
@NonNull
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
public List<DeviceFilter<?>> getDeviceFilters() {
@@ -386,7 +406,7 @@
* @param deviceFilters
* If set, only devices matching either of the given filters will be shown to the user
* @param deviceProfile
- * If set, association will be requested as a corresponding kind of device
+ * Profile of the device.
* @param displayName
* The Display name of the device to be shown in the CDM confirmation UI. Must be non-null for
* "self-managed" association.
@@ -443,27 +463,6 @@
}
/**
- * If set, association will be requested as a corresponding kind of device
- *
- * @hide
- */
- @DataClass.Generated.Member
- public @Nullable @DeviceProfile String getDeviceProfile() {
- return mDeviceProfile;
- }
-
- /**
- * The Display name of the device to be shown in the CDM confirmation UI. Must be non-null for
- * "self-managed" association.
- *
- * @hide
- */
- @DataClass.Generated.Member
- public @Nullable CharSequence getDisplayName() {
- return mDisplayName;
- }
-
- /**
* The app package making the request.
*
* Populated by the system.
@@ -655,10 +654,10 @@
};
@DataClass.Generated(
- time = 1637228802427L,
+ time = 1638368698639L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/companion/AssociationRequest.java",
- inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\nprivate boolean mSingleDevice\nprivate @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate final @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate final boolean mSelfManaged\nprivate final boolean mForceConfirmation\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate long mCreationTime\nprivate boolean mSkipPrompt\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission boolean isSelfManaged()\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission boolean isForceConfirmation()\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic @android.compat.annotation.UnsupportedAppUsage boolean isSingleDevice()\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nprivate void onConstructed()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate boolean mSelfManaged\nprivate boolean mForceConfirmation\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDisplayName(java.lang.CharSequence)\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setSelfManaged(boolean)\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setForceConfirmation(boolean)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false, genConstDefs=false)")
+ inputSignatures = "public static final java.lang.String DEVICE_PROFILE_WATCH\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_APP_STREAMING\npublic static final @android.annotation.RequiresPermission @android.annotation.SystemApi java.lang.String DEVICE_PROFILE_AUTOMOTIVE_PROJECTION\nprivate final boolean mSingleDevice\nprivate final @com.android.internal.util.DataClass.PluralOf(\"deviceFilter\") @android.annotation.NonNull java.util.List<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate final @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String mDeviceProfile\nprivate final @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate final boolean mSelfManaged\nprivate final boolean mForceConfirmation\nprivate @android.annotation.Nullable java.lang.String mCallingPackage\nprivate @android.annotation.Nullable java.lang.String mDeviceProfilePrivilegesDescription\nprivate long mCreationTime\nprivate boolean mSkipPrompt\npublic @android.annotation.Nullable @android.companion.AssociationRequest.DeviceProfile java.lang.String getDeviceProfile()\npublic @android.annotation.Nullable java.lang.CharSequence getDisplayName()\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission boolean isSelfManaged()\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission boolean isForceConfirmation()\npublic boolean isSingleDevice()\npublic void setCallingPackage(java.lang.String)\npublic void setDeviceProfilePrivilegesDescription(java.lang.String)\npublic void setSkipPrompt(boolean)\npublic @android.annotation.NonNull @android.compat.annotation.UnsupportedAppUsage java.util.List<android.companion.DeviceFilter<?>> getDeviceFilters()\nprivate void onConstructed()\nclass AssociationRequest extends java.lang.Object implements [android.os.Parcelable]\nprivate boolean mSingleDevice\nprivate @android.annotation.Nullable java.util.ArrayList<android.companion.DeviceFilter<?>> mDeviceFilters\nprivate @android.annotation.Nullable java.lang.String mDeviceProfile\nprivate @android.annotation.Nullable java.lang.CharSequence mDisplayName\nprivate boolean mSelfManaged\nprivate boolean mForceConfirmation\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setSingleDevice(boolean)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder addDeviceFilter(android.companion.DeviceFilter<?>)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDeviceProfile(java.lang.String)\npublic @android.annotation.NonNull android.companion.AssociationRequest.Builder setDisplayName(java.lang.CharSequence)\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setSelfManaged(boolean)\npublic @android.annotation.SystemApi @android.annotation.RequiresPermission @android.annotation.NonNull android.companion.AssociationRequest.Builder setForceConfirmation(boolean)\npublic @android.annotation.NonNull @java.lang.Override android.companion.AssociationRequest build()\nclass Builder extends android.provider.OneTimeUseBuilder<android.companion.AssociationRequest> implements []\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true, genHiddenGetters=true, genParcelable=true, genHiddenConstructor=true, genBuilder=false, genConstDefs=false)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/hardware/DataSpace.java b/core/java/android/hardware/DataSpace.java
new file mode 100644
index 0000000..65383c5
--- /dev/null
+++ b/core/java/android/hardware/DataSpace.java
@@ -0,0 +1,649 @@
+/*
+ * Copyright 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 android.hardware;
+
+import android.annotation.LongDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * DataSpace identifies three components of colors - standard (primaries), transfer and range.
+ *
+ * <p>A DataSpace describes how buffer data, such as from an {@link android.media.Image Image}
+ * or a {@link android.hardware.HardwareBuffer HardwareBuffer}
+ * should be interpreted by both applications and typical hardware.</p>
+ *
+ * <p>As buffer information is not guaranteed to be representative of color information,
+ * while DataSpace is typically used to describe three aspects of interpreting colors,
+ * some DataSpaces may describe other typical interpretations of buffer data
+ * such as depth information.</p>
+ *
+ * <p>Note that while {@link android.graphics.ColorSpace ColorSpace} and {@code DataSpace}
+ * are similar concepts, they are not equivalent. Not all ColorSpaces,
+ * such as {@link android.graphics.ColorSpace.Named#ACES ColorSpace.Named.ACES},
+ * are able to be understood by typical hardware blocks so they cannot be DataSpaces.</p>
+ *
+ * <h3>Standard aspect</h3>
+ *
+ * <p>Defines the chromaticity coordinates of the source primaries in terms of
+ * the CIE 1931 definition of x and y specified in ISO 11664-1.</p>
+ *
+ * <h3>Transfer aspect</h3>
+ *
+ * <p>Transfer characteristics are the opto-electronic transfer characteristic
+ * at the source as a function of linear optical intensity (luminance).</p>
+ *
+ * <p>For digital signals, E corresponds to the recorded value. Normally, the
+ * transfer function is applied in RGB space to each of the R, G and B
+ * components independently. This may result in color shift that can be
+ * minized by applying the transfer function in Lab space only for the L
+ * component. Implementation may apply the transfer function in RGB space
+ * for all pixel formats if desired.</p>
+ *
+ * <h3>Range aspect</h3>
+ *
+ * <p>Defines the range of values corresponding to the unit range of {@code 0-1}.</p>
+ */
+
+public final class DataSpace {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @LongDef(flag = true, value = {
+ STANDARD_UNSPECIFIED,
+ STANDARD_BT709,
+ STANDARD_BT601_625,
+ STANDARD_BT601_625_UNADJUSTED,
+ STANDARD_BT601_525,
+ STANDARD_BT601_525_UNADJUSTED,
+ STANDARD_BT2020,
+ STANDARD_BT2020_CONSTANT_LUMINANCE,
+ STANDARD_BT470M,
+ STANDARD_FILM,
+ STANDARD_DCI_P3,
+ STANDARD_ADOBE_RGB
+ })
+ public @interface DataSpaceStandard {};
+
+ private static final long STANDARD_MASK = 63 << 16;
+
+ /**
+ * Chromacity coordinates are unknown or are determined by the application.
+ */
+ public static final long STANDARD_UNSPECIFIED = 0 << 16;
+ /**
+ * Use the unadjusted {@code KR = 0.2126}, {@code KB = 0.0722} luminance interpretation
+ * for RGB conversion.
+ *
+ * <pre>
+ * Primaries: x y
+ * green 0.300 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290 </pre>
+ */
+ public static final long STANDARD_BT709 = 1 << 16;
+ /**
+ * Use the adjusted {@code KR = 0.299}, {@code KB = 0.114} luminance interpretation
+ * for RGB conversion from the one purely determined by the primaries
+ * to minimize the color shift into RGB space that uses BT.709
+ * primaries.
+ *
+ * <pre>
+ * Primaries: x y
+ * green 0.290 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290 </pre>
+ */
+ public static final long STANDARD_BT601_625 = 2 << 16;
+ /**
+ * Use the unadjusted {@code KR = 0.222}, {@code KB = 0.071} luminance interpretation
+ * for RGB conversion.
+ *
+ * <pre>
+ * Primaries: x y
+ * green 0.290 0.600
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290 </pre>
+ */
+ public static final long STANDARD_BT601_625_UNADJUSTED = 3 << 16;
+ /**
+ * Use the adjusted {@code KR = 0.299}, {@code KB = 0.114} luminance interpretation
+ * for RGB conversion from the one purely determined by the primaries
+ * to minimize the color shift into RGB space that uses BT.709
+ * primaries.
+ *
+ * <pre>
+ * Primaries: x y
+ * green 0.310 0.595
+ * blue 0.155 0.070
+ * red 0.630 0.340
+ * white (D65) 0.3127 0.3290 </pre>
+ */
+ public static final long STANDARD_BT601_525 = 4 << 16;
+ /**
+ * Use the unadjusted {@code KR = 0.212}, {@code KB = 0.087} luminance interpretation
+ * for RGB conversion (as in SMPTE 240M).
+ *
+ * <pre>
+ * Primaries: x y
+ * green 0.310 0.595
+ * blue 0.155 0.070
+ * red 0.630 0.340
+ * white (D65) 0.3127 0.3290 </pre>
+ */
+ public static final long STANDARD_BT601_525_UNADJUSTED = 5 << 16;
+ /**
+ * Use the unadjusted {@code KR = 0.2627}, {@code KB = 0.0593} luminance interpretation
+ * for RGB conversion.
+ *
+ * <pre>
+ * Primaries: x y
+ * green 0.170 0.797
+ * blue 0.131 0.046
+ * red 0.708 0.292
+ * white (D65) 0.3127 0.3290 </pre>
+ */
+ public static final long STANDARD_BT2020 = 6 << 16;
+ /**
+ * Use the unadjusted {@code KR = 0.2627}, {@code KB = 0.0593} luminance interpretation
+ * for RGB conversion using the linear domain.
+ *
+ * <pre>
+ * Primaries: x y
+ * green 0.170 0.797
+ * blue 0.131 0.046
+ * red 0.708 0.292
+ * white (D65) 0.3127 0.3290 </pre>
+ */
+ public static final long STANDARD_BT2020_CONSTANT_LUMINANCE = 7 << 16;
+ /**
+ * Use the unadjusted {@code KR = 0.30}, {@code KB = 0.11} luminance interpretation
+ * for RGB conversion.
+ *
+ * <pre>
+ * Primaries: x y
+ * green 0.21 0.71
+ * blue 0.14 0.08
+ * red 0.67 0.33
+ * white (C) 0.310 0.316 </pre>
+ */
+ public static final long STANDARD_BT470M = 8 << 16;
+ /**
+ * Use the unadjusted {@code KR = 0.254}, {@code KB = 0.068} luminance interpretation
+ * for RGB conversion.
+ *
+ * <pre>
+ * Primaries: x y
+ * green 0.243 0.692
+ * blue 0.145 0.049
+ * red 0.681 0.319
+ * white (C) 0.310 0.316 </pre>
+ */
+ public static final long STANDARD_FILM = 9 << 16;
+ /**
+ * SMPTE EG 432-1 and SMPTE RP 431-2.
+ *
+ * <pre>
+ * Primaries: x y
+ * green 0.265 0.690
+ * blue 0.150 0.060
+ * red 0.680 0.320
+ * white (D65) 0.3127 0.3290 </pre>
+ */
+ public static final long STANDARD_DCI_P3 = 10 << 16;
+ /**
+ * Adobe RGB primaries.
+ *
+ * <pre>
+ * Primaries: x y
+ * green 0.210 0.710
+ * blue 0.150 0.060
+ * red 0.640 0.330
+ * white (D65) 0.3127 0.3290 </pre>
+ */
+ public static final long STANDARD_ADOBE_RGB = 11 << 16;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @LongDef(flag = true, value = {
+ TRANSFER_UNSPECIFIED,
+ TRANSFER_LINEAR,
+ TRANSFER_SRGB,
+ TRANSFER_SMPTE_170M,
+ TRANSFER_GAMMA2_2,
+ TRANSFER_GAMMA2_6,
+ TRANSFER_GAMMA2_8,
+ TRANSFER_ST2084,
+ TRANSFER_HLG
+ })
+ public @interface DataSpaceTransfer {};
+
+ private static final long TRANSFER_MASK = 31 << 22;
+
+ /**
+ * Transfer characteristics are unknown or are determined by the
+ * application.
+ */
+ public static final long TRANSFER_UNSPECIFIED = 0 << 22;
+ /**
+ * Linear transfer.
+ *
+ * <pre>{@code
+ * Transfer characteristic curve:
+ * E = L
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal}</pre>
+ */
+ public static final long TRANSFER_LINEAR = 1 << 22;
+ /**
+ * sRGB transfer.
+ *
+ * <pre>{@code
+ * Transfer characteristic curve:
+ * E = 1.055 * L^(1/2.4) - 0.055 for 0.0031308 <= L <= 1
+ * = 12.92 * L for 0 <= L < 0.0031308
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal}</pre>
+ *
+ * Use for RGB formats.
+ */
+ public static final long TRANSFER_SRGB = 2 << 22;
+ /**
+ * SMPTE 170M transfer.
+ *
+ * <pre>{@code
+ * Transfer characteristic curve:
+ * E = 1.099 * L ^ 0.45 - 0.099 for 0.018 <= L <= 1
+ * = 4.500 * L for 0 <= L < 0.018
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal}</pre>
+ *
+ * Use for YCbCr formats.
+ */
+ public static final long TRANSFER_SMPTE_170M = 3 << 22;
+ /**
+ * Display gamma 2.2.
+ *
+ * <pre>{@code
+ * Transfer characteristic curve:
+ * E = L ^ (1/2.2)
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal}</pre>
+ */
+ public static final long TRANSFER_GAMMA2_2 = 4 << 22;
+ /**
+ * Display gamma 2.6.
+ *
+ * <pre>{@code
+ * Transfer characteristic curve:
+ * E = L ^ (1/2.6)
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal}</pre>
+ */
+ public static final long TRANSFER_GAMMA2_6 = 5 << 22;
+ /**
+ * Display gamma 2.8.
+ *
+ * <pre>{@code
+ * Transfer characteristic curve:
+ * E = L ^ (1/2.8)
+ * L - luminance of image 0 <= L <= 1 for conventional colorimetry
+ * E - corresponding electrical signal}</pre>
+ */
+ public static final long TRANSFER_GAMMA2_8 = 6 << 22;
+ /**
+ * SMPTE ST 2084 (Dolby Perceptual Quantizer).
+ *
+ * <pre>{@code
+ * Transfer characteristic curve:
+ * E = ((c1 + c2 * L^n) / (1 + c3 * L^n)) ^ m
+ * c1 = c3 - c2 + 1 = 3424 / 4096 = 0.8359375
+ * c2 = 32 * 2413 / 4096 = 18.8515625
+ * c3 = 32 * 2392 / 4096 = 18.6875
+ * m = 128 * 2523 / 4096 = 78.84375
+ * n = 0.25 * 2610 / 4096 = 0.1593017578125
+ * L - luminance of image 0 <= L <= 1 for HDR colorimetry.
+ * L = 1 corresponds to 10000 cd/m2
+ * E - corresponding electrical signal}</pre>
+ */
+ public static final long TRANSFER_ST2084 = 7 << 22;
+ /**
+ * ARIB STD-B67 Hybrid Log Gamma.
+ *
+ * <pre>{@code
+ * Transfer characteristic curve:
+ * E = r * L^0.5 for 0 <= L <= 1
+ * = a * ln(L - b) + c for 1 < L
+ * a = 0.17883277
+ * b = 0.28466892
+ * c = 0.55991073
+ * r = 0.5
+ * L - luminance of image 0 <= L for HDR colorimetry. L = 1 corresponds
+ * to reference white level of 100 cd/m2
+ * E - corresponding electrical signal}</pre>
+ */
+ public static final long TRANSFER_HLG = 8 << 22;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @LongDef(flag = true, value = {
+ RANGE_UNSPECIFIED,
+ RANGE_FULL,
+ RANGE_LIMITED,
+ RANGE_EXTENDED
+ })
+ public @interface DataSpaceRange {};
+
+ private static final long RANGE_MASK = 7 << 27;
+
+ /**
+ * Range characteristics are unknown or are determined by the application.
+ */
+ public static final long RANGE_UNSPECIFIED = 0 << 27;
+ /**
+ * Full range uses all values for Y, Cb and Cr from
+ * {@code 0} to {@code 2^b-1}, where b is the bit depth of the color format.
+ */
+ public static final long RANGE_FULL = 1 << 27;
+ /**
+ * Limited range uses values {@code 16/256*2^b} to {@code 235/256*2^b} for Y, and
+ * {@code 1/16*2^b} to {@code 15/16*2^b} for Cb, Cr, R, G and B, where b is the bit depth of
+ * the color format.
+ *
+ * <p>E.g. For 8-bit-depth formats:
+ * Luma (Y) samples should range from 16 to 235, inclusive
+ * Chroma (Cb, Cr) samples should range from 16 to 240, inclusive
+ *
+ * For 10-bit-depth formats:
+ * Luma (Y) samples should range from 64 to 940, inclusive
+ * Chroma (Cb, Cr) samples should range from 64 to 960, inclusive. </p>
+ */
+ public static final long RANGE_LIMITED = 2 << 27;
+ /**
+ * Extended range is used for scRGB only.
+ *
+ * <p>Intended for use with floating point pixel formats. [0.0 - 1.0] is the standard
+ * sRGB space. Values outside the range [0.0 - 1.0] can encode
+ * color outside the sRGB gamut. [-0.5, 7.5] is the scRGB range.
+ * Used to blend/merge multiple dataspaces on a single display.</p>
+ */
+ public static final long RANGE_EXTENDED = 3 << 27;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @LongDef(flag = true, value = {
+ DATASPACE_UNKNOWN,
+ DATASPACE_SCRGB_LINEAR,
+ DATASPACE_SRGB,
+ DATASPACE_SCRGB,
+ DATASPACE_DISPLAY_P3,
+ DATASPACE_BT2020_PQ,
+ DATASPACE_ADOBE_RGB,
+ DATASPACE_JFIF,
+ DATASPACE_BT601_625,
+ DATASPACE_BT601_525,
+ DATASPACE_BT2020,
+ DATASPACE_BT709,
+ DATASPACE_DCI_P3,
+ DATASPACE_SRGB_LINEAR
+ })
+ public @interface NamedDataSpace {};
+
+ /**
+ * Default-assumption data space, when not explicitly specified.
+ *
+ * <p>It is safest to assume a buffer is an image with sRGB primaries and
+ * encoding ranges, but the consumer and/or the producer of the data may
+ * simply be using defaults. No automatic gamma transform should be
+ * expected, except for a possible display gamma transform when drawn to a
+ * screen.</p>
+ */
+ public static final long DATASPACE_UNKNOWN = 0;
+ /**
+ * scRGB linear encoding.
+ *
+ * <p>Composed of the following -</p>
+ * <pre>
+ * Primaries: STANDARD_BT709
+ * Transfer: TRANSFER_LINEAR
+ * Range: RANGE_EXTENDED</pre>
+ *
+ * The values are floating point.
+ * A pixel value of 1.0, 1.0, 1.0 corresponds to sRGB white (D65) at 80 nits.
+ * Values beyond the range [0.0 - 1.0] would correspond to other colors
+ * spaces and/or HDR content.
+ */
+ public static final long DATASPACE_SCRGB_LINEAR = 406913024;
+ /**
+ * sRGB gamma encoding.
+ *
+ * <p>Composed of the following -</p>
+ * <pre>
+ * Primaries: STANDARD_BT709
+ * Transfer: TRANSFER_SRGB
+ * Range: RANGE_FULL</pre>
+ *
+ * When written, the inverse transformation is performed.
+ *
+ * The alpha component, if present, is always stored in linear space and
+ * is left unmodified when read or written.
+ */
+ public static final long DATASPACE_SRGB = 142671872;
+ /**
+ * scRGB gamma encoding.
+ *
+ * <p>Composed of the following -</p>
+ * <pre>
+ * Primaries: STANDARD_BT709
+ * Transfer: TRANSFER_SRGB
+ * Range: RANGE_EXTENDED</pre>
+ *
+ * The values are floating point.
+ *
+ * A pixel value of 1.0, 1.0, 1.0 corresponds to sRGB white (D65) at 80 nits.
+ * Values beyond the range [0.0 - 1.0] would correspond to other colors
+ * spaces and/or HDR content.
+ */
+ public static final long DATASPACE_SCRGB = 411107328;
+ /**
+ * Display P3 encoding.
+ *
+ * <p>Composed of the following -</p>
+ * <pre>
+ * Primaries: STANDARD_DCI_P3
+ * Transfer: TRANSFER_SRGB
+ * Range: RANGE_FULL</pre>
+ */
+ public static final long DATASPACE_DISPLAY_P3 = 143261696;
+ /**
+ * ITU-R Recommendation 2020 (BT.2020)
+ *
+ * Ultra High-definition television.
+ *
+ * <p>Composed of the following -</p>
+ * <pre>
+ * Primaries: STANDARD_BT2020
+ * Transfer: TRANSFER_ST2084
+ * Range: RANGE_FULL</pre>
+ */
+ public static final long DATASPACE_BT2020_PQ = 163971072;
+ /**
+ * Adobe RGB encoding.
+ *
+ * <p>Composed of the following -</p>
+ * <pre>
+ * Primaries: STANDARD_ADOBE_RGB
+ * Transfer: TRANSFER_GAMMA2_2
+ * Range: RANGE_FULL</pre>
+ *
+ * Note: Application is responsible for gamma encoding the data.
+ */
+ public static final long DATASPACE_ADOBE_RGB = 151715840;
+ /**
+ * JPEG File Interchange Format (JFIF).
+ *
+ * <p>Composed of the following -</p>
+ * <pre>
+ * Primaries: STANDARD_BT601_625
+ * Transfer: TRANSFER_SMPTE_170M
+ * Range: RANGE_FULL</pre>
+ *
+ * Same model as BT.601-625, but all values (Y, Cb, Cr) range from {@code 0} to {@code 255}
+ */
+ public static final long DATASPACE_JFIF = 146931712;
+ /**
+ * ITU-R Recommendation 601 (BT.601) - 525-line
+ *
+ * Standard-definition television, 525 Lines (NTSC).
+ *
+ * <p>Composed of the following -</p>
+ * <pre>
+ * Primaries: STANDARD_BT601_625
+ * Transfer: TRANSFER_SMPTE_170M
+ * Range: RANGE_LIMITED</pre>
+ */
+ public static final long DATASPACE_BT601_625 = 281149440;
+ /**
+ * ITU-R Recommendation 709 (BT.709)
+ *
+ * High-definition television.
+ *
+ * <p>Composed of the following -</p>
+ * <pre>
+ * Primaries: STANDARD_BT601_525
+ * Transfer: TRANSFER_SMPTE_170M
+ * Range: RANGE_LIMITED</pre>
+ */
+ public static final long DATASPACE_BT601_525 = 281280512;
+ /**
+ * ITU-R Recommendation 2020 (BT.2020)
+ *
+ * Ultra High-definition television.
+ *
+ * <p>Composed of the following -</p>
+ * <pre>
+ * Primaries: STANDARD_BT2020
+ * Transfer: TRANSFER_SMPTE_170M
+ * Range: RANGE_FULL</pre>
+ */
+ public static final long DATASPACE_BT2020 = 147193856;
+ /**
+ * ITU-R Recommendation 709 (BT.709)
+ *
+ * High-definition television.
+ *
+ * <p>Composed of the following -</p>
+ * <pre>
+ * Primaries: STANDARD_BT709
+ * Transfer: TRANSFER_SMPTE_170M
+ * Range: RANGE_LIMITED</pre>
+ */
+ public static final long DATASPACE_BT709 = 281083904;
+ /**
+ * SMPTE EG 432-1 and SMPTE RP 431-2
+ *
+ * Digital Cinema DCI-P3.
+ *
+ * <p>Composed of the following -</p>
+ * <pre>
+ * Primaries: STANDARD_DCI_P3
+ * Transfer: TRANSFER_GAMMA2_6
+ * Range: RANGE_FULL</pre>
+ *
+ * Note: Application is responsible for gamma encoding the data as
+ * a 2.6 gamma encoding is not supported in HW.
+ */
+ public static final long DATASPACE_DCI_P3 = 155844608;
+ /**
+ * sRGB linear encoding.
+ *
+ * <p>Composed of the following -</p>
+ * <pre>
+ * Primaries: STANDARD_BT709
+ * Transfer: TRANSFER_LINEAR
+ * Range: RANGE_FULL</pre>
+ *
+ * The values are encoded using the full range ([0,255] for 8-bit) for all
+ * components.
+ */
+ public static final long DATASPACE_SRGB_LINEAR = 138477568;
+
+ private DataSpace() {}
+
+ /**
+ * Pack the dataSpace value using standard, transfer and range field value.
+ * Field values should be in the correct bits place.
+ *
+ * @param standard Chromaticity coordinates of source primaries
+ * @param transfer Opto-electronic transfer characteristic at the source
+ * @param range The range of values
+ *
+ * @return The long dataspace packed by standard, transfer and range value
+ */
+ public static @NamedDataSpace long pack(@DataSpaceStandard long standard,
+ @DataSpaceTransfer long transfer,
+ @DataSpaceRange long range) {
+ if ((standard & STANDARD_MASK) != standard) {
+ throw new IllegalArgumentException("Invalid standard " + standard);
+ }
+ if ((transfer & TRANSFER_MASK) != transfer) {
+ throw new IllegalArgumentException("Invalid transfer " + transfer);
+ }
+ if ((range & RANGE_MASK) != range) {
+ throw new IllegalArgumentException("Invalid range " + range);
+ }
+ return standard | transfer | range;
+ }
+
+ /**
+ * Unpack the standard field value from the packed dataSpace value.
+ *
+ * @param dataSpace The packed dataspace value
+ *
+ * @return The standard aspect
+ */
+ public static @DataSpaceStandard long getStandard(@NamedDataSpace long dataSpace) {
+ @DataSpaceStandard long standard = dataSpace & STANDARD_MASK;
+ return standard;
+ }
+
+ /**
+ * Unpack the transfer field value from the packed dataSpace value
+ *
+ * @param dataSpace The packed dataspace value
+ *
+ * @return The transfer aspect
+ */
+ public static @DataSpaceTransfer long getTransfer(@NamedDataSpace long dataSpace) {
+ @DataSpaceTransfer long transfer = dataSpace & TRANSFER_MASK;
+ return transfer;
+ }
+
+ /**
+ * Unpack the range field value from the packed dataSpace value
+ *
+ * @param dataSpace The packed dataspace value
+ *
+ * @return The range aspect
+ */
+ public static @DataSpaceRange long getRange(@NamedDataSpace long dataSpace) {
+ @DataSpaceRange long range = dataSpace & RANGE_MASK;
+ return range;
+ }
+}
diff --git a/core/java/android/hardware/fingerprint/FingerprintStateListener.java b/core/java/android/hardware/fingerprint/FingerprintStateListener.java
index cf914c5..551f512 100644
--- a/core/java/android/hardware/fingerprint/FingerprintStateListener.java
+++ b/core/java/android/hardware/fingerprint/FingerprintStateListener.java
@@ -49,10 +49,10 @@
* Defines behavior in response to state update
* @param newState new state of fingerprint sensor
*/
- public void onStateChanged(@FingerprintStateListener.State int newState) {};
+ public void onStateChanged(@FingerprintStateListener.State int newState) {}
/**
* Invoked when enrollment state changes for the specified user
*/
- public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {};
+ public void onEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {}
}
diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java
new file mode 100644
index 0000000..6b33e4f
--- /dev/null
+++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkPriority.java
@@ -0,0 +1,291 @@
+/*
+ * 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 android.net.vcn;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_DESERIALIZER;
+import static com.android.server.vcn.util.PersistableBundleUtils.INTEGER_SERIALIZER;
+import static com.android.server.vcn.util.PersistableBundleUtils.STRING_DESERIALIZER;
+import static com.android.server.vcn.util.PersistableBundleUtils.STRING_SERIALIZER;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.PersistableBundle;
+import android.telephony.SubscriptionInfo;
+import android.telephony.TelephonyManager;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.vcn.util.PersistableBundleUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
+
+// TODO: Add documents
+/** @hide */
+public final class VcnCellUnderlyingNetworkPriority extends VcnUnderlyingNetworkPriority {
+ private static final String ALLOWED_NETWORK_PLMN_IDS_KEY = "mAllowedNetworkPlmnIds";
+ @NonNull private final Set<String> mAllowedNetworkPlmnIds;
+ private static final String ALLOWED_SPECIFIC_CARRIER_IDS_KEY = "mAllowedSpecificCarrierIds";
+ @NonNull private final Set<Integer> mAllowedSpecificCarrierIds;
+
+ private static final String ALLOW_ROAMING_KEY = "mAllowRoaming";
+ private final boolean mAllowRoaming;
+
+ private static final String REQUIRE_OPPORTUNISTIC_KEY = "mRequireOpportunistic";
+ private final boolean mRequireOpportunistic;
+
+ private VcnCellUnderlyingNetworkPriority(
+ int networkQuality,
+ boolean allowMetered,
+ Set<String> allowedNetworkPlmnIds,
+ Set<Integer> allowedSpecificCarrierIds,
+ boolean allowRoaming,
+ boolean requireOpportunistic) {
+ super(NETWORK_PRIORITY_TYPE_CELL, networkQuality, allowMetered);
+ mAllowedNetworkPlmnIds = new ArraySet<>(allowedNetworkPlmnIds);
+ mAllowedSpecificCarrierIds = new ArraySet<>(allowedSpecificCarrierIds);
+ mAllowRoaming = allowRoaming;
+ mRequireOpportunistic = requireOpportunistic;
+
+ validate();
+ }
+
+ /** @hide */
+ @Override
+ protected void validate() {
+ super.validate();
+ validatePlmnIds(mAllowedNetworkPlmnIds);
+ Objects.requireNonNull(mAllowedSpecificCarrierIds, "allowedCarrierIds is null");
+ }
+
+ private static void validatePlmnIds(Set<String> allowedNetworkPlmnIds) {
+ Objects.requireNonNull(allowedNetworkPlmnIds, "allowedNetworkPlmnIds is null");
+
+ // A valid PLMN is a concatenation of MNC and MCC, and thus consists of 5 or 6 decimal
+ // digits.
+ for (String id : allowedNetworkPlmnIds) {
+ if ((id.length() == 5 || id.length() == 6) && id.matches("[0-9]+")) {
+ continue;
+ } else {
+ throw new IllegalArgumentException("Found invalid PLMN ID: " + id);
+ }
+ }
+ }
+
+ /** @hide */
+ @NonNull
+ @VisibleForTesting(visibility = Visibility.PROTECTED)
+ public static VcnCellUnderlyingNetworkPriority fromPersistableBundle(
+ @NonNull PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle is null");
+
+ final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
+ final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY);
+
+ final PersistableBundle plmnIdsBundle =
+ in.getPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY);
+ Objects.requireNonNull(plmnIdsBundle, "plmnIdsBundle is null");
+ final Set<String> allowedNetworkPlmnIds =
+ new ArraySet<String>(
+ PersistableBundleUtils.toList(plmnIdsBundle, STRING_DESERIALIZER));
+
+ final PersistableBundle specificCarrierIdsBundle =
+ in.getPersistableBundle(ALLOWED_SPECIFIC_CARRIER_IDS_KEY);
+ Objects.requireNonNull(specificCarrierIdsBundle, "specificCarrierIdsBundle is null");
+ final Set<Integer> allowedSpecificCarrierIds =
+ new ArraySet<Integer>(
+ PersistableBundleUtils.toList(
+ specificCarrierIdsBundle, INTEGER_DESERIALIZER));
+
+ final boolean allowRoaming = in.getBoolean(ALLOW_ROAMING_KEY);
+ final boolean requireOpportunistic = in.getBoolean(REQUIRE_OPPORTUNISTIC_KEY);
+
+ return new VcnCellUnderlyingNetworkPriority(
+ networkQuality,
+ allowMetered,
+ allowedNetworkPlmnIds,
+ allowedSpecificCarrierIds,
+ allowRoaming,
+ requireOpportunistic);
+ }
+
+ /** @hide */
+ @Override
+ @NonNull
+ @VisibleForTesting(visibility = Visibility.PROTECTED)
+ public PersistableBundle toPersistableBundle() {
+ final PersistableBundle result = super.toPersistableBundle();
+
+ final PersistableBundle plmnIdsBundle =
+ PersistableBundleUtils.fromList(
+ new ArrayList<>(mAllowedNetworkPlmnIds), STRING_SERIALIZER);
+ result.putPersistableBundle(ALLOWED_NETWORK_PLMN_IDS_KEY, plmnIdsBundle);
+
+ final PersistableBundle specificCarrierIdsBundle =
+ PersistableBundleUtils.fromList(
+ new ArrayList<>(mAllowedSpecificCarrierIds), INTEGER_SERIALIZER);
+ result.putPersistableBundle(ALLOWED_SPECIFIC_CARRIER_IDS_KEY, specificCarrierIdsBundle);
+
+ result.putBoolean(ALLOW_ROAMING_KEY, mAllowRoaming);
+ result.putBoolean(REQUIRE_OPPORTUNISTIC_KEY, mRequireOpportunistic);
+
+ return result;
+ }
+
+ /** Retrieve the allowed PLMN IDs, or an empty set if any PLMN ID is acceptable. */
+ @NonNull
+ public Set<String> getAllowedPlmnIds() {
+ return Collections.unmodifiableSet(mAllowedNetworkPlmnIds);
+ }
+
+ /**
+ * Retrieve the allowed specific carrier IDs, or an empty set if any specific carrier ID is
+ * acceptable.
+ */
+ @NonNull
+ public Set<Integer> getAllowedSpecificCarrierIds() {
+ return Collections.unmodifiableSet(mAllowedSpecificCarrierIds);
+ }
+
+ /** Return if roaming is allowed. */
+ public boolean allowRoaming() {
+ return mAllowRoaming;
+ }
+
+ /** Return if requiring an opportunistic network. */
+ public boolean requireOpportunistic() {
+ return mRequireOpportunistic;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ super.hashCode(),
+ mAllowedNetworkPlmnIds,
+ mAllowedSpecificCarrierIds,
+ mAllowRoaming,
+ mRequireOpportunistic);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!super.equals(other)) {
+ return false;
+ }
+
+ if (!(other instanceof VcnCellUnderlyingNetworkPriority)) {
+ return false;
+ }
+
+ final VcnCellUnderlyingNetworkPriority rhs = (VcnCellUnderlyingNetworkPriority) other;
+ return Objects.equals(mAllowedNetworkPlmnIds, rhs.mAllowedNetworkPlmnIds)
+ && Objects.equals(mAllowedSpecificCarrierIds, rhs.mAllowedSpecificCarrierIds)
+ && mAllowRoaming == rhs.mAllowRoaming
+ && mRequireOpportunistic == rhs.mRequireOpportunistic;
+ }
+
+ /** This class is used to incrementally build WifiNetworkPriority objects. */
+ public static class Builder extends VcnUnderlyingNetworkPriority.Builder<Builder> {
+ @NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>();
+ @NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>();
+
+ private boolean mAllowRoaming = false;
+ private boolean mRequireOpportunistic = false;
+
+ /** Construct a Builder object. */
+ public Builder() {}
+
+ /**
+ * Set allowed operator PLMN IDs.
+ *
+ * <p>This is used to distinguish cases where roaming agreements may dictate a different
+ * priority from a partner's networks.
+ *
+ * @param allowedNetworkPlmnIds the allowed operator PLMN IDs in String. Defaults to an
+ * empty set, allowing ANY PLMN ID. A valid PLMN is a concatenation of MNC and MCC, and
+ * thus consists of 5 or 6 decimal digits. See {@link SubscriptionInfo#getMccString()}
+ * and {@link SubscriptionInfo#getMncString()}.
+ */
+ @NonNull
+ public Builder setAllowedPlmnIds(@NonNull Set<String> allowedNetworkPlmnIds) {
+ validatePlmnIds(allowedNetworkPlmnIds);
+
+ mAllowedNetworkPlmnIds.clear();
+ mAllowedNetworkPlmnIds.addAll(allowedNetworkPlmnIds);
+ return this;
+ }
+
+ /**
+ * Set allowed specific carrier IDs.
+ *
+ * @param allowedSpecificCarrierIds the allowed specific carrier IDs. Defaults to an empty
+ * set, allowing ANY carrier ID. See {@link TelephonyManager#getSimSpecificCarrierId()}.
+ */
+ @NonNull
+ public Builder setAllowedSpecificCarrierIds(
+ @NonNull Set<Integer> allowedSpecificCarrierIds) {
+ Objects.requireNonNull(allowedSpecificCarrierIds, "allowedCarrierIds is null");
+ mAllowedSpecificCarrierIds.clear();
+ mAllowedSpecificCarrierIds.addAll(allowedSpecificCarrierIds);
+ return this;
+ }
+
+ /**
+ * Set if roaming is allowed.
+ *
+ * @param allowRoaming the flag to indicate if roaming is allowed. Defaults to {@code
+ * false}.
+ */
+ @NonNull
+ public Builder setAllowRoaming(boolean allowRoaming) {
+ mAllowRoaming = allowRoaming;
+ return this;
+ }
+
+ /**
+ * Set if requiring an opportunistic network.
+ *
+ * @param requireOpportunistic the flag to indicate if caller requires an opportunistic
+ * network. Defaults to {@code false}.
+ */
+ @NonNull
+ public Builder setRequireOpportunistic(boolean requireOpportunistic) {
+ mRequireOpportunistic = requireOpportunistic;
+ return this;
+ }
+
+ /** Build the VcnCellUnderlyingNetworkPriority. */
+ @NonNull
+ public VcnCellUnderlyingNetworkPriority build() {
+ return new VcnCellUnderlyingNetworkPriority(
+ mNetworkQuality,
+ mAllowMetered,
+ mAllowedNetworkPlmnIds,
+ mAllowedSpecificCarrierIds,
+ mAllowRoaming,
+ mRequireOpportunistic);
+ }
+
+ /** @hide */
+ @Override
+ Builder self() {
+ return this;
+ }
+ }
+}
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index 752ef3e..de4ada2 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -16,6 +16,7 @@
package android.net.vcn;
import static android.net.ipsec.ike.IkeSessionParams.IKE_OPTION_MOBIKE;
+import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
import static com.android.internal.annotations.VisibleForTesting.Visibility;
@@ -41,6 +42,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
@@ -157,6 +159,34 @@
TimeUnit.MINUTES.toMillis(5),
TimeUnit.MINUTES.toMillis(15)
};
+
+ private static final LinkedHashSet<VcnUnderlyingNetworkPriority>
+ DEFAULT_UNDERLYING_NETWORK_PRIORITIES = new LinkedHashSet<>();
+
+ static {
+ DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
+ new VcnCellUnderlyingNetworkPriority.Builder()
+ .setNetworkQuality(NETWORK_QUALITY_OK)
+ .setAllowMetered(true /* allowMetered */)
+ .setAllowRoaming(true /* allowRoaming */)
+ .setRequireOpportunistic(true /* requireOpportunistic */)
+ .build());
+
+ DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
+ new VcnWifiUnderlyingNetworkPriority.Builder()
+ .setNetworkQuality(NETWORK_QUALITY_OK)
+ .setAllowMetered(true /* allowMetered */)
+ .build());
+
+ DEFAULT_UNDERLYING_NETWORK_PRIORITIES.add(
+ new VcnCellUnderlyingNetworkPriority.Builder()
+ .setNetworkQuality(NETWORK_QUALITY_OK)
+ .setAllowMetered(true /* allowMetered */)
+ .setAllowRoaming(true /* allowRoaming */)
+ .setRequireOpportunistic(false /* requireOpportunistic */)
+ .build());
+ }
+
private static final String GATEWAY_CONNECTION_NAME_KEY = "mGatewayConnectionName";
@NonNull private final String mGatewayConnectionName;
@@ -166,6 +196,9 @@
private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities";
@NonNull private final SortedSet<Integer> mExposedCapabilities;
+ private static final String UNDERLYING_NETWORK_PRIORITIES_KEY = "mUnderlyingNetworkPriorities";
+ @NonNull private final LinkedHashSet<VcnUnderlyingNetworkPriority> mUnderlyingNetworkPriorities;
+
private static final String MAX_MTU_KEY = "mMaxMtu";
private final int mMaxMtu;
@@ -177,6 +210,7 @@
@NonNull String gatewayConnectionName,
@NonNull IkeTunnelConnectionParams tunnelConnectionParams,
@NonNull Set<Integer> exposedCapabilities,
+ @NonNull LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities,
@NonNull long[] retryIntervalsMs,
@IntRange(from = MIN_MTU_V6) int maxMtu) {
mGatewayConnectionName = gatewayConnectionName;
@@ -185,6 +219,11 @@
mRetryIntervalsMs = retryIntervalsMs;
mMaxMtu = maxMtu;
+ mUnderlyingNetworkPriorities = new LinkedHashSet<>(underlyingNetworkPriorities);
+ if (mUnderlyingNetworkPriorities.isEmpty()) {
+ mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
+ }
+
validate();
}
@@ -198,12 +237,19 @@
final PersistableBundle exposedCapsBundle =
in.getPersistableBundle(EXPOSED_CAPABILITIES_KEY);
+ final PersistableBundle networkPrioritiesBundle =
+ in.getPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY);
mGatewayConnectionName = in.getString(GATEWAY_CONNECTION_NAME_KEY);
mTunnelConnectionParams =
TunnelConnectionParamsUtils.fromPersistableBundle(tunnelConnectionParamsBundle);
mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
+ mUnderlyingNetworkPriorities =
+ new LinkedHashSet<>(
+ PersistableBundleUtils.toList(
+ networkPrioritiesBundle,
+ VcnUnderlyingNetworkPriority::fromPersistableBundle));
mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
mMaxMtu = in.getInt(MAX_MTU_KEY);
@@ -221,6 +267,7 @@
checkValidCapability(cap);
}
+ Objects.requireNonNull(mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null");
Objects.requireNonNull(mRetryIntervalsMs, "retryIntervalsMs was null");
validateRetryInterval(mRetryIntervalsMs);
@@ -303,6 +350,18 @@
}
/**
+ * Retrieve the configured VcnUnderlyingNetworkPriority list, or a default list if it is not
+ * configured.
+ *
+ * @see Builder#setVcnUnderlyingNetworkPriorities(LinkedHashSet<VcnUnderlyingNetworkPriority>)
+ * @hide
+ */
+ @NonNull
+ public LinkedHashSet<VcnUnderlyingNetworkPriority> getVcnUnderlyingNetworkPriorities() {
+ return new LinkedHashSet<>(mUnderlyingNetworkPriorities);
+ }
+
+ /**
* Retrieves the configured retry intervals.
*
* @see Builder#setRetryIntervalsMillis(long[])
@@ -338,10 +397,15 @@
PersistableBundleUtils.fromList(
new ArrayList<>(mExposedCapabilities),
PersistableBundleUtils.INTEGER_SERIALIZER);
+ final PersistableBundle networkPrioritiesBundle =
+ PersistableBundleUtils.fromList(
+ new ArrayList<>(mUnderlyingNetworkPriorities),
+ VcnUnderlyingNetworkPriority::toPersistableBundle);
result.putString(GATEWAY_CONNECTION_NAME_KEY, mGatewayConnectionName);
result.putPersistableBundle(TUNNEL_CONNECTION_PARAMS_KEY, tunnelConnectionParamsBundle);
result.putPersistableBundle(EXPOSED_CAPABILITIES_KEY, exposedCapsBundle);
+ result.putPersistableBundle(UNDERLYING_NETWORK_PRIORITIES_KEY, networkPrioritiesBundle);
result.putLongArray(RETRY_INTERVAL_MS_KEY, mRetryIntervalsMs);
result.putInt(MAX_MTU_KEY, mMaxMtu);
@@ -379,6 +443,11 @@
@NonNull private final String mGatewayConnectionName;
@NonNull private final IkeTunnelConnectionParams mTunnelConnectionParams;
@NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
+
+ @NonNull
+ private final LinkedHashSet<VcnUnderlyingNetworkPriority> mUnderlyingNetworkPriorities =
+ new LinkedHashSet<>(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
+
@NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
private int mMaxMtu = DEFAULT_MAX_MTU;
@@ -450,6 +519,33 @@
}
/**
+ * Set the VcnUnderlyingNetworkPriority list.
+ *
+ * @param underlyingNetworkPriorities a list of unique VcnUnderlyingNetworkPriorities that
+ * are ordered from most to least preferred, or an empty list to use the default
+ * prioritization. The default network prioritization is Opportunistic cellular, Carrier
+ * WiFi and Macro cellular
+ * @return
+ */
+ /** @hide */
+ @NonNull
+ public Builder setVcnUnderlyingNetworkPriorities(
+ @NonNull LinkedHashSet<VcnUnderlyingNetworkPriority> underlyingNetworkPriorities) {
+ Objects.requireNonNull(
+ mUnderlyingNetworkPriorities, "underlyingNetworkPriorities is null");
+
+ mUnderlyingNetworkPriorities.clear();
+
+ if (underlyingNetworkPriorities.isEmpty()) {
+ mUnderlyingNetworkPriorities.addAll(DEFAULT_UNDERLYING_NETWORK_PRIORITIES);
+ } else {
+ mUnderlyingNetworkPriorities.addAll(underlyingNetworkPriorities);
+ }
+
+ return this;
+ }
+
+ /**
* Set the retry interval between VCN establishment attempts upon successive failures.
*
* <p>The last retry interval will be repeated until safe mode is entered, or a connection
@@ -513,6 +609,7 @@
mGatewayConnectionName,
mTunnelConnectionParams,
mExposedCapabilities,
+ mUnderlyingNetworkPriorities,
mRetryIntervalsMs,
mMaxMtu);
}
diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnUnderlyingNetworkPriority.java
new file mode 100644
index 0000000..82f6ae7
--- /dev/null
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPriority.java
@@ -0,0 +1,181 @@
+/*
+ * 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 android.net.vcn;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.PersistableBundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+// TODO: Add documents
+/** @hide */
+public abstract class VcnUnderlyingNetworkPriority {
+ /** @hide */
+ protected static final int NETWORK_PRIORITY_TYPE_WIFI = 1;
+ /** @hide */
+ protected static final int NETWORK_PRIORITY_TYPE_CELL = 2;
+
+ /** Denotes that network quality needs to be OK */
+ public static final int NETWORK_QUALITY_OK = 10000;
+ /** Denotes that any network quality is acceptable */
+ public static final int NETWORK_QUALITY_ANY = Integer.MAX_VALUE;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({NETWORK_QUALITY_OK, NETWORK_QUALITY_ANY})
+ public @interface NetworkQuality {}
+
+ private static final String NETWORK_PRIORITY_TYPE_KEY = "mNetworkPriorityType";
+ private final int mNetworkPriorityType;
+
+ /** @hide */
+ protected static final String NETWORK_QUALITY_KEY = "mNetworkQuality";
+ private final int mNetworkQuality;
+
+ /** @hide */
+ protected static final String ALLOW_METERED_KEY = "mAllowMetered";
+ private final boolean mAllowMetered;
+
+ /** @hide */
+ protected VcnUnderlyingNetworkPriority(
+ int networkPriorityType, int networkQuality, boolean allowMetered) {
+ mNetworkPriorityType = networkPriorityType;
+ mNetworkQuality = networkQuality;
+ mAllowMetered = allowMetered;
+ }
+
+ private static void validateNetworkQuality(int networkQuality) {
+ Preconditions.checkArgument(
+ networkQuality == NETWORK_QUALITY_ANY || networkQuality == NETWORK_QUALITY_OK,
+ "Invalid networkQuality:" + networkQuality);
+ }
+
+ /** @hide */
+ protected void validate() {
+ validateNetworkQuality(mNetworkQuality);
+ }
+
+ /** @hide */
+ @NonNull
+ @VisibleForTesting(visibility = Visibility.PROTECTED)
+ public static VcnUnderlyingNetworkPriority fromPersistableBundle(
+ @NonNull PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle is null");
+
+ final int networkPriorityType = in.getInt(NETWORK_PRIORITY_TYPE_KEY);
+ switch (networkPriorityType) {
+ case NETWORK_PRIORITY_TYPE_WIFI:
+ return VcnWifiUnderlyingNetworkPriority.fromPersistableBundle(in);
+ case NETWORK_PRIORITY_TYPE_CELL:
+ return VcnCellUnderlyingNetworkPriority.fromPersistableBundle(in);
+ default:
+ throw new IllegalArgumentException(
+ "Invalid networkPriorityType:" + networkPriorityType);
+ }
+ }
+
+ /** @hide */
+ @NonNull
+ PersistableBundle toPersistableBundle() {
+ final PersistableBundle result = new PersistableBundle();
+
+ result.putInt(NETWORK_PRIORITY_TYPE_KEY, mNetworkPriorityType);
+ result.putInt(NETWORK_QUALITY_KEY, mNetworkQuality);
+ result.putBoolean(ALLOW_METERED_KEY, mAllowMetered);
+
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mNetworkPriorityType, mNetworkQuality, mAllowMetered);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!(other instanceof VcnUnderlyingNetworkPriority)) {
+ return false;
+ }
+
+ final VcnUnderlyingNetworkPriority rhs = (VcnUnderlyingNetworkPriority) other;
+ return mNetworkPriorityType == rhs.mNetworkPriorityType
+ && mNetworkQuality == rhs.mNetworkQuality
+ && mAllowMetered == rhs.mAllowMetered;
+ }
+
+ /** Retrieve the required network quality. */
+ @NetworkQuality
+ public int getNetworkQuality() {
+ return mNetworkQuality;
+ }
+
+ /** Return if a metered network is allowed. */
+ public boolean allowMetered() {
+ return mAllowMetered;
+ }
+
+ /**
+ * This class is used to incrementally build VcnUnderlyingNetworkPriority objects.
+ *
+ * @param <T> The subclass to be built.
+ */
+ public abstract static class Builder<T extends Builder<T>> {
+ /** @hide */
+ protected int mNetworkQuality = NETWORK_QUALITY_ANY;
+ /** @hide */
+ protected boolean mAllowMetered = false;
+
+ /** @hide */
+ protected Builder() {}
+
+ /**
+ * Set the required network quality.
+ *
+ * @param networkQuality the required network quality. Defaults to NETWORK_QUALITY_ANY
+ */
+ @NonNull
+ public T setNetworkQuality(@NetworkQuality int networkQuality) {
+ validateNetworkQuality(networkQuality);
+
+ mNetworkQuality = networkQuality;
+ return self();
+ }
+
+ /**
+ * Set if a metered network is allowed.
+ *
+ * @param allowMetered the flag to indicate if a metered network is allowed, defaults to
+ * {@code false}
+ */
+ @NonNull
+ public T setAllowMetered(boolean allowMetered) {
+ mAllowMetered = allowMetered;
+ return self();
+ }
+
+ /** @hide */
+ abstract T self();
+ }
+}
diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java
new file mode 100644
index 0000000..fc7e7e2
--- /dev/null
+++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkPriority.java
@@ -0,0 +1,120 @@
+/*
+ * 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 android.net.vcn;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.PersistableBundle;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
+// TODO: Add documents
+/** @hide */
+public final class VcnWifiUnderlyingNetworkPriority extends VcnUnderlyingNetworkPriority {
+ private static final String SSID_KEY = "mSsid";
+ @Nullable private final String mSsid;
+
+ private VcnWifiUnderlyingNetworkPriority(
+ int networkQuality, boolean allowMetered, String ssid) {
+ super(NETWORK_PRIORITY_TYPE_WIFI, networkQuality, allowMetered);
+ mSsid = ssid;
+
+ validate();
+ }
+
+ /** @hide */
+ @NonNull
+ @VisibleForTesting(visibility = Visibility.PROTECTED)
+ public static VcnWifiUnderlyingNetworkPriority fromPersistableBundle(
+ @NonNull PersistableBundle in) {
+ Objects.requireNonNull(in, "PersistableBundle is null");
+
+ final int networkQuality = in.getInt(NETWORK_QUALITY_KEY);
+ final boolean allowMetered = in.getBoolean(ALLOW_METERED_KEY);
+ final String ssid = in.getString(SSID_KEY);
+ return new VcnWifiUnderlyingNetworkPriority(networkQuality, allowMetered, ssid);
+ }
+
+ /** @hide */
+ @Override
+ @NonNull
+ @VisibleForTesting(visibility = Visibility.PROTECTED)
+ public PersistableBundle toPersistableBundle() {
+ final PersistableBundle result = super.toPersistableBundle();
+ result.putString(SSID_KEY, mSsid);
+ return result;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), mSsid);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!super.equals(other)) {
+ return false;
+ }
+
+ if (!(other instanceof VcnWifiUnderlyingNetworkPriority)) {
+ return false;
+ }
+
+ final VcnWifiUnderlyingNetworkPriority rhs = (VcnWifiUnderlyingNetworkPriority) other;
+ return mSsid == rhs.mSsid;
+ }
+
+ /** Retrieve the required SSID, or {@code null} if there is no requirement on SSID. */
+ @Nullable
+ public String getSsid() {
+ return mSsid;
+ }
+
+ /** This class is used to incrementally build VcnWifiUnderlyingNetworkPriority objects. */
+ public static class Builder extends VcnUnderlyingNetworkPriority.Builder<Builder> {
+ @Nullable private String mSsid;
+
+ /** Construct a Builder object. */
+ public Builder() {}
+
+ /**
+ * Set the required SSID.
+ *
+ * @param ssid the required SSID, or {@code null} if any SSID is acceptable.
+ */
+ @NonNull
+ public Builder setSsid(@Nullable String ssid) {
+ mSsid = ssid;
+ return this;
+ }
+
+ /** Build the VcnWifiUnderlyingNetworkPriority. */
+ @NonNull
+ public VcnWifiUnderlyingNetworkPriority build() {
+ return new VcnWifiUnderlyingNetworkPriority(mNetworkQuality, mAllowMetered, mSsid);
+ }
+
+ /** @hide */
+ @Override
+ Builder self() {
+ return this;
+ }
+ }
+}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index afd0ff7..3664515 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -3646,7 +3646,14 @@
* list was {@code null}, {@code list} is cleared.
*
* @see #writeParcelableList(List, int)
+ *
+ * @deprecated Use the type-safer version {@link #readParcelableList(List, ClassLoader, Class)}
+ * starting from Android {@link Build.VERSION_CODES#TIRAMISU}. Also consider changing the
+ * format to use {@link #readTypedList(List, Parcelable.Creator)} if possible (eg. if the
+ * items' class is final) since this is also more performant. Note that changing to the
+ * latter also requires changing the writes.
*/
+ @Deprecated
@NonNull
public final <T extends Parcelable> List<T> readParcelableList(@NonNull List<T> list,
@Nullable ClassLoader cl) {
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index 3e01c53..b7e3068 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -238,7 +238,7 @@
public static final int DISABLED = 9;
}
- private IUpdateEngine mUpdateEngine;
+ private final IUpdateEngine mUpdateEngine;
private IUpdateEngineCallback mUpdateEngineCallback = null;
private final Object mUpdateEngineCallbackLock = new Object();
@@ -248,6 +248,9 @@
public UpdateEngine() {
mUpdateEngine = IUpdateEngine.Stub.asInterface(
ServiceManager.getService(UPDATE_ENGINE_SERVICE));
+ if (mUpdateEngine == null) {
+ throw new IllegalStateException("Failed to find update_engine");
+ }
}
/**
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 9612ca6..5831573 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -136,7 +136,7 @@
/**
* @hide
*/
- @IntDef(prefix = { "FLAG_" }, value = {
+ @IntDef(prefix = { "FLAG_" }, flag = true, value = {
FLAG_BYPASS_INTERRUPTION_POLICY,
})
@Retention(RetentionPolicy.SOURCE)
@@ -162,7 +162,8 @@
private final int mFlags;
private final int mOriginalAudioUsage;
- private VibrationAttributes(int usage, int audioUsage, int flags) {
+ private VibrationAttributes(@Usage int usage, @AudioAttributes.AttributeUsage int audioUsage,
+ @Flag int flags) {
mUsage = usage;
mOriginalAudioUsage = audioUsage;
mFlags = flags & FLAG_ALL_SUPPORTED;
@@ -172,6 +173,7 @@
* Return the vibration usage class.
* @return USAGE_CLASS_ALARM, USAGE_CLASS_FEEDBACK or USAGE_CLASS_UNKNOWN
*/
+ @UsageClass
public int getUsageClass() {
return mUsage & USAGE_CLASS_MASK;
}
@@ -180,6 +182,7 @@
* Return the vibration usage.
* @return one of the values that can be set in {@link Builder#setUsage(int)}
*/
+ @Usage
public int getUsage() {
return mUsage;
}
@@ -188,6 +191,7 @@
* Return the flags.
* @return a combined mask of all flags
*/
+ @Flag
public int getFlags() {
return mFlags;
}
@@ -196,7 +200,7 @@
* Check whether a flag is set
* @return true if a flag is set and false otherwise
*/
- public boolean isFlagSet(int flag) {
+ public boolean isFlagSet(@Flag int flag) {
return (mFlags & flag) > 0;
}
@@ -206,6 +210,7 @@
* @hide
*/
@TestApi
+ @AudioAttributes.AttributeUsage
public int getAudioUsage() {
if (mOriginalAudioUsage != AudioAttributes.USAGE_UNKNOWN) {
// Return same audio usage set in the Builder.
@@ -292,7 +297,7 @@
}
/** @hide */
- public static String usageToString(int usage) {
+ public static String usageToString(@Usage int usage) {
switch (usage) {
case USAGE_UNKNOWN:
return "UNKNOWN";
@@ -419,7 +424,7 @@
* {@link VibrationAttributes#USAGE_MEDIA}.
* @return the same Builder instance.
*/
- public @NonNull Builder setUsage(int usage) {
+ public @NonNull Builder setUsage(@Usage int usage) {
mOriginalAudioUsage = AudioAttributes.USAGE_UNKNOWN;
mUsage = usage;
return this;
@@ -431,7 +436,7 @@
* @param mask Bit range that should be changed.
* @return the same Builder instance.
*/
- public @NonNull Builder setFlags(int flags, int mask) {
+ public @NonNull Builder setFlags(@Flag int flags, int mask) {
mask &= FLAG_ALL_SUPPORTED;
mFlags = (mFlags & ~mask) | (flags & mask);
return this;
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index 2adcbc3..0c2f8b6 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -99,6 +99,7 @@
@UnsupportedAppUsage
private final boolean mRemovable;
private final boolean mEmulated;
+ private final boolean mStub;
private final boolean mAllowMassStorage;
private final long mMaxFileSize;
private final UserHandle mOwner;
@@ -137,8 +138,9 @@
/** {@hide} */
public StorageVolume(String id, File path, File internalPath, String description,
- boolean primary, boolean removable, boolean emulated, boolean allowMassStorage,
- long maxFileSize, UserHandle owner, UUID uuid, String fsUuid, String state) {
+ boolean primary, boolean removable, boolean emulated, boolean stub,
+ boolean allowMassStorage, long maxFileSize, UserHandle owner, UUID uuid, String fsUuid,
+ String state) {
mId = Preconditions.checkNotNull(id);
mPath = Preconditions.checkNotNull(path);
mInternalPath = Preconditions.checkNotNull(internalPath);
@@ -146,6 +148,7 @@
mPrimary = primary;
mRemovable = removable;
mEmulated = emulated;
+ mStub = stub;
mAllowMassStorage = allowMassStorage;
mMaxFileSize = maxFileSize;
mOwner = Preconditions.checkNotNull(owner);
@@ -162,6 +165,7 @@
mPrimary = in.readInt() != 0;
mRemovable = in.readInt() != 0;
mEmulated = in.readInt() != 0;
+ mStub = in.readInt() != 0;
mAllowMassStorage = in.readInt() != 0;
mMaxFileSize = in.readLong();
mOwner = in.readParcelable(null);
@@ -264,13 +268,23 @@
/**
* Returns true if the volume is emulated.
*
- * @return is removable
+ * @return is emulated
*/
public boolean isEmulated() {
return mEmulated;
}
/**
+ * Returns true if the volume is a stub volume (a volume managed from outside Android).
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isStub() {
+ return mStub;
+ }
+
+ /**
* Returns true if this volume can be shared via USB mass storage.
*
* @return whether mass storage is allowed
@@ -506,6 +520,7 @@
pw.printPair("mPrimary", mPrimary);
pw.printPair("mRemovable", mRemovable);
pw.printPair("mEmulated", mEmulated);
+ pw.printPair("mStub", mStub);
pw.printPair("mAllowMassStorage", mAllowMassStorage);
pw.printPair("mMaxFileSize", mMaxFileSize);
pw.printPair("mOwner", mOwner);
@@ -540,6 +555,7 @@
parcel.writeInt(mPrimary ? 1 : 0);
parcel.writeInt(mRemovable ? 1 : 0);
parcel.writeInt(mEmulated ? 1 : 0);
+ parcel.writeInt(mStub ? 1 : 0);
parcel.writeInt(mAllowMassStorage ? 1 : 0);
parcel.writeLong(mMaxFileSize);
parcel.writeParcelable(mOwner, flags);
@@ -621,6 +637,7 @@
mPrimary,
mRemovable,
mEmulated,
+ /* stub= */ false,
/* allowMassStorage= */ false,
/* maxFileSize= */ 0,
mOwner,
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 39a2e13..ebd143c 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -404,6 +404,7 @@
final boolean removable;
final boolean emulated;
+ final boolean stub = type == TYPE_STUB;
final boolean allowMassStorage = false;
final String envState = reportUnmounted
? Environment.MEDIA_UNMOUNTED : getEnvironmentForState(state);
@@ -459,8 +460,8 @@
}
return new StorageVolume(id, userPath, internalPath, description, isPrimary(), removable,
- emulated, allowMassStorage, maxFileSize, new UserHandle(userId),
- uuid, derivedFsUuid, envState);
+ emulated, stub, allowMassStorage, maxFileSize, new UserHandle(userId), uuid,
+ derivedFsUuid, envState);
}
@UnsupportedAppUsage
diff --git a/core/java/android/os/storage/VolumeRecord.java b/core/java/android/os/storage/VolumeRecord.java
index 1cc982e7..eac09aa 100644
--- a/core/java/android/os/storage/VolumeRecord.java
+++ b/core/java/android/os/storage/VolumeRecord.java
@@ -105,6 +105,7 @@
final boolean primary = false;
final boolean removable = true;
final boolean emulated = false;
+ final boolean stub = false;
final boolean allowMassStorage = false;
final long maxFileSize = 0;
final UserHandle user = new UserHandle(UserHandle.USER_NULL);
@@ -116,7 +117,8 @@
}
return new StorageVolume(id, userPath, internalPath, description, primary, removable,
- emulated, allowMassStorage, maxFileSize, user, null /* uuid */, fsUuid, envState);
+ emulated, stub, allowMassStorage, maxFileSize, user, null /* uuid */, fsUuid,
+ envState);
}
public void dump(IndentingPrintWriter pw) {
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 5d84af0..50a44a1 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -3775,6 +3775,25 @@
public static final String NETWORK_TYPE_BITMASK = "network_type_bitmask";
/**
+ * Lingering radio technology (network type) bitmask.
+ * To check what values can be contained, refer to the NETWORK_TYPE_ constants in
+ * {@link android.telephony.TelephonyManager}.
+ * Bitmask for a radio tech R is (1 << (R - 1))
+ * <P>Type: INTEGER (long)</P>
+ * @hide
+ */
+ public static final String LINGERING_NETWORK_TYPE_BITMASK =
+ "lingering_network_type_bitmask";
+
+ /**
+ * Sets whether the PDU session brought up by this APN should always be on.
+ * See 3GPP TS 23.501 section 5.6.13
+ * <P>Type: INTEGER</P>
+ * @hide
+ */
+ public static final String ALWAYS_ON = "always_on";
+
+ /**
* MVNO type:
* {@code SPN (Service Provider Name), IMSI, GID (Group Identifier Level 1)}.
* <P>Type: TEXT</P>
@@ -3852,11 +3871,31 @@
* connected, in bytes.
* <p>Type: INTEGER </p>
* @hide
+ * @deprecated use {@link #MTU_V4} or {@link #MTU_V6} instead
*/
@SystemApi
+ @Deprecated
public static final String MTU = "mtu";
/**
+ * The MTU (maximum transmit unit) size of the mobile interface for IPv4 to which the APN is
+ * connected, in bytes.
+ * <p>Type: INTEGER </p>
+ * @hide
+ */
+ @SystemApi
+ public static final String MTU_V4 = "mtu_v4";
+
+ /**
+ * The MTU (maximum transmit unit) size of the mobile interface for IPv6 to which the APN is
+ * connected, in bytes.
+ * <p>Type: INTEGER </p>
+ * @hide
+ */
+ @SystemApi
+ public static final String MTU_V6 = "mtu_v6";
+
+ /**
* APN edit status. APN could be added/edited/deleted by a user or carrier.
* see all possible returned APN edit status.
* <ul>
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 62585c1..a8bf50e 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1848,7 +1848,7 @@
if (mHasPendingRestartInputForSetText) {
final InputMethodManager imm = getInputMethodManager();
if (imm != null) {
- imm.restartInput(mTextView);
+ imm.invalidateInput(mTextView);
}
mHasPendingRestartInputForSetText = false;
}
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 7a960c6..5e75797 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -368,10 +368,12 @@
*/
@NonNull
public WindowContainerTransaction setAdjacentRoots(
- @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2) {
+ @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2,
+ boolean moveTogether) {
mHierarchyOps.add(HierarchyOp.createForAdjacentRoots(
root1.asBinder(),
- root2.asBinder()));
+ root2.asBinder(),
+ moveTogether));
return this;
}
@@ -975,6 +977,9 @@
private boolean mReparentTopOnly;
+ // TODO(b/207185041): Remove this once having a single-top root for split screen.
+ private boolean mMoveAdjacentTogether;
+
@Nullable
private int[] mWindowingModes;
@@ -1033,10 +1038,13 @@
.build();
}
- public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2) {
+ /** Create a hierarchy op for setting adjacent root tasks. */
+ public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2,
+ boolean moveTogether) {
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS)
.setContainer(root1)
.setReparentContainer(root2)
+ .setMoveAdjacentTogether(moveTogether)
.build();
}
@@ -1070,6 +1078,7 @@
mReparent = copy.mReparent;
mToTop = copy.mToTop;
mReparentTopOnly = copy.mReparentTopOnly;
+ mMoveAdjacentTogether = copy.mMoveAdjacentTogether;
mWindowingModes = copy.mWindowingModes;
mActivityTypes = copy.mActivityTypes;
mLaunchOptions = copy.mLaunchOptions;
@@ -1084,6 +1093,7 @@
mReparent = in.readStrongBinder();
mToTop = in.readBoolean();
mReparentTopOnly = in.readBoolean();
+ mMoveAdjacentTogether = in.readBoolean();
mWindowingModes = in.createIntArray();
mActivityTypes = in.createIntArray();
mLaunchOptions = in.readBundle();
@@ -1128,6 +1138,10 @@
return mReparentTopOnly;
}
+ public boolean getMoveAdjacentTogether() {
+ return mMoveAdjacentTogether;
+ }
+
public int[] getWindowingModes() {
return mWindowingModes;
}
@@ -1175,7 +1189,8 @@
return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}";
case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
return "{SetAdjacentRoot: container=" + mContainer
- + " adjacentRoot=" + mReparent + "}";
+ + " adjacentRoot=" + mReparent + " mMoveAdjacentTogether="
+ + mMoveAdjacentTogether + "}";
case HIERARCHY_OP_TYPE_LAUNCH_TASK:
return "{LaunchTask: " + mLaunchOptions + "}";
case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT:
@@ -1212,6 +1227,7 @@
dest.writeStrongBinder(mReparent);
dest.writeBoolean(mToTop);
dest.writeBoolean(mReparentTopOnly);
+ dest.writeBoolean(mMoveAdjacentTogether);
dest.writeIntArray(mWindowingModes);
dest.writeIntArray(mActivityTypes);
dest.writeBundle(mLaunchOptions);
@@ -1251,6 +1267,8 @@
private boolean mReparentTopOnly;
+ private boolean mMoveAdjacentTogether;
+
@Nullable
private int[] mWindowingModes;
@@ -1293,6 +1311,11 @@
return this;
}
+ Builder setMoveAdjacentTogether(boolean moveAdjacentTogether) {
+ mMoveAdjacentTogether = moveAdjacentTogether;
+ return this;
+ }
+
Builder setWindowingModes(@Nullable int[] windowingModes) {
mWindowingModes = windowingModes;
return this;
@@ -1336,6 +1359,7 @@
: null;
hierarchyOp.mToTop = mToTop;
hierarchyOp.mReparentTopOnly = mReparentTopOnly;
+ hierarchyOp.mMoveAdjacentTogether = mMoveAdjacentTogether;
hierarchyOp.mLaunchOptions = mLaunchOptions;
hierarchyOp.mActivityIntent = mActivityIntent;
hierarchyOp.mPendingIntent = mPendingIntent;
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index 0723766..4dc9aa5 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -328,7 +328,7 @@
if (multiStateCounter != null) {
if (mAccumulatedMultiStateChargeMicroCoulomb == null) {
mAccumulatedMultiStateChargeMicroCoulomb =
- new LongMultiStateCounter[numWrittenEntries];
+ new LongMultiStateCounter[mAccumulatedChargeMicroCoulomb.length];
}
mAccumulatedMultiStateChargeMicroCoulomb[index] = multiStateCounter;
}
diff --git a/core/jni/android_graphics_SurfaceTexture.cpp b/core/jni/android_graphics_SurfaceTexture.cpp
index 2f12289..0f647ea 100644
--- a/core/jni/android_graphics_SurfaceTexture.cpp
+++ b/core/jni/android_graphics_SurfaceTexture.cpp
@@ -346,6 +346,11 @@
return surfaceTexture->getTimestamp();
}
+static jlong SurfaceTexture_getDataSpace(JNIEnv* env, jobject thiz) {
+ sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
+ return surfaceTexture->getCurrentDataSpace();
+}
+
static void SurfaceTexture_release(JNIEnv* env, jobject thiz)
{
sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
@@ -361,17 +366,18 @@
// ----------------------------------------------------------------------------
static const JNINativeMethod gSurfaceTextureMethods[] = {
- {"nativeInit", "(ZIZLjava/lang/ref/WeakReference;)V", (void*)SurfaceTexture_init },
- {"nativeFinalize", "()V", (void*)SurfaceTexture_finalize },
- {"nativeSetDefaultBufferSize", "(II)V", (void*)SurfaceTexture_setDefaultBufferSize },
- {"nativeUpdateTexImage", "()V", (void*)SurfaceTexture_updateTexImage },
- {"nativeReleaseTexImage", "()V", (void*)SurfaceTexture_releaseTexImage },
- {"nativeDetachFromGLContext", "()I", (void*)SurfaceTexture_detachFromGLContext },
- {"nativeAttachToGLContext", "(I)I", (void*)SurfaceTexture_attachToGLContext },
- {"nativeGetTransformMatrix", "([F)V", (void*)SurfaceTexture_getTransformMatrix },
- {"nativeGetTimestamp", "()J", (void*)SurfaceTexture_getTimestamp },
- {"nativeRelease", "()V", (void*)SurfaceTexture_release },
- {"nativeIsReleased", "()Z", (void*)SurfaceTexture_isReleased },
+ {"nativeInit", "(ZIZLjava/lang/ref/WeakReference;)V", (void*)SurfaceTexture_init},
+ {"nativeFinalize", "()V", (void*)SurfaceTexture_finalize},
+ {"nativeSetDefaultBufferSize", "(II)V", (void*)SurfaceTexture_setDefaultBufferSize},
+ {"nativeUpdateTexImage", "()V", (void*)SurfaceTexture_updateTexImage},
+ {"nativeReleaseTexImage", "()V", (void*)SurfaceTexture_releaseTexImage},
+ {"nativeDetachFromGLContext", "()I", (void*)SurfaceTexture_detachFromGLContext},
+ {"nativeAttachToGLContext", "(I)I", (void*)SurfaceTexture_attachToGLContext},
+ {"nativeGetTransformMatrix", "([F)V", (void*)SurfaceTexture_getTransformMatrix},
+ {"nativeGetTimestamp", "()J", (void*)SurfaceTexture_getTimestamp},
+ {"nativeGetDataSpace", "()J", (void*)SurfaceTexture_getDataSpace},
+ {"nativeRelease", "()V", (void*)SurfaceTexture_release},
+ {"nativeIsReleased", "()Z", (void*)SurfaceTexture_isReleased},
};
int register_android_graphics_SurfaceTexture(JNIEnv* env)
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 268871b..8b45907 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2284,10 +2284,8 @@
return jStatus;
}
-static jint
-android_media_AudioSystem_getHwOffloadEncodingFormatsSupportedForA2DP(
- JNIEnv *env, jobject thiz, jobject jEncodingFormatList)
-{
+static jint android_media_AudioSystem_getHwOffloadFormatsSupportedForBluetoothMedia(
+ JNIEnv *env, jobject thiz, jint deviceType, jobject jEncodingFormatList) {
ALOGV("%s", __FUNCTION__);
jint jStatus = AUDIO_JAVA_SUCCESS;
if (!env->IsInstanceOf(jEncodingFormatList, gArrayListClass)) {
@@ -2295,8 +2293,10 @@
return (jint)AUDIO_JAVA_BAD_VALUE;
}
std::vector<audio_format_t> encodingFormats;
- status_t status = AudioSystem::getHwOffloadEncodingFormatsSupportedForA2DP(
- &encodingFormats);
+ status_t status =
+ AudioSystem::getHwOffloadFormatsSupportedForBluetoothMedia(static_cast<audio_devices_t>(
+ deviceType),
+ &encodingFormats);
if (status != NO_ERROR) {
ALOGE("%s: error %d", __FUNCTION__, status);
jStatus = nativeToJavaStatus(status);
@@ -2875,8 +2875,8 @@
{"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids},
{"isHapticPlaybackSupported", "()Z",
(void *)android_media_AudioSystem_isHapticPlaybackSupported},
- {"getHwOffloadEncodingFormatsSupportedForA2DP", "(Ljava/util/ArrayList;)I",
- (void *)android_media_AudioSystem_getHwOffloadEncodingFormatsSupportedForA2DP},
+ {"getHwOffloadFormatsSupportedForBluetoothMedia", "(ILjava/util/ArrayList;)I",
+ (void *)android_media_AudioSystem_getHwOffloadFormatsSupportedForBluetoothMedia},
{"setSupportedSystemUsages", "([I)I",
(void *)android_media_AudioSystem_setSupportedSystemUsages},
{"setAllowedCapturePolicy", "(II)I",
@@ -2919,7 +2919,6 @@
"[Landroid/media/AudioDeviceAttributes;)Z",
(void *)android_media_AudioSystem_canBeSpatialized}};
-
static const JNINativeMethod gEventHandlerMethods[] = {
{"native_setup",
"(Ljava/lang/Object;)V",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index aada7eb..316ea34 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4880,6 +4880,11 @@
<permission android:name="android.permission.CHANGE_APP_IDLE_STATE"
android:protectionLevel="signature|privileged" />
+ <!-- @hide @SystemApi Allows an application to change the estimated launch time of an app.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE"
+ android:protectionLevel="signature|privileged" />
+
<!-- @hide @SystemApi Allows an application to temporarily allowlist an inactive app to
access the network and acquire wakelocks.
<p>Not for use by third-party applications. -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f6a0e61..688bced 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3505,20 +3505,35 @@
<!-- [CHAR LIMIT=40] Title of dialog shown to confirm device going to sleep if the power button
is pressed during fingerprint enrollment. -->
- <string name="fp_enrollment_powerbutton_intent_title">Turn off screen?</string>
+ <string name="fp_power_button_enrollment_title">Continue setup?</string>
<!-- [CHAR LIMIT=NONE] Message of dialog shown to confirm device going to sleep if the power
button is pressed during fingerprint enrollment. -->
- <string name="fp_enrollment_powerbutton_intent_message">While setting up your fingerprint, you
- pressed the Power button.\n\nThis usually turns off your screen.</string>
+ <string name="fp_power_button_enrollment_message">You pressed the power button — this usually turns off the screen.\n\nTry tapping lightly while setting up your fingerprint.</string>
<!-- [CHAR LIMIT=20] Positive button of dialog shown to confirm device going to sleep if the
power button is pressed during fingerprint enrollment. -->
- <string name="fp_enrollment_powerbutton_intent_positive_button">Turn off</string>
+ <string name="fp_power_button_enrollment_positive_button">Turn off screen</string>
<!-- [CHAR LIMIT=20] Negative button of dialog shown to confirm device going to sleep if the
power button is pressed during fingerprint enrollment. -->
- <string name="fp_enrollment_powerbutton_intent_negative_button">Cancel</string>
+ <string name="fp_power_button_enrollment_negative_button">Continue setup</string>
+
+ <!-- [CHAR LIMIT=40] Title of dialog shown to confirm device going to sleep if the power button
+ is pressed during biometric prompt when a side fingerprint sensor is present. -->
+ <string name="fp_power_button_bp_title">Continue verifying your fingerprint?</string>
+
+ <!-- [CHAR LIMIT=NONE] Message of dialog shown to confirm device going to sleep if the power
+ button is pressed during biometric prompt when a side fingerprint sensor is present. -->
+ <string name="fp_power_button_bp_message">You pressed the power button — this usually turns off the screen.\n\nTry tapping lightly to verify your fingerprint.</string>
+
+ <!-- [CHAR LIMIT=20] Positive button of dialog shown to confirm device going to sleep if the
+ power button is pressed during biometric prompt when a side fingerprint sensor is present. -->
+ <string name="fp_power_button_bp_positive_button">Turn off screen</string>
+
+ <!-- [CHAR LIMIT=20] Negative button of dialog shown to confirm device going to sleep if the
+ power button is pressed during biometric prompt when a side fingerprint sensor is present. -->
+ <string name="fp_power_button_bp_negative_button">Continue</string>
<!-- Notification text to tell the user that a heavy-weight application is running. -->
<string name="heavy_weight_notification"><xliff:g id="app">%1$s</xliff:g> running</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0495122..76e9774 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1840,10 +1840,14 @@
<java-symbol type="string" name="bugreport_status" />
<java-symbol type="string" name="bugreport_title" />
<java-symbol type="string" name="faceunlock_multiple_failures" />
- <java-symbol type="string" name="fp_enrollment_powerbutton_intent_title" />
- <java-symbol type="string" name="fp_enrollment_powerbutton_intent_message" />
- <java-symbol type="string" name="fp_enrollment_powerbutton_intent_positive_button" />
- <java-symbol type="string" name="fp_enrollment_powerbutton_intent_negative_button" />
+ <java-symbol type="string" name="fp_power_button_bp_title" />
+ <java-symbol type="string" name="fp_power_button_bp_message" />
+ <java-symbol type="string" name="fp_power_button_bp_positive_button" />
+ <java-symbol type="string" name="fp_power_button_bp_negative_button" />
+ <java-symbol type="string" name="fp_power_button_enrollment_title" />
+ <java-symbol type="string" name="fp_power_button_enrollment_message" />
+ <java-symbol type="string" name="fp_power_button_enrollment_positive_button" />
+ <java-symbol type="string" name="fp_power_button_enrollment_negative_button" />
<java-symbol type="string" name="global_actions" />
<java-symbol type="string" name="global_action_power_off" />
<java-symbol type="string" name="global_action_power_options" />
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java
new file mode 100644
index 0000000..6471492
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothLeAudioCodecConfigTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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 android.bluetooth;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test cases for {@link BluetoothLeAudioCodecConfig}.
+ */
+public class BluetoothLeAudioCodecConfigTest extends TestCase {
+ private int[] mCodecTypeArray = new int[] {
+ BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3,
+ BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID,
+ };
+
+ @SmallTest
+ public void testBluetoothLeAudioCodecConfig_valid_get_methods() {
+
+ for (int codecIdx = 0; codecIdx < mCodecTypeArray.length; codecIdx++) {
+ int codecType = mCodecTypeArray[codecIdx];
+
+ BluetoothLeAudioCodecConfig leAudioCodecConfig =
+ buildBluetoothLeAudioCodecConfig(codecType);
+
+ if (codecType == BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3) {
+ assertEquals("LC3", leAudioCodecConfig.getCodecName());
+ }
+ if (codecType == BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
+ assertEquals("INVALID CODEC", leAudioCodecConfig.getCodecName());
+ }
+
+ assertEquals(1, leAudioCodecConfig.getMaxCodecType());
+ assertEquals(codecType, leAudioCodecConfig.getCodecType());
+ }
+ }
+
+ private BluetoothLeAudioCodecConfig buildBluetoothLeAudioCodecConfig(int sourceCodecType) {
+ return new BluetoothLeAudioCodecConfig.Builder()
+ .setCodecType(sourceCodecType)
+ .build();
+
+ }
+}
diff --git a/core/tests/coretests/res/drawable/custom_drawable.xml b/core/tests/coretests/res/drawable/custom_drawable.xml
new file mode 100644
index 0000000..ebb821f
--- /dev/null
+++ b/core/tests/coretests/res/drawable/custom_drawable.xml
@@ -0,0 +1,21 @@
+<?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.
+ -->
+
+<drawable xmlns:android="http://schemas.android.com/apk/res/android"
+ class="android.window.CustomDrawable"
+ android:drawable="@drawable/bitmap_drawable"
+ android:inset="10dp" />
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/window/CustomDrawable.java b/core/tests/coretests/src/android/window/CustomDrawable.java
new file mode 100644
index 0000000..c25f877
--- /dev/null
+++ b/core/tests/coretests/src/android/window/CustomDrawable.java
@@ -0,0 +1,25 @@
+/*
+ * 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 android.window;
+
+import android.graphics.drawable.InsetDrawable;
+
+public class CustomDrawable extends InsetDrawable {
+ public CustomDrawable() {
+ super(null, 0);
+ }
+}
diff --git a/core/tests/coretests/src/android/window/WindowContextTest.java b/core/tests/coretests/src/android/window/WindowContextTest.java
index 83280f1..656e756 100644
--- a/core/tests/coretests/src/android/window/WindowContextTest.java
+++ b/core/tests/coretests/src/android/window/WindowContextTest.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.app.Activity;
@@ -47,6 +48,8 @@
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
+import com.android.frameworks.coretests.R;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -242,6 +245,12 @@
mInstrumentation.runOnMainSync(() -> wm.addView(subWindow, subWindowAttrs));
}
+ @Test
+ public void testGetCustomDrawable() {
+ assertNotNull(mWindowContext.getResources().getDrawable(R.drawable.custom_drawable,
+ null /* theme */));
+ }
+
private WindowContext createWindowContext() {
return createWindowContext(TYPE_APPLICATION_OVERLAY);
}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index cfcbc7d..f8db0606 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -269,7 +269,7 @@
onView(withId(R.id.content_preview_thumbnail)).check(matches(isDisplayed()));
}
- @Test @Ignore
+ @Test
public void twoOptionsAndUserSelectsOne() throws InterruptedException {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -298,7 +298,7 @@
assertThat(chosen[0], is(toChoose));
}
- @Test @Ignore
+ @Test
public void fourOptionsStackedIntoOneTarget() throws InterruptedException {
Intent sendIntent = createSendTextIntent();
@@ -351,7 +351,7 @@
}
}
- @Test @Ignore
+ @Test
public void updateChooserCountsAndModelAfterUserSelection() throws InterruptedException {
Intent sendIntent = createSendTextIntent();
List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
@@ -461,7 +461,7 @@
assertThat(chosen[0], is(toChoose));
}
- @Test @Ignore
+ @Test
public void hasOtherProfileTwoOptionsAndUserSelectsOne() throws Exception {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
@@ -500,7 +500,7 @@
assertThat(chosen[0], is(toChoose));
}
- @Test @Ignore
+ @Test
public void hasLastChosenActivityAndOtherProfile() throws Exception {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
@@ -1549,7 +1549,7 @@
assertThat(activity.getWorkListAdapter().getCount(), is(workProfileTargets));
}
- @Test @Ignore
+ @Test
public void testWorkTab_selectingWorkTabAppOpensAppInWorkProfile() throws InterruptedException {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
@@ -1947,7 +1947,7 @@
.SharesheetTargetSelectedEvent.SHARESHEET_COPY_TARGET_SELECTED.getId()));
}
- @Test @Ignore
+ @Test
public void testSwitchProfileLogging() throws InterruptedException {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f83f401..0a2670a 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -512,6 +512,8 @@
<permission name="android.permission.NOTIFY_PENDING_SYSTEM_UPDATE" />
<!-- Permission required for GTS test - GtsAssistIntentTestCases -->
<permission name="android.permission.MANAGE_VOICE_KEYPHRASES" />
+ <!-- Permission required for ATS test - CarDevicePolicyManagerTest -->
+ <permission name="android.permission.LOCK_DEVICE" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 9b2effc..d84a24d 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -17,7 +17,9 @@
package android.graphics;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
+import android.hardware.DataSpace.NamedDataSpace;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -348,6 +350,14 @@
}
/**
+ * Retrieve the dataspace associated with the texture image.
+ */
+ @SuppressLint("MethodNameUnits")
+ public @NamedDataSpace long getDataSpace() {
+ return nativeGetDataSpace();
+ }
+
+ /**
* {@code release()} frees all the buffers and puts the SurfaceTexture into the
* 'abandoned' state. Once put in this state the SurfaceTexture can never
* leave it. When in the 'abandoned' state, all methods of the
@@ -416,6 +426,7 @@
private native void nativeFinalize();
private native void nativeGetTransformMatrix(float[] mtx);
private native long nativeGetTimestamp();
+ private native long nativeGetDataSpace();
private native void nativeSetDefaultBufferSize(int width, int height);
private native void nativeUpdateTexImage();
private native void nativeReleaseTexImage();
diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
index 850c551..6fa1a69 100644
--- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
@@ -89,7 +89,7 @@
// specific sensor (the one that hasn't changed), and 2) currently the only
// signal to developers is the UserNotAuthenticatedException, which doesn't
// indicate a specific sensor.
- boolean canUnlockViaBiometrics = true;
+ boolean canUnlockViaBiometrics = biometricSids.length > 0;
for (long sid : biometricSids) {
if (!keySids.contains(sid)) {
canUnlockViaBiometrics = false;
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 0c3a6b2..5161092 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
@@ -95,9 +95,8 @@
// Update bitmap
val fg = InsetDrawable(overflowBtn?.drawable, overflowIconInset)
- bitmap = iconFactory.createBadgedIconBitmap(AdaptiveIconDrawable(
- ColorDrawable(colorAccent), fg),
- null /* user */, true /* shrinkNonAdaptiveIcons */).icon
+ bitmap = iconFactory.createBadgedIconBitmap(
+ AdaptiveIconDrawable(ColorDrawable(colorAccent), fg)).icon
// Update dot path
dotPath = PathParser.createPathFromPathData(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 932f879..91aff3e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -189,9 +189,7 @@
BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon,
b.isImportantConversation());
info.badgeBitmap = badgeBitmapInfo.icon;
- info.bubbleBitmap = iconFactory.createBadgedIconBitmap(bubbleDrawable,
- null /* user */,
- true /* shrinkNonAdaptiveIcons */).icon;
+ info.bubbleBitmap = iconFactory.createBadgedIconBitmap(bubbleDrawable).icon;
// Dot color & placement
Path iconPath = PathParser.createPathFromPathData(
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 0b3b25a..e30e6c5 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
@@ -794,7 +794,8 @@
if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
// Make the stages adjacent to each other so they occlude what's behind them.
- wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
+ wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token,
+ true /* moveTogether */);
wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token);
mTaskOrganizer.applyTransaction(wct);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
index 8d7fbce..a17942f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java
@@ -634,7 +634,8 @@
mUseLegacySplit = mContext.getResources().getBoolean(R.bool.config_useLegacySplit);
final WindowContainerTransaction wct = new WindowContainerTransaction();
// Make the stages adjacent to each other so they occlude what's behind them.
- wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token);
+ wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token,
+ true /* moveTogether */);
// Only sets side stage as launch-adjacent-flag-root when the device is not using legacy
// split to prevent new split behavior confusing users.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index a163f37..413627d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -390,8 +390,7 @@
final ShapeIconFactory factory = new ShapeIconFactory(
SplashscreenContentDrawer.this.mContext,
scaledIconDpi, mFinalIconSize);
- final Bitmap bitmap = factory.createScaledBitmapWithoutShadow(
- iconDrawable, true /* shrinkNonAdaptiveIcons */);
+ final Bitmap bitmap = factory.createScaledBitmapWithoutShadow(iconDrawable);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
createIconDrawable(new BitmapDrawable(bitmap), true);
}
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index bbd4c81..35b6170 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -384,7 +384,16 @@
return base::unexpected(IOError::PAGES_MISSING);
}
- auto offset = dtohl(entry_offset_ptr.value());
+ uint32_t offset;
+ uint16_t res_idx;
+ if (type->flags & ResTable_type::FLAG_SPARSE) {
+ auto sparse_entry = entry_offset_ptr.convert<ResTable_sparseTypeEntry>();
+ offset = dtohs(sparse_entry->offset) * 4u;
+ res_idx = dtohs(sparse_entry->idx);
+ } else {
+ offset = dtohl(entry_offset_ptr.value());
+ res_idx = entry_idx;
+ }
if (offset != ResTable_type::NO_ENTRY) {
auto entry = type.offset(dtohl(type->entriesStart) + offset).convert<ResTable_entry>();
if (!entry) {
@@ -394,7 +403,7 @@
if (dtohl(entry->key.index) == static_cast<uint32_t>(*key_idx)) {
// The package ID will be overridden by the caller (due to runtime assignment of package
// IDs for shared libraries).
- return make_resid(0x00, *type_idx + type_id_offset_ + 1, entry_idx);
+ return make_resid(0x00, *type_idx + type_id_offset_ + 1, res_idx);
}
}
}
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index f356c8130..d214e2d 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -95,6 +95,38 @@
ASSERT_TRUE(LoadedPackage::GetEntry(type.type, entry_index).has_value());
}
+TEST(LoadedArscTest, FindSparseEntryApp) {
+ std::string contents;
+ ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc",
+ &contents));
+
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(),
+ contents.length());
+ ASSERT_THAT(loaded_arsc, NotNull());
+
+ const LoadedPackage* package =
+ loaded_arsc->GetPackageById(get_package_id(sparse::R::string::only_v26));
+ ASSERT_THAT(package, NotNull());
+
+ const uint8_t type_index = get_type_id(sparse::R::string::only_v26) - 1;
+ const uint16_t entry_index = get_entry_id(sparse::R::string::only_v26);
+
+ const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index);
+ ASSERT_THAT(type_spec, NotNull());
+ ASSERT_THAT(type_spec->type_entries.size(), Ge(1u));
+
+ // Ensure that AAPT2 sparsely encoded the v26 config as expected.
+ auto type_entry = std::find_if(
+ type_spec->type_entries.begin(), type_spec->type_entries.end(),
+ [](const TypeSpec::TypeEntry& x) { return x.config.sdkVersion == 26; });
+ ASSERT_NE(type_entry, type_spec->type_entries.end());
+ ASSERT_NE(type_entry->type->flags & ResTable_type::FLAG_SPARSE, 0);
+
+ // Test fetching a resource with only sparsely encoded configs by name.
+ auto id = package->FindEntryByName(u"string", u"only_v26");
+ ASSERT_EQ(id.value(), fix_package_id(sparse::R::string::only_v26, 0));
+}
+
TEST(LoadedArscTest, LoadSharedLibrary) {
std::string contents;
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc",
diff --git a/libs/androidfw/tests/data/sparse/R.h b/libs/androidfw/tests/data/sparse/R.h
index 243e74f..2492dbf 100644
--- a/libs/androidfw/tests/data/sparse/R.h
+++ b/libs/androidfw/tests/data/sparse/R.h
@@ -27,21 +27,22 @@
struct integer {
enum : uint32_t {
foo_0 = 0x7f010000,
- foo_1 = 0x7f010000,
- foo_2 = 0x7f010000,
- foo_3 = 0x7f010000,
- foo_4 = 0x7f010000,
- foo_5 = 0x7f010000,
- foo_6 = 0x7f010000,
- foo_7 = 0x7f010000,
- foo_8 = 0x7f010000,
- foo_9 = 0x7f010000,
+ foo_1 = 0x7f010001,
+ foo_2 = 0x7f010002,
+ foo_3 = 0x7f010003,
+ foo_4 = 0x7f010004,
+ foo_5 = 0x7f010005,
+ foo_6 = 0x7f010006,
+ foo_7 = 0x7f010007,
+ foo_8 = 0x7f010008,
+ foo_9 = 0x7f010009,
};
};
struct string {
enum : uint32_t {
foo_999 = 0x7f0203e7,
+ only_v26 = 0x7f0203e8
};
};
};
diff --git a/libs/androidfw/tests/data/sparse/gen_strings.sh b/libs/androidfw/tests/data/sparse/gen_strings.sh
index e7e1d60..4ea5468 100755
--- a/libs/androidfw/tests/data/sparse/gen_strings.sh
+++ b/libs/androidfw/tests/data/sparse/gen_strings.sh
@@ -14,5 +14,7 @@
fi
done
echo "</resources>" >> $OUTPUT_default
+
+echo " <string name=\"only_v26\">only v26</string>" >> $OUTPUT_v26
echo "</resources>" >> $OUTPUT_v26
diff --git a/libs/androidfw/tests/data/sparse/not_sparse.apk b/libs/androidfw/tests/data/sparse/not_sparse.apk
index 599a370..b08a621 100644
--- a/libs/androidfw/tests/data/sparse/not_sparse.apk
+++ b/libs/androidfw/tests/data/sparse/not_sparse.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml b/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml
index b6f8299..d116087e 100644
--- a/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml
+++ b/libs/androidfw/tests/data/sparse/res/values-v26/strings.xml
@@ -333,4 +333,5 @@
<string name="foo_993">9930</string>
<string name="foo_996">9960</string>
<string name="foo_999">9990</string>
+ <string name="only_v26">only v26</string>
</resources>
diff --git a/libs/androidfw/tests/data/sparse/sparse.apk b/libs/androidfw/tests/data/sparse/sparse.apk
index 1f9bba3..9fd01fb 100644
--- a/libs/androidfw/tests/data/sparse/sparse.apk
+++ b/libs/androidfw/tests/data/sparse/sparse.apk
Binary files differ
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 337b45c..faae9a5 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -33,6 +33,7 @@
import android.app.compat.CompatChanges;
import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeAudioCodecConfig;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
@@ -6768,30 +6769,56 @@
/**
* Returns a list of audio formats that corresponds to encoding formats
- * supported on offload path for A2DP playback.
+ * supported on offload path for A2DP and LE audio playback.
*
+ * @param deviceType Indicates the target device type {@link AudioSystem.DeviceType}
* @return a list of {@link BluetoothCodecConfig} objects containing encoding formats
- * supported for offload A2DP playback
+ * supported for offload A2DP playback or a list of {@link BluetoothLeAudioCodecConfig}
+ * objects containing encoding formats supported for offload LE Audio playback
* @hide
*/
- public List<BluetoothCodecConfig> getHwOffloadEncodingFormatsSupportedForA2DP() {
+ public List<?> getHwOffloadFormatsSupportedForBluetoothMedia(
+ @AudioSystem.DeviceType int deviceType) {
ArrayList<Integer> formatsList = new ArrayList<Integer>();
- ArrayList<BluetoothCodecConfig> codecConfigList = new ArrayList<BluetoothCodecConfig>();
+ ArrayList<BluetoothCodecConfig> a2dpCodecConfigList = new ArrayList<BluetoothCodecConfig>();
+ ArrayList<BluetoothLeAudioCodecConfig> leAudioCodecConfigList =
+ new ArrayList<BluetoothLeAudioCodecConfig>();
- int status = AudioSystem.getHwOffloadEncodingFormatsSupportedForA2DP(formatsList);
+ if (deviceType != AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP
+ && deviceType != AudioSystem.DEVICE_OUT_BLE_HEADSET) {
+ throw new IllegalArgumentException(
+ "Illegal devicetype for the getHwOffloadFormatsSupportedForBluetoothMedia");
+ }
+
+ int status = AudioSystem.getHwOffloadFormatsSupportedForBluetoothMedia(deviceType,
+ formatsList);
if (status != AudioManager.SUCCESS) {
- Log.e(TAG, "getHwOffloadEncodingFormatsSupportedForA2DP failed:" + status);
- return codecConfigList;
+ Log.e(TAG, "getHwOffloadFormatsSupportedForBluetoothMedia for deviceType "
+ + deviceType + " failed:" + status);
+ return a2dpCodecConfigList;
}
- for (Integer format : formatsList) {
- int btSourceCodec = AudioSystem.audioFormatToBluetoothSourceCodec(format);
- if (btSourceCodec
- != BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
- codecConfigList.add(new BluetoothCodecConfig(btSourceCodec));
+ if (deviceType == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
+ for (Integer format : formatsList) {
+ int btSourceCodec = AudioSystem.audioFormatToBluetoothSourceCodec(format);
+ if (btSourceCodec != BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
+ a2dpCodecConfigList.add(new BluetoothCodecConfig(btSourceCodec));
+ }
}
+ return a2dpCodecConfigList;
+ } else if (deviceType == AudioSystem.DEVICE_OUT_BLE_HEADSET) {
+ for (Integer format : formatsList) {
+ int btLeAudioCodec = AudioSystem.audioFormatToBluetoothLeAudioSourceCodec(format);
+ if (btLeAudioCodec != BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID) {
+ leAudioCodecConfigList.add(new BluetoothLeAudioCodecConfig.Builder()
+ .setCodecType(btLeAudioCodec)
+ .build());
+ }
+ }
+ return leAudioCodecConfigList;
}
- return codecConfigList;
+ Log.e(TAG, "Input deviceType " + deviceType + " doesn't support.");
+ return a2dpCodecConfigList;
}
// Since we need to calculate the changes since THE LAST NOTIFICATION, and not since the
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 16cb5f4..cc37c38 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -22,6 +22,7 @@
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.bluetooth.BluetoothCodecConfig;
+import android.bluetooth.BluetoothLeAudioCodecConfig;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -236,6 +237,9 @@
public static final int AUDIO_FORMAT_APTX_HD = 0x21000000;
/** @hide */
public static final int AUDIO_FORMAT_LDAC = 0x23000000;
+ /** @hide */
+ public static final int AUDIO_FORMAT_LC3 = 0x2B000000;
+
/** @hide */
@IntDef(flag = false, prefix = "AUDIO_FORMAT_", value = {
@@ -245,11 +249,26 @@
AUDIO_FORMAT_SBC,
AUDIO_FORMAT_APTX,
AUDIO_FORMAT_APTX_HD,
- AUDIO_FORMAT_LDAC }
+ AUDIO_FORMAT_LDAC}
)
@Retention(RetentionPolicy.SOURCE)
public @interface AudioFormatNativeEnumForBtCodec {}
+ /** @hide */
+ @IntDef(flag = false, prefix = "AUDIO_FORMAT_", value = {
+ AUDIO_FORMAT_LC3}
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AudioFormatNativeEnumForBtLeAudioCodec {}
+
+ /** @hide */
+ @IntDef(flag = false, prefix = "DEVICE_", value = {
+ DEVICE_OUT_BLUETOOTH_A2DP,
+ DEVICE_OUT_BLE_HEADSET}
+ )
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeviceType {}
+
/**
* @hide
* Convert audio format enum values to Bluetooth codec values
@@ -271,6 +290,21 @@
/**
* @hide
+ * Convert audio format enum values to Bluetooth LE audio codec values
+ */
+ public static int audioFormatToBluetoothLeAudioSourceCodec(
+ @AudioFormatNativeEnumForBtLeAudioCodec int audioFormat) {
+ switch (audioFormat) {
+ case AUDIO_FORMAT_LC3: return BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3;
+ default:
+ Log.e(TAG, "Unknown audio format 0x" + Integer.toHexString(audioFormat)
+ + " for conversion to BT LE audio codec");
+ return BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_INVALID;
+ }
+ }
+
+ /**
+ * @hide
* Convert a Bluetooth codec to an audio format enum
* @param btCodec the codec to convert.
* @return the audio format, or {@link #AUDIO_FORMAT_DEFAULT} if unknown
@@ -1761,10 +1795,10 @@
/**
* @hide
- * Returns a list of audio formats (codec) supported on the A2DP offload path.
+ * Returns a list of audio formats (codec) supported on the A2DP and LE audio offload path.
*/
- public static native int getHwOffloadEncodingFormatsSupportedForA2DP(
- ArrayList<Integer> formatList);
+ public static native int getHwOffloadFormatsSupportedForBluetoothMedia(
+ @DeviceType int deviceType, ArrayList<Integer> formatList);
/** @hide */
public static native int setSurroundFormatEnabled(int audioFormat, boolean enabled);
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index e979a1b5..5261555 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -17,9 +17,12 @@
package android.media;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Rect;
+import android.hardware.DataSpace;
+import android.hardware.DataSpace.NamedDataSpace;
import android.hardware.HardwareBuffer;
import java.nio.ByteBuffer;
@@ -280,6 +283,31 @@
return;
}
+ private @NamedDataSpace long mDataSpace = DataSpace.DATASPACE_UNKNOWN;
+
+ /**
+ * Get the dataspace associated with this frame.
+ */
+ @SuppressLint("MethodNameUnits")
+ public @NamedDataSpace long getDataSpace() {
+ throwISEIfImageIsInvalid();
+ return mDataSpace;
+ }
+
+ /**
+ * Set the dataspace associated with this frame.
+ * <p>
+ * If dataspace for an image is not set, dataspace value depends on {@link android.view.Surface}
+ * that is provided in the {@link ImageWriter} constructor.
+ * </p>
+ *
+ * @param dataSpace The Dataspace to be set for this image
+ */
+ public void setDataSpace(@NamedDataSpace long dataSpace) {
+ throwISEIfImageIsInvalid();
+ mDataSpace = dataSpace;
+ }
+
private Rect mCropRect;
/**
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 5656dff..bd0f32e 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -16,7 +16,6 @@
package android.media;
-import android.annotation.CallbackExecutor;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.graphics.GraphicBuffer;
@@ -28,7 +27,6 @@
import android.hardware.camera2.MultiResolutionImageReader;
import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.view.Surface;
import dalvik.system.VMRuntime;
diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java
index 1b74367..1fc2cf9 100644
--- a/media/java/android/media/ImageWriter.java
+++ b/media/java/android/media/ImageWriter.java
@@ -23,9 +23,9 @@
import android.graphics.ImageFormat.Format;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.SurfaceUtils;
-import android.hardware.HardwareBuffer;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -454,8 +454,9 @@
}
Rect crop = image.getCropRect();
- nativeQueueInputImage(mNativeContext, image, image.getTimestamp(), crop.left, crop.top,
- crop.right, crop.bottom, image.getTransform(), image.getScalingMode());
+ nativeQueueInputImage(mNativeContext, image, image.getTimestamp(), image.getDataSpace(),
+ crop.left, crop.top, crop.right, crop.bottom, image.getTransform(),
+ image.getScalingMode());
/**
* Only remove and cleanup the Images that are owned by this
@@ -642,13 +643,13 @@
Rect crop = image.getCropRect();
if (image.getNativeContext() != 0) {
nativeAttachAndQueueImage(mNativeContext, image.getNativeContext(), image.getFormat(),
- image.getTimestamp(), crop.left, crop.top, crop.right, crop.bottom,
- image.getTransform(), image.getScalingMode());
+ image.getTimestamp(), image.getDataSpace(), crop.left, crop.top, crop.right,
+ crop.bottom, image.getTransform(), image.getScalingMode());
} else {
GraphicBuffer gb = GraphicBuffer.createFromHardwareBuffer(image.getHardwareBuffer());
nativeAttachAndQueueGraphicBuffer(mNativeContext, gb, image.getFormat(),
- image.getTimestamp(), crop.left, crop.top, crop.right, crop.bottom,
- image.getTransform(), image.getScalingMode());
+ image.getTimestamp(), image.getDataSpace(), crop.left, crop.top, crop.right,
+ crop.bottom, image.getTransform(), image.getScalingMode());
gb.destroy();
image.close();
}
@@ -976,15 +977,15 @@
private synchronized native void nativeDequeueInputImage(long nativeCtx, Image wi);
private synchronized native void nativeQueueInputImage(long nativeCtx, Image image,
- long timestampNs, int left, int top, int right, int bottom, int transform,
- int scalingMode);
+ long timestampNs, long dataSpace, int left, int top, int right, int bottom,
+ int transform, int scalingMode);
private synchronized native int nativeAttachAndQueueImage(long nativeCtx,
- long imageNativeBuffer, int imageFormat, long timestampNs, int left,
- int top, int right, int bottom, int transform, int scalingMode);
+ long imageNativeBuffer, int imageFormat, long timestampNs, long dataSpace,
+ int left, int top, int right, int bottom, int transform, int scalingMode);
private synchronized native int nativeAttachAndQueueGraphicBuffer(long nativeCtx,
- GraphicBuffer graphicBuffer, int imageFormat, long timestampNs, int left,
- int top, int right, int bottom, int transform, int scalingMode);
+ GraphicBuffer graphicBuffer, int imageFormat, long timestampNs, long dataSpace,
+ int left, int top, int right, int bottom, int transform, int scalingMode);
private synchronized native void cancelImage(long nativeCtx, Image image);
diff --git a/media/java/android/media/tv/AitInfo.aidl b/media/java/android/media/tv/AitInfo.aidl
new file mode 100644
index 0000000..b7d9fe8
--- /dev/null
+++ b/media/java/android/media/tv/AitInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.media.tv;
+
+parcelable AitInfo;
diff --git a/media/java/android/media/tv/AitInfo.java b/media/java/android/media/tv/AitInfo.java
new file mode 100644
index 0000000..5f8487d
--- /dev/null
+++ b/media/java/android/media/tv/AitInfo.java
@@ -0,0 +1,87 @@
+/*
+ * 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 android.media.tv;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * AIT info.
+ * @hide
+ */
+public final class AitInfo implements Parcelable {
+ static final String TAG = "AitInfo";
+
+ public static final Creator<AitInfo> CREATOR = new Creator<AitInfo>() {
+ @Override
+ public AitInfo createFromParcel(Parcel in) {
+ return new AitInfo(in);
+ }
+
+ @Override
+ public AitInfo[] newArray(int size) {
+ return new AitInfo[size];
+ }
+ };
+
+ private final int mType;
+ private final int mVersion;
+
+ private AitInfo(Parcel in) {
+ mType = in.readInt();
+ mVersion = in.readInt();
+ }
+
+ /**
+ * Constructs AIT info.
+ */
+ public AitInfo(int type, int version) {
+ mType = type;
+ mVersion = version;
+ }
+
+ /**
+ * Gets type.
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Gets version.
+ */
+ public int getVersion() {
+ return mVersion;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeInt(mVersion);
+ }
+
+ @Override
+ public String toString() {
+ return "type=" + mType + ";version=" + mVersion;
+ }
+}
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index 5dad633..6f7db4a 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -17,12 +17,13 @@
package android.media.tv;
import android.content.ComponentName;
+import android.media.tv.AitInfo;
+import android.media.tv.BroadcastInfoResponse;
import android.media.tv.ITvInputSession;
import android.net.Uri;
import android.media.tv.TvTrackInfo;
import android.os.Bundle;
import android.view.InputChannel;
-import android.media.tv.BroadcastInfoResponse;
/**
* Interface a client of the ITvInputManager implements, to identify itself and receive information
@@ -44,9 +45,10 @@
void onTimeShiftStatusChanged(int status, int seq);
void onTimeShiftStartPositionChanged(long timeMs, int seq);
void onTimeShiftCurrentPositionChanged(long timeMs, int seq);
+ void onAitInfoUpdated(in AitInfo aitInfo, int seq);
+ void onTuned(in Uri channelUri, int seq);
// For the recording session
- void onTuned(int seq, in Uri channelUri);
void onRecordingStopped(in Uri recordedProgramUri, int seq);
void onError(int error, int seq);
diff --git a/media/java/android/media/tv/ITvInputManager.aidl b/media/java/android/media/tv/ITvInputManager.aidl
index eaf89ba..0070898 100644
--- a/media/java/android/media/tv/ITvInputManager.aidl
+++ b/media/java/android/media/tv/ITvInputManager.aidl
@@ -20,6 +20,7 @@
import android.content.Intent;
import android.graphics.Rect;
import android.media.PlaybackParams;
+import android.media.tv.BroadcastInfoRequest;
import android.media.tv.DvbDeviceInfo;
import android.media.tv.ITvInputClient;
import android.media.tv.ITvInputHardware;
@@ -35,7 +36,6 @@
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.view.Surface;
-import android.media.tv.BroadcastInfoRequest;
/**
* Interface to the TV input manager service.
@@ -73,6 +73,8 @@
void setCaptionEnabled(in IBinder sessionToken, boolean enabled, int userId);
void selectTrack(in IBinder sessionToken, int type, in String trackId, int userId);
+ void setIAppNotificationEnabled(in IBinder sessionToken, boolean enabled, int userId);
+
void sendAppPrivateCommand(in IBinder sessionToken, in String action, in Bundle data,
int userId);
diff --git a/media/java/android/media/tv/ITvInputSession.aidl b/media/java/android/media/tv/ITvInputSession.aidl
index 1eab650..984a551 100644
--- a/media/java/android/media/tv/ITvInputSession.aidl
+++ b/media/java/android/media/tv/ITvInputSession.aidl
@@ -18,11 +18,11 @@
import android.graphics.Rect;
import android.media.PlaybackParams;
+import android.media.tv.BroadcastInfoRequest;
import android.media.tv.TvTrackInfo;
import android.net.Uri;
import android.os.Bundle;
import android.view.Surface;
-import android.media.tv.BroadcastInfoRequest;
/**
* Sub-interface of ITvInputService which is created per session and has its own context.
@@ -41,6 +41,8 @@
void setCaptionEnabled(boolean enabled);
void selectTrack(int type, in String trackId);
+ void setIAppNotificationEnabled(boolean enable);
+
void appPrivateCommand(in String action, in Bundle data);
void createOverlayView(in IBinder windowToken, in Rect frame);
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index d857c00..9a0aaa3 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -16,11 +16,12 @@
package android.media.tv;
+import android.media.tv.AitInfo;
+import android.media.tv.BroadcastInfoResponse;
import android.media.tv.ITvInputSession;
import android.net.Uri;
import android.media.tv.TvTrackInfo;
import android.os.Bundle;
-import android.media.tv.BroadcastInfoResponse;
/**
* Helper interface for ITvInputSession to allow the TV input to notify the system service when a
@@ -41,6 +42,7 @@
void onTimeShiftStatusChanged(int status);
void onTimeShiftStartPositionChanged(long timeMs);
void onTimeShiftCurrentPositionChanged(long timeMs);
+ void onAitInfoUpdated(in AitInfo aitInfo);
// For the recording session
void onTuned(in Uri channelUri);
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 4257145..6539472 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -71,6 +71,7 @@
private static final int DO_PAUSE_RECORDING = 22;
private static final int DO_RESUME_RECORDING = 23;
private static final int DO_REQUEST_BROADCAST_INFO = 24;
+ private static final int DO_SET_IAPP_NOTIFICATION_ENABLED = 25;
private final boolean mIsRecordingSession;
private final HandlerCaller mCaller;
@@ -239,6 +240,10 @@
mTvInputSessionImpl.requestBroadcastInfo((BroadcastInfoRequest) msg.obj);
break;
}
+ case DO_SET_IAPP_NOTIFICATION_ENABLED: {
+ mTvInputSessionImpl.setIAppNotificationEnabled((Boolean) msg.obj);
+ break;
+ }
default: {
Log.w(TAG, "Unhandled message code: " + msg.what);
break;
@@ -307,6 +312,12 @@
}
@Override
+ public void setIAppNotificationEnabled(boolean enabled) {
+ mCaller.executeOrSendMessage(
+ mCaller.obtainMessageO(DO_SET_IAPP_NOTIFICATION_ENABLED, enabled));
+ }
+
+ @Override
public void appPrivateCommand(String action, Bundle data) {
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action,
data));
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index bafb03b..b655a61 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -627,14 +627,20 @@
public void onTimeShiftCurrentPositionChanged(Session session, long timeMs) {
}
- // For the recording session only
/**
- * This is called when the recording session has been tuned to the given channel and is
- * ready to start recording.
+ * This is called when AIT info is updated.
+ * @param session A {@link TvInputManager.Session} associated with this callback.
+ * @param aitInfo The current AIT info.
+ */
+ public void onAitInfoUpdated(Session session, AitInfo aitInfo) {
+ }
+
+ /**
+ * This is called when the session has been tuned to the given channel.
*
* @param channelUri The URI of a channel.
*/
- void onTuned(Session session, Uri channelUri) {
+ public void onTuned(Session session, Uri channelUri) {
}
// For the recording session only
@@ -807,12 +813,23 @@
});
}
- // For the recording session only
+ void postAitInfoUpdated(final AitInfo aitInfo) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onAitInfoUpdated(mSession, aitInfo);
+ }
+ });
+ }
+
void postTuned(final Uri channelUri) {
mHandler.post(new Runnable() {
@Override
public void run() {
mSessionCallback.onTuned(mSession, channelUri);
+ if (mSession.mIAppNotificationEnabled && mSession.getIAppSession() != null) {
+ mSession.getIAppSession().notifyTuned(channelUri);
+ }
}
});
}
@@ -838,12 +855,16 @@
}
void postBroadcastInfoResponse(final BroadcastInfoResponse response) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mSession.getIAppSession().notifyBroadcastInfoResponse(response);
- }
- });
+ if (mSession.mIAppNotificationEnabled) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mSession.getIAppSession() != null) {
+ mSession.getIAppSession().notifyBroadcastInfoResponse(response);
+ }
+ }
+ });
+ }
}
}
@@ -1209,7 +1230,19 @@
}
@Override
- public void onTuned(int seq, Uri channelUri) {
+ public void onAitInfoUpdated(AitInfo aitInfo, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postAitInfoUpdated(aitInfo);
+ }
+ }
+
+ @Override
+ public void onTuned(Uri channelUri, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
if (record == null) {
@@ -1217,6 +1250,13 @@
return;
}
record.postTuned(channelUri);
+ // TODO: synchronized and wrap the channelUri
+ if (record.mSession.mIAppNotificationEnabled) {
+ TvIAppManager.Session iappSession = record.mSession.mIAppSession;
+ if (iappSession != null) {
+ iappSession.notifyTuned(channelUri);
+ }
+ }
}
}
@@ -2068,6 +2108,7 @@
private int mVideoHeight;
private TvIAppManager.Session mIAppSession;
+ private boolean mIAppNotificationEnabled = false;
private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId,
int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
@@ -2340,6 +2381,25 @@
}
/**
+ * Enables interactive app notification.
+ * @param enabled {@code true} if you want to enable interactive app notifications.
+ * {@code false} otherwise.
+ * @hide
+ */
+ public void setIAppNotificationEnabled(boolean enabled) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.setIAppNotificationEnabled(mToken, enabled, mUserId);
+ mIAppNotificationEnabled = enabled;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Responds to onTracksChanged() and updates the internal track information. Returns true if
* there is an update.
*/
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index d285b13..6743dd6 100755
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -828,6 +828,27 @@
}
/**
+ * Notifies AIT info updated.
+ * @hide
+ */
+ public void notifyAitInfoUpdated(@NonNull final AitInfo aitInfo) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) Log.d(TAG, "notifyAitInfoUpdated");
+ if (mSessionCallback != null) {
+ mSessionCallback.onAitInfoUpdated(aitInfo);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in notifyAitInfoUpdated", e);
+ }
+ }
+ });
+ }
+
+ /**
* Assigns a size and position to the surface passed in {@link #onSetSurface}. The position
* is relative to the overlay view that sits on top of this surface.
*
@@ -1021,6 +1042,14 @@
}
/**
+ * Enables or disables interactive app notification.
+ * @param enabled {@code true} to enable, {@code false} to disable.
+ * @hide
+ */
+ public void onSetIAppNotificationEnabled(boolean enabled) {
+ }
+
+ /**
* Processes a private command sent from the application to the TV input. This can be used
* to provide domain-specific features that are only known between certain TV inputs and
* their clients.
@@ -1369,6 +1398,13 @@
}
/**
+ * Calls {@link #onSetIAppNotificationEnabled}.
+ */
+ void setIAppNotificationEnabled(boolean enabled) {
+ onSetIAppNotificationEnabled(enabled);
+ }
+
+ /**
* Calls {@link #onAppPrivateCommand}.
*/
void appPrivateCommand(String action, Bundle data) {
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
index 180e2bd..87d7a0b 100644
--- a/media/java/android/media/tv/TvRecordingClient.java
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -472,7 +472,7 @@
}
@Override
- void onTuned(TvInputManager.Session session, Uri channelUri) {
+ public void onTuned(TvInputManager.Session session, Uri channelUri) {
if (DEBUG) {
Log.d(TAG, "onTuned()");
}
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 6994d28..4a12cd7 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -34,8 +34,6 @@
import android.media.tv.TvInputManager.Session;
import android.media.tv.TvInputManager.Session.FinishedInputEventCallback;
import android.media.tv.TvInputManager.SessionCallback;
-import android.media.tv.interactive.TvIAppManager;
-import android.media.tv.interactive.TvIAppView;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -483,6 +481,18 @@
}
/**
+ * Enables interactive app notification.
+ * @param enabled {@code true} if you want to enable interactive app notifications.
+ * {@code false} otherwise.
+ * @hide
+ */
+ public void setIAppNotificationEnabled(boolean enabled) {
+ if (mSession != null) {
+ mSession.setIAppNotificationEnabled(enabled);
+ }
+ }
+
+ /**
* Plays a given recorded TV program.
*
* @param inputId The ID of the TV input that created the given recorded program.
@@ -1050,6 +1060,24 @@
public void onTimeShiftStatusChanged(
String inputId, @TvInputManager.TimeShiftStatus int status) {
}
+
+ /**
+ * This is called when the AIT info has been updated.
+ *
+ * @param aitInfo The current AIT info.
+ * @hide
+ */
+ public void onAitInfoUpdated(String inputId, AitInfo aitInfo) {
+ }
+
+ /**
+ * This is called when the session has been tuned to the given channel.
+ *
+ * @param channelUri The URI of a channel.
+ * @hide
+ */
+ public void onTuned(String inputId, Uri channelUri) {
+ }
}
/**
@@ -1346,5 +1374,33 @@
mTimeShiftPositionCallback.onTimeShiftCurrentPositionChanged(mInputId, timeMs);
}
}
+
+ @Override
+ public void onAitInfoUpdated(Session session, AitInfo aitInfo) {
+ if (DEBUG) {
+ Log.d(TAG, "onAitInfoUpdated(aitInfo=" + aitInfo + ")");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onAitInfoUpdated - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onAitInfoUpdated(mInputId, aitInfo);
+ }
+ }
+
+ @Override
+ public void onTuned(Session session, Uri channelUri) {
+ if (DEBUG) {
+ Log.d(TAG, "onTuned(channelUri=" + channelUri + ")");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onTuned - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onTuned(mInputId, channelUri);
+ }
+ }
}
}
diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
index 39c438a..9fc1fe7 100644
--- a/media/java/android/media/tv/interactive/ITvIAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
@@ -17,6 +17,7 @@
package android.media.tv.interactive;
import android.media.tv.BroadcastInfoRequest;
+import android.media.tv.BroadcastInfoRequest;
import android.view.InputChannel;
/**
diff --git a/media/java/android/media/tv/interactive/ITvIAppManager.aidl b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
index 25e1ace..cd87a09 100644
--- a/media/java/android/media/tv/interactive/ITvIAppManager.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppManager.aidl
@@ -16,10 +16,12 @@
package android.media.tv.interactive;
+import android.graphics.Rect;
+import android.media.tv.BroadcastInfoResponse;
import android.media.tv.interactive.ITvIAppClient;
import android.media.tv.interactive.ITvIAppManagerCallback;
import android.media.tv.interactive.TvIAppInfo;
-import android.media.tv.BroadcastInfoResponse;
+import android.net.Uri;
import android.view.Surface;
/**
@@ -28,16 +30,23 @@
*/
interface ITvIAppManager {
List<TvIAppInfo> getTvIAppServiceList(int userId);
+ void prepare(String tiasId, int type, int userId);
void startIApp(in IBinder sessionToken, int userId);
void createSession(
in ITvIAppClient client, in String iAppServiceId, int type, int seq, int userId);
void releaseSession(in IBinder sessionToken, int userId);
+ void notifyTuned(in IBinder sessionToken, in Uri channelUri, int userId);
void setSurface(in IBinder sessionToken, in Surface surface, int userId);
void dispatchSurfaceChanged(in IBinder sessionToken, int format, int width, int height,
int userId);
void notifyBroadcastInfoResponse(in IBinder sessionToken, in BroadcastInfoResponse response,
int UserId);
+ void createMediaView(in IBinder sessionToken, in IBinder windowToken, in Rect frame,
+ int userId);
+ void relayoutMediaView(in IBinder sessionToken, in Rect frame, int userId);
+ void removeMediaView(in IBinder sessionToken, int userId);
+
void registerCallback(in ITvIAppManagerCallback callback, int userId);
void unregisterCallback(in ITvIAppManagerCallback callback, int userId);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppService.aidl b/media/java/android/media/tv/interactive/ITvIAppService.aidl
index 1dee9cc..af15dd8 100644
--- a/media/java/android/media/tv/interactive/ITvIAppService.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppService.aidl
@@ -30,4 +30,5 @@
void unregisterCallback(in ITvIAppServiceCallback callback);
void createSession(in InputChannel channel, in ITvIAppSessionCallback callback,
in String iAppServiceId, int type);
+ void prepare(int type);
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppSession.aidl b/media/java/android/media/tv/interactive/ITvIAppSession.aidl
index 440b3d30..0d37a2b 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSession.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppSession.aidl
@@ -16,6 +16,9 @@
package android.media.tv.interactive;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.media.tv.BroadcastInfoResponse;
import android.view.Surface;
import android.media.tv.BroadcastInfoResponse;
@@ -26,7 +29,12 @@
oneway interface ITvIAppSession {
void startIApp();
void release();
+ void notifyTuned(in Uri channelUri);
void setSurface(in Surface surface);
void dispatchSurfaceChanged(int format, int width, int height);
void notifyBroadcastInfoResponse(in BroadcastInfoResponse response);
+
+ void createMediaView(in IBinder windowToken, in Rect frame);
+ void relayoutMediaView(in Rect frame);
+ void removeMediaView();
}
\ No newline at end of file
diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
index d308463..d2b966e 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
@@ -16,6 +16,7 @@
package android.media.tv.interactive;
+import android.media.tv.BroadcastInfoRequest;
import android.media.tv.interactive.ITvIAppSession;
import android.media.tv.BroadcastInfoRequest;
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java
index ae35edc..fed9769 100644
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvIAppManager.java
@@ -20,9 +20,11 @@
import android.annotation.Nullable;
import android.annotation.SystemService;
import android.content.Context;
+import android.graphics.Rect;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.TvInputManager;
+import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -35,6 +37,7 @@
import android.view.InputEvent;
import android.view.InputEventSender;
import android.view.Surface;
+import android.view.View;
import com.android.internal.util.Preconditions;
@@ -332,6 +335,18 @@
}
/**
+ * Prepares TV IApp service for the given type.
+ * @hide
+ */
+ public void prepare(String tvIAppServiceId, int type) {
+ try {
+ mService.prepare(tvIAppServiceId, type, 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.
@@ -443,6 +458,67 @@
}
/**
+ * Creates a media view. Once the media view is created, {@link #relayoutMediaView}
+ * should be called whenever the layout of its containing view is changed.
+ * {@link #removeMediaView()} should be called to remove the media view.
+ * Since a session can have only one media view, this method should be called only once
+ * or it can be called again after calling {@link #removeMediaView()}.
+ *
+ * @param view A view for interactive app.
+ * @param frame A position of the media view.
+ * @throws IllegalStateException if {@code view} is not attached to a window.
+ */
+ void createMediaView(@NonNull View view, @NonNull Rect frame) {
+ Preconditions.checkNotNull(view);
+ Preconditions.checkNotNull(frame);
+ if (view.getWindowToken() == null) {
+ throw new IllegalStateException("view must be attached to a window");
+ }
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.createMediaView(mToken, view.getWindowToken(), frame, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Relayouts the current media view.
+ *
+ * @param frame A new position of the media view.
+ */
+ void relayoutMediaView(@NonNull Rect frame) {
+ Preconditions.checkNotNull(frame);
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.relayoutMediaView(mToken, frame, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes the current media view.
+ */
+ void removeMediaView() {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.removeMediaView(mToken, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Notifies of any structural changes (format or size) of the surface passed in
* {@link #setSurface}.
*
@@ -533,6 +609,21 @@
releaseInternal();
}
+ /**
+ * Notifies IAPP session when a channels is tuned.
+ */
+ public void notifyTuned(Uri channelUri) {
+ if (mToken == null) {
+ Log.w(TAG, "The session has been already released");
+ return;
+ }
+ try {
+ mService.notifyTuned(mToken, channelUri, mUserId);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private void flushPendingEventsLocked() {
mHandler.removeMessages(InputEventHandler.MSG_FLUSH_INPUT_EVENT);
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
index fe087ca..027b890 100644
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -20,18 +20,25 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.app.ActivityManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
+import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
+import android.view.Gravity;
import android.view.InputChannel;
import android.view.InputDevice;
import android.view.InputEvent;
@@ -39,6 +46,9 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
import com.android.internal.os.SomeArgs;
@@ -52,6 +62,8 @@
private static final boolean DEBUG = false;
private static final String TAG = "TvIAppService";
+ private static final int DETACH_MEDIA_VIEW_TIMEOUT_MS = 5000;
+
// TODO: cleanup and unhide APIs.
/**
@@ -106,10 +118,23 @@
mServiceHandler.obtainMessage(ServiceHandler.DO_CREATE_SESSION, args)
.sendToTarget();
}
+
+ @Override
+ public void prepare(int type) {
+ onPrepare(type);
+ }
};
return tvIAppServiceBinder;
}
+ /**
+ * Prepares TV IApp service for the given type.
+ * @hide
+ */
+ public void onPrepare(int type) {
+ // TODO: make it abstract when unhide
+ }
+
/**
* Returns a concrete implementation of {@link Session}.
@@ -141,8 +166,16 @@
private final List<Runnable> mPendingActions = new ArrayList<>();
private final Context mContext;
- private final Handler mHandler;
+ final Handler mHandler;
+ private final WindowManager mWindowManager;
+ private WindowManager.LayoutParams mWindowParams;
private Surface mSurface;
+ private FrameLayout mMediaViewContainer;
+ private View mMediaView;
+ private MediaViewCleanUpTask mMediaViewCleanUpTask;
+ private boolean mMediaViewEnabled;
+ private IBinder mWindowToken;
+ private Rect mMediaFrame;
/**
* Creates a new Session.
@@ -151,10 +184,41 @@
*/
public Session(Context context) {
mContext = context;
+ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mHandler = new Handler(context.getMainLooper());
}
/**
+ * Enables or disables the media view.
+ *
+ * <p>By default, the media view is disabled. Must be called explicitly after the
+ * session is created to enable the media view.
+ *
+ * <p>The TV IApp service can disable its media view when needed.
+ *
+ * @param enable {@code true} if you want to enable the media view. {@code false}
+ * otherwise.
+ */
+ public void setMediaViewEnabled(final boolean enable) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (enable == mMediaViewEnabled) {
+ return;
+ }
+ mMediaViewEnabled = enable;
+ if (enable) {
+ if (mWindowToken != null) {
+ createMediaView(mWindowToken, mMediaFrame);
+ }
+ } else {
+ removeMediaView(false);
+ }
+ }
+ });
+ }
+
+ /**
* Starts TvIAppService session.
* @hide
*/
@@ -187,11 +251,27 @@
}
/**
- * Called when a broadcast info response is received from TIS.
+ * Called when the size of the media view is changed by the application.
*
- * @param response response received from TIS.
+ * <p>This is always called at least once when the session is created regardless of whether
+ * the media view is enabled or not. The media view container size is the same as the
+ * containing {@link TvIAppView}. Note that the size of the underlying surface can be
+ * different if the surface was changed by calling {@link #layoutSurface}.
+ *
+ * @param width The width of the media view.
+ * @param height The height of the media view.
*/
- public void onNotifyBroadcastInfoResponse(BroadcastInfoResponse response) {
+ public void onMediaViewSizeChanged(int width, int height) {
+ }
+
+ /**
+ * Called when the application requests to create an media view. Each session
+ * implementation can override this method and return its own view.
+ *
+ * @return a view attached to the media window
+ */
+ public View onCreateMediaView() {
+ return null;
}
/**
@@ -202,6 +282,20 @@
}
/**
+ * Called when the corresponding TV input tuned to a channel.
+ * @hide
+ */
+ public void onTuned(Uri channelUri) {
+ }
+
+ /**
+ * Called when a broadcast info response is received.
+ * @hide
+ */
+ public void onBroadcastInfoResponse(BroadcastInfoResponse response) {
+ }
+
+ /**
* TODO: JavaDoc of APIs related to input events.
* @hide
*/
@@ -288,6 +382,10 @@
});
}
+ /**
+ * Requests broadcast related information from the related TV input.
+ * @param request
+ */
public void requestBroadcastInfo(@NonNull final BroadcastInfoRequest request) {
executeOrPostRunnableOnMainThread(new Runnable() {
@MainThread
@@ -318,6 +416,32 @@
mSurface.release();
mSurface = null;
}
+ synchronized (mLock) {
+ mSessionCallback = null;
+ mPendingActions.clear();
+ }
+ // Removes the media view lastly so that any hanging on the main thread can be handled
+ // in {@link #scheduleMediaViewCleanup}.
+ removeMediaView(true);
+ }
+
+ void notifyTuned(Uri channelUri) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyTuned (channelUri=" + channelUri + ")");
+ }
+ onTuned(channelUri);
+ }
+
+
+ /**
+ * Calls {@link #onBroadcastInfoResponse}.
+ */
+ void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyBroadcastInfoResponse (requestId="
+ + response.getRequestId() + ")");
+ }
+ onBroadcastInfoResponse(response);
}
/**
@@ -386,18 +510,6 @@
onSurfaceChanged(format, width, height);
}
- /**
- *
- * Calls {@link #notifyBroadcastInfoResponse}.
- */
- void notifyBroadcastInfoResponse(BroadcastInfoResponse response) {
- if (DEBUG) {
- Log.d(TAG, "notifyBroadcastInfoResponse (requestId="
- + response.getRequestId() + ")");
- }
- onNotifyBroadcastInfoResponse(response);
- }
-
private void executeOrPostRunnableOnMainThread(Runnable action) {
synchronized (mLock) {
if (mSessionCallback == null) {
@@ -413,6 +525,137 @@
}
}
}
+
+ /**
+ * Creates an media view. This calls {@link #onCreateMediaView} to get a view to attach
+ * to the media window.
+ *
+ * @param windowToken A window token of the application.
+ * @param frame A position of the media view.
+ */
+ void createMediaView(IBinder windowToken, Rect frame) {
+ if (mMediaViewContainer != null) {
+ removeMediaView(false);
+ }
+ if (DEBUG) Log.d(TAG, "create media view(" + frame + ")");
+ mWindowToken = windowToken;
+ mMediaFrame = frame;
+ onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top);
+ if (!mMediaViewEnabled) {
+ return;
+ }
+ mMediaView = onCreateMediaView();
+ if (mMediaView == null) {
+ return;
+ }
+ if (mMediaViewCleanUpTask != null) {
+ mMediaViewCleanUpTask.cancel(true);
+ mMediaViewCleanUpTask = null;
+ }
+ // Creates a container view to check hanging on the media view detaching.
+ // Adding/removing the media view to/from the container make the view attach/detach
+ // logic run on the main thread.
+ mMediaViewContainer = new FrameLayout(mContext.getApplicationContext());
+ mMediaViewContainer.addView(mMediaView);
+
+ int type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
+ // We make the overlay view non-focusable and non-touchable so that
+ // the application that owns the window token can decide whether to consume or
+ // dispatch the input events.
+ int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+ if (ActivityManager.isHighEndGfx()) {
+ flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+ }
+ mWindowParams = new WindowManager.LayoutParams(
+ frame.right - frame.left, frame.bottom - frame.top,
+ frame.left, frame.top, type, flags, PixelFormat.TRANSPARENT);
+ mWindowParams.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+ mWindowParams.gravity = Gravity.START | Gravity.TOP;
+ mWindowParams.token = windowToken;
+ mWindowManager.addView(mMediaViewContainer, mWindowParams);
+ }
+
+ /**
+ * Relayouts the current media view.
+ *
+ * @param frame A new position of the media view.
+ */
+ void relayoutMediaView(Rect frame) {
+ if (DEBUG) Log.d(TAG, "relayoutMediaView(" + frame + ")");
+ if (mMediaFrame == null || mMediaFrame.width() != frame.width()
+ || mMediaFrame.height() != frame.height()) {
+ // Note: relayoutMediaView is called whenever TvIAppView's layout is changed
+ // regardless of setMediaViewEnabled.
+ onMediaViewSizeChanged(frame.right - frame.left, frame.bottom - frame.top);
+ }
+ mMediaFrame = frame;
+ if (!mMediaViewEnabled || mMediaViewContainer == null) {
+ return;
+ }
+ mWindowParams.x = frame.left;
+ mWindowParams.y = frame.top;
+ mWindowParams.width = frame.right - frame.left;
+ mWindowParams.height = frame.bottom - frame.top;
+ mWindowManager.updateViewLayout(mMediaViewContainer, mWindowParams);
+ }
+
+ /**
+ * Removes the current media view.
+ */
+ void removeMediaView(boolean clearWindowToken) {
+ if (DEBUG) Log.d(TAG, "removeMediaView(" + mMediaViewContainer + ")");
+ if (clearWindowToken) {
+ mWindowToken = null;
+ mMediaFrame = null;
+ }
+ if (mMediaViewContainer != null) {
+ // Removes the media view from the view hierarchy in advance so that it can be
+ // cleaned up in the {@link MediaViewCleanUpTask} if the remove process is
+ // hanging.
+ mMediaViewContainer.removeView(mMediaView);
+ mMediaView = null;
+ mWindowManager.removeView(mMediaViewContainer);
+ mMediaViewContainer = null;
+ mWindowParams = null;
+ }
+ }
+
+ /**
+ * Schedules a task which checks whether the media view is detached and kills the process
+ * if it is not. Note that this method is expected to be called in a non-main thread.
+ */
+ void scheduleMediaViewCleanup() {
+ View mediaViewParent = mMediaViewContainer;
+ if (mediaViewParent != null) {
+ mMediaViewCleanUpTask = new MediaViewCleanUpTask();
+ mMediaViewCleanUpTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
+ mediaViewParent);
+ }
+ }
+ }
+
+ private static final class MediaViewCleanUpTask extends AsyncTask<View, Void, Void> {
+ @Override
+ protected Void doInBackground(View... views) {
+ View mediaViewParent = views[0];
+ try {
+ Thread.sleep(DETACH_MEDIA_VIEW_TIMEOUT_MS);
+ } catch (InterruptedException e) {
+ return null;
+ }
+ if (isCancelled()) {
+ return null;
+ }
+ if (mediaViewParent.isAttachedToWindow()) {
+ Log.e(TAG, "Time out on releasing media view. Killing "
+ + mediaViewParent.getContext().getPackageName());
+ android.os.Process.killProcess(Process.myPid());
+ }
+ return null;
+ }
}
/**
@@ -440,10 +683,16 @@
@Override
public void release() {
+ mSessionImpl.scheduleMediaViewCleanup();
mSessionImpl.release();
}
@Override
+ public void notifyTuned(Uri channelUri) {
+ mSessionImpl.notifyTuned(channelUri);
+ }
+
+ @Override
public void setSurface(Surface surface) {
mSessionImpl.setSurface(surface);
}
@@ -458,6 +707,21 @@
mSessionImpl.notifyBroadcastInfoResponse(response);
}
+ @Override
+ public void createMediaView(IBinder windowToken, Rect frame) {
+ mSessionImpl.createMediaView(windowToken, frame);
+ }
+
+ @Override
+ public void relayoutMediaView(Rect frame) {
+ mSessionImpl.relayoutMediaView(frame);
+ }
+
+ @Override
+ public void removeMediaView() {
+ mSessionImpl.removeMediaView(true);
+ }
+
private final class TvIAppEventReceiver extends InputEventReceiver {
TvIAppEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
diff --git a/media/java/android/media/tv/interactive/TvIAppView.java b/media/java/android/media/tv/interactive/TvIAppView.java
index 1b25c23..8031981 100644
--- a/media/java/android/media/tv/interactive/TvIAppView.java
+++ b/media/java/android/media/tv/interactive/TvIAppView.java
@@ -20,6 +20,8 @@
import android.content.Context;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import android.graphics.Rect;
+import android.graphics.RectF;
import android.media.tv.TvInputManager;
import android.media.tv.TvView;
import android.media.tv.interactive.TvIAppManager.Session;
@@ -65,6 +67,9 @@
private int mSurfaceViewTop;
private int mSurfaceViewBottom;
+ private boolean mMediaViewCreated;
+ private Rect mMediaViewFrame;
+
private final AttributeSet mAttrs;
private final int mDefStyleAttr;
private final XmlResourceParser mParser;
@@ -119,6 +124,18 @@
}
@Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ createSessionMediaView();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ removeSessionMediaView();
+ super.onDetachedFromWindow();
+ }
+
+ @Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (DEBUG) {
Log.d(TAG, "onLayout (left=" + left + ", top=" + top + ", right=" + right
@@ -147,6 +164,11 @@
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
mSurfaceView.setVisibility(visibility);
+ if (visibility == View.VISIBLE) {
+ createSessionMediaView();
+ } else {
+ removeSessionMediaView();
+ }
}
private void resetSurfaceView() {
@@ -155,7 +177,12 @@
removeView(mSurfaceView);
}
mSurface = null;
- mSurfaceView = new SurfaceView(getContext(), mAttrs, mDefStyleAttr);
+ mSurfaceView = new SurfaceView(getContext(), mAttrs, mDefStyleAttr) {
+ @Override
+ protected void updateSurface() {
+ super.updateSurface();
+ relayoutSessionMediaView();
+ }};
// The surface view's content should be treated as secure all the time.
mSurfaceView.setSecure(true);
mSurfaceView.getHolder().addCallback(mSurfaceHolderCallback);
@@ -170,6 +197,46 @@
resetInternal();
}
+ private void createSessionMediaView() {
+ // TODO: handle z-order
+ if (mSession == null || !isAttachedToWindow() || mMediaViewCreated) {
+ return;
+ }
+ mMediaViewFrame = getViewFrameOnScreen();
+ mSession.createMediaView(this, mMediaViewFrame);
+ mMediaViewCreated = true;
+ }
+
+ private void removeSessionMediaView() {
+ if (mSession == null || !mMediaViewCreated) {
+ return;
+ }
+ mSession.removeMediaView();
+ mMediaViewCreated = false;
+ mMediaViewFrame = null;
+ }
+
+ private void relayoutSessionMediaView() {
+ if (mSession == null || !isAttachedToWindow() || !mMediaViewCreated) {
+ return;
+ }
+ Rect viewFrame = getViewFrameOnScreen();
+ if (viewFrame.equals(mMediaViewFrame)) {
+ return;
+ }
+ mSession.relayoutMediaView(viewFrame);
+ mMediaViewFrame = viewFrame;
+ }
+
+ private Rect getViewFrameOnScreen() {
+ Rect frame = new Rect();
+ getGlobalVisibleRect(frame);
+ RectF frameF = new RectF(frame);
+ getMatrix().mapRect(frameF);
+ frameF.round(frame);
+ return frame;
+ }
+
private void setSessionSurface(Surface surface) {
if (mSession == null) {
return;
@@ -214,6 +281,7 @@
mSessionCallback = null;
if (mSession != null) {
setSessionSurface(null);
+ removeSessionMediaView();
mUseRequestedSurfaceLayout = false;
mSession.release();
mSession = null;
@@ -287,6 +355,7 @@
dispatchSurfaceChanged(mSurfaceFormat, mSurfaceWidth, mSurfaceHeight);
}
}
+ createSessionMediaView();
} else {
// Failed to create
// Todo: forward error to Tv App
@@ -303,6 +372,8 @@
Log.w(TAG, "onSessionReleased - session not created");
return;
}
+ mMediaViewCreated = false;
+ mMediaViewFrame = null;
mSessionCallback = null;
mSession = null;
}
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 5174c0c..021507c 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -48,6 +48,7 @@
#define ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID "mNativeContext"
#define ANDROID_MEDIA_SURFACEIMAGE_BUFFER_JNI_ID "mNativeBuffer"
#define ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID "mTimestamp"
+#define ANDROID_MEDIA_SURFACEIMAGE_DS_JNI_ID "mDataSpace"
#define ANDROID_MEDIA_SURFACEIMAGE_TF_JNI_ID "mTransform"
#define ANDROID_MEDIA_SURFACEIMAGE_SM_JNI_ID "mScalingMode"
@@ -71,6 +72,7 @@
static struct {
jfieldID mNativeBuffer;
jfieldID mTimestamp;
+ jfieldID mDataSpace;
jfieldID mTransform;
jfieldID mScalingMode;
jfieldID mPlanes;
@@ -319,6 +321,12 @@
"can't find android/graphics/ImageReader.%s",
ANDROID_MEDIA_SURFACEIMAGE_TS_JNI_ID);
+ gSurfaceImageClassInfo.mDataSpace = env->GetFieldID(
+ imageClazz, ANDROID_MEDIA_SURFACEIMAGE_DS_JNI_ID, "J");
+ LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mDataSpace == NULL,
+ "can't find android/graphics/ImageReader.%s",
+ ANDROID_MEDIA_SURFACEIMAGE_DS_JNI_ID);
+
gSurfaceImageClassInfo.mTransform = env->GetFieldID(
imageClazz, ANDROID_MEDIA_SURFACEIMAGE_TF_JNI_ID, "I");
LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mTransform == NULL,
@@ -619,6 +627,8 @@
Image_setBufferItem(env, image, buffer);
env->SetLongField(image, gSurfaceImageClassInfo.mTimestamp,
static_cast<jlong>(buffer->mTimestamp));
+ env->SetLongField(image, gSurfaceImageClassInfo.mDataSpace,
+ static_cast<jlong>(buffer->mDataSpace));
auto transform = buffer->mTransform;
if (buffer->mTransformToDisplayInverse) {
transform |= NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp
index b291ac95b..0a5490d 100644
--- a/media/jni/android_media_ImageWriter.cpp
+++ b/media/jni/android_media_ImageWriter.cpp
@@ -53,6 +53,7 @@
} gImageWriterClassInfo;
static struct {
+ jfieldID mDataSpace;
jfieldID mNativeBuffer;
jfieldID mNativeFenceFd;
jfieldID mPlanes;
@@ -87,6 +88,9 @@
void setBufferHeight(int height) { mHeight = height; }
int getBufferHeight() { return mHeight; }
+ void setBufferDataSpace(android_dataspace dataSpace) { mDataSpace = dataSpace; }
+ android_dataspace getBufferDataSpace() { return mDataSpace; }
+
void queueAttachedFlag(bool isAttached) {
Mutex::Autolock l(mAttachedFlagQueueLock);
mAttachedFlagQueue.push_back(isAttached);
@@ -105,6 +109,7 @@
int mFormat;
int mWidth;
int mHeight;
+ android_dataspace mDataSpace;
// Class for a shared thread used to detach buffers from buffer queues
// to discard buffers after consumers are done using them.
@@ -316,7 +321,7 @@
// -------------------------------Private method declarations--------------
static void Image_setNativeContext(JNIEnv* env, jobject thiz,
- sp<GraphicBuffer> buffer, int fenceFd);
+ sp<GraphicBuffer> buffer, int fenceFd, long dataSpace);
static void Image_getNativeContext(JNIEnv* env, jobject thiz,
GraphicBuffer** buffer, int* fenceFd);
static void Image_unlockIfLocked(JNIEnv* env, jobject thiz);
@@ -328,6 +333,12 @@
jclass imageClazz = env->FindClass("android/media/ImageWriter$WriterSurfaceImage");
LOG_ALWAYS_FATAL_IF(imageClazz == NULL,
"can't find android/media/ImageWriter$WriterSurfaceImage");
+
+ gSurfaceImageClassInfo.mDataSpace = env->GetFieldID(
+ imageClazz, "mDataSpace", "J");
+ LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mDataSpace == NULL,
+ "can't find android/media/ImageWriter$WriterSurfaceImage.mDataSpace");
+
gSurfaceImageClassInfo.mNativeBuffer = env->GetFieldID(
imageClazz, IMAGE_BUFFER_JNI_ID, "J");
LOG_ALWAYS_FATAL_IF(gSurfaceImageClassInfo.mNativeBuffer == NULL,
@@ -465,6 +476,7 @@
jniThrowRuntimeException(env, "Failed to set Surface dataspace");
return 0;
}
+ ctx->setBufferDataSpace(nativeDataspace);
surfaceFormat = userFormat;
}
@@ -544,7 +556,7 @@
// 3. need use lockAsync here, as it will handle the dequeued fence for us automatically.
// Finally, set the native info into image object.
- Image_setNativeContext(env, image, buffer, fenceFd);
+ Image_setNativeContext(env, image, buffer, fenceFd, ctx->getBufferDataSpace());
}
static void ImageWriter_close(JNIEnv* env, jobject thiz, jlong nativeCtx) {
@@ -605,12 +617,12 @@
anw->cancelBuffer(anw.get(), buffer, fenceFd);
- Image_setNativeContext(env, image, NULL, -1);
+ Image_setNativeContext(env, image, NULL, -1, HAL_DATASPACE_UNKNOWN);
}
static void ImageWriter_queueImage(JNIEnv* env, jobject thiz, jlong nativeCtx, jobject image,
- jlong timestampNs, jint left, jint top, jint right, jint bottom, jint transform,
- jint scalingMode) {
+ jlong timestampNs, jlong dataSpace, jint left, jint top, jint right,
+ jint bottom, jint transform, jint scalingMode) {
ALOGV("%s", __FUNCTION__);
JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
if (ctx == NULL || thiz == NULL) {
@@ -642,6 +654,15 @@
return;
}
+ // Set dataSpace
+ ALOGV("dataSpace to be queued: %" PRId64, dataSpace);
+ res = native_window_set_buffers_data_space(
+ anw.get(), static_cast<android_dataspace>(dataSpace));
+ if (res != OK) {
+ jniThrowRuntimeException(env, "Set dataspace failed");
+ return;
+ }
+
// Set crop
android_native_rect_t cropRect;
cropRect.left = left;
@@ -689,12 +710,12 @@
}
// Clear the image native context: end of this image's lifecycle in public API.
- Image_setNativeContext(env, image, NULL, -1);
+ Image_setNativeContext(env, image, NULL, -1, HAL_DATASPACE_UNKNOWN);
}
static status_t attachAndQeueuGraphicBuffer(JNIEnv* env, JNIImageWriterContext *ctx,
- sp<Surface> surface, sp<GraphicBuffer> gb, jlong timestampNs, jint left, jint top,
- jint right, jint bottom, jint transform, jint scalingMode) {
+ sp<Surface> surface, sp<GraphicBuffer> gb, jlong timestampNs, jlong dataSpace,
+ jint left, jint top, jint right, jint bottom, jint transform, jint scalingMode) {
status_t res = OK;
// Step 1. Attach Image
res = surface->attachBuffer(gb.get());
@@ -713,8 +734,8 @@
}
sp < ANativeWindow > anw = surface;
- // Step 2. Set timestamp, crop, transform and scaling mode. Note that we do not need unlock the
- // image because it was not locked.
+ // Step 2. Set timestamp, dataspace, crop, transform and scaling mode.
+ // Note that we do not need unlock the image because it was not locked.
ALOGV("timestamp to be queued: %" PRId64, timestampNs);
res = native_window_set_buffers_timestamp(anw.get(), timestampNs);
if (res != OK) {
@@ -722,6 +743,14 @@
return res;
}
+ ALOGV("dataSpace to be queued: %" PRId64, dataSpace);
+ res = native_window_set_buffers_data_space(
+ anw.get(), static_cast<android_dataspace>(dataSpace));
+ if (res != OK) {
+ jniThrowRuntimeException(env, "Set dataSpace failed");
+ return res;
+ }
+
android_native_rect_t cropRect;
cropRect.left = left;
cropRect.top = top;
@@ -775,8 +804,8 @@
}
static jint ImageWriter_attachAndQueueImage(JNIEnv* env, jobject thiz, jlong nativeCtx,
- jlong nativeBuffer, jint imageFormat, jlong timestampNs, jint left, jint top,
- jint right, jint bottom, jint transform, jint scalingMode) {
+ jlong nativeBuffer, jint imageFormat, jlong timestampNs, jlong dataSpace,
+ jint left, jint top, jint right, jint bottom, jint transform, jint scalingMode) {
ALOGV("%s", __FUNCTION__);
JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
if (ctx == NULL || thiz == NULL) {
@@ -801,12 +830,12 @@
return -1;
}
- return attachAndQeueuGraphicBuffer(env, ctx, surface, buffer->mGraphicBuffer, timestampNs, left,
- top, right, bottom, transform, scalingMode);
+ return attachAndQeueuGraphicBuffer(env, ctx, surface, buffer->mGraphicBuffer, timestampNs,
+ dataSpace, left, top, right, bottom, transform, scalingMode);
}
static jint ImageWriter_attachAndQueueGraphicBuffer(JNIEnv* env, jobject thiz, jlong nativeCtx,
- jobject buffer, jint format, jlong timestampNs, jint left, jint top,
+ jobject buffer, jint format, jlong timestampNs, jlong dataSpace, jint left, jint top,
jint right, jint bottom, jint transform, jint scalingMode) {
ALOGV("%s", __FUNCTION__);
JNIImageWriterContext* const ctx = reinterpret_cast<JNIImageWriterContext *>(nativeCtx);
@@ -830,9 +859,8 @@
"Trying to attach an invalid graphic buffer");
return -1;
}
-
- return attachAndQeueuGraphicBuffer(env, ctx, surface, graphicBuffer, timestampNs, left,
- top, right, bottom, transform, scalingMode);
+ return attachAndQeueuGraphicBuffer(env, ctx, surface, graphicBuffer, timestampNs,
+ dataSpace, left, top, right, bottom, transform, scalingMode);
}
// --------------------------Image methods---------------------------------------
@@ -853,7 +881,7 @@
}
static void Image_setNativeContext(JNIEnv* env, jobject thiz,
- sp<GraphicBuffer> buffer, int fenceFd) {
+ sp<GraphicBuffer> buffer, int fenceFd, long dataSpace) {
ALOGV("%s:", __FUNCTION__);
GraphicBuffer* p = NULL;
Image_getNativeContext(env, thiz, &p, /*fenceFd*/NULL);
@@ -867,6 +895,8 @@
reinterpret_cast<jlong>(buffer.get()));
env->SetIntField(thiz, gSurfaceImageClassInfo.mNativeFenceFd, reinterpret_cast<jint>(fenceFd));
+
+ env->SetLongField(thiz, gSurfaceImageClassInfo.mDataSpace, dataSpace);
}
static void Image_unlockIfLocked(JNIEnv* env, jobject thiz) {
@@ -1066,12 +1096,15 @@
{"nativeInit", "(Ljava/lang/Object;Landroid/view/Surface;IIII)J",
(void*)ImageWriter_init },
{"nativeClose", "(J)V", (void*)ImageWriter_close },
- {"nativeAttachAndQueueImage", "(JJIJIIIIII)I", (void*)ImageWriter_attachAndQueueImage },
+ {"nativeAttachAndQueueImage",
+ "(JJIJJIIIIII)I",
+ (void*)ImageWriter_attachAndQueueImage },
{"nativeAttachAndQueueGraphicBuffer",
- "(JLandroid/graphics/GraphicBuffer;IJIIIIII)I",
+ "(JLandroid/graphics/GraphicBuffer;IJJIIIIII)I",
(void*)ImageWriter_attachAndQueueGraphicBuffer },
{"nativeDequeueInputImage", "(JLandroid/media/Image;)V", (void*)ImageWriter_dequeueImage },
- {"nativeQueueInputImage", "(JLandroid/media/Image;JIIIIII)V", (void*)ImageWriter_queueImage },
+ {"nativeQueueInputImage", "(JLandroid/media/Image;JJIIIIII)V",
+ (void*)ImageWriter_queueImage },
{"cancelImage", "(JLandroid/media/Image;)V", (void*)ImageWriter_cancelImage },
};
diff --git a/media/tests/MtpTests/src/android/mtp/MtpDatabaseTest.java b/media/tests/MtpTests/src/android/mtp/MtpDatabaseTest.java
index 48be6fe..669ad62 100644
--- a/media/tests/MtpTests/src/android/mtp/MtpDatabaseTest.java
+++ b/media/tests/MtpTests/src/android/mtp/MtpDatabaseTest.java
@@ -132,7 +132,7 @@
StorageVolume mainStorage = new StorageVolume(MAIN_STORAGE_ID_STR,
mMainStorageDir, mMainStorageDir, "Primary Storage",
- true, false, true, false, -1, UserHandle.CURRENT, null /* uuid */, "", "");
+ true, false, true, false, false, -1, UserHandle.CURRENT, null /* uuid */, "", "");
final StorageVolume primary = mainStorage;
diff --git a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
index fdf6582..abdc7e5 100644
--- a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
+++ b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
@@ -132,9 +132,10 @@
secondaryStorageDir = createNewDir(TEMP_DIR_FILE);
StorageVolume mainStorage = new StorageVolume("1", mainStorageDir, mainStorageDir,
- "", true, false, true, false, -1, UserHandle.CURRENT, null /* uuid */, "", "");
+ "", true, false, true, false, false, -1, UserHandle.CURRENT, null /* uuid */, "",
+ "");
StorageVolume secondaryStorage = new StorageVolume("2", secondaryStorageDir,
- secondaryStorageDir, "", false, false, true, false, -1, UserHandle.CURRENT,
+ secondaryStorageDir, "", false, false, true, false, false, -1, UserHandle.CURRENT,
null /* uuid */, "", "");
objectsAdded = new ArrayList<>();
diff --git a/packages/Nsd/OWNERS b/packages/Nsd/OWNERS
new file mode 100644
index 0000000..4862377
--- /dev/null
+++ b/packages/Nsd/OWNERS
@@ -0,0 +1 @@
+file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
\ No newline at end of file
diff --git a/packages/Nsd/framework/Android.bp b/packages/Nsd/framework/Android.bp
new file mode 100644
index 0000000..2363a9f
--- /dev/null
+++ b/packages/Nsd/framework/Android.bp
@@ -0,0 +1,54 @@
+//
+// 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 {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+filegroup {
+ name: "framework-connectivity-nsd-internal-sources",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.aidl",
+ ],
+ path: "src",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+filegroup {
+ name: "framework-connectivity-nsd-aidl-export-sources",
+ srcs: [
+ "aidl-export/**/*.aidl",
+ ],
+ path: "aidl-export",
+ visibility: [
+ "//visibility:private",
+ ],
+}
+
+filegroup {
+ name: "framework-connectivity-nsd-sources",
+ srcs: [
+ ":framework-connectivity-nsd-internal-sources",
+ ":framework-connectivity-nsd-aidl-export-sources",
+ ],
+ visibility: [
+ "//frameworks/base",
+ ],
+}
diff --git a/core/java/android/net/nsd/NsdServiceInfo.aidl b/packages/Nsd/framework/aidl-export/android/net/nsd/NsdServiceInfo.aidl
similarity index 100%
rename from core/java/android/net/nsd/NsdServiceInfo.aidl
rename to packages/Nsd/framework/aidl-export/android/net/nsd/NsdServiceInfo.aidl
diff --git a/core/java/android/net/nsd/INsdManager.aidl b/packages/Nsd/framework/src/android/net/nsd/INsdManager.aidl
similarity index 100%
rename from core/java/android/net/nsd/INsdManager.aidl
rename to packages/Nsd/framework/src/android/net/nsd/INsdManager.aidl
diff --git a/core/java/android/net/nsd/INsdManagerCallback.aidl b/packages/Nsd/framework/src/android/net/nsd/INsdManagerCallback.aidl
similarity index 100%
rename from core/java/android/net/nsd/INsdManagerCallback.aidl
rename to packages/Nsd/framework/src/android/net/nsd/INsdManagerCallback.aidl
diff --git a/core/java/android/net/nsd/INsdServiceConnector.aidl b/packages/Nsd/framework/src/android/net/nsd/INsdServiceConnector.aidl
similarity index 100%
rename from core/java/android/net/nsd/INsdServiceConnector.aidl
rename to packages/Nsd/framework/src/android/net/nsd/INsdServiceConnector.aidl
diff --git a/core/java/android/net/nsd/NsdManager.java b/packages/Nsd/framework/src/android/net/nsd/NsdManager.java
similarity index 100%
rename from core/java/android/net/nsd/NsdManager.java
rename to packages/Nsd/framework/src/android/net/nsd/NsdManager.java
diff --git a/core/java/android/net/nsd/NsdServiceInfo.java b/packages/Nsd/framework/src/android/net/nsd/NsdServiceInfo.java
similarity index 100%
rename from core/java/android/net/nsd/NsdServiceInfo.java
rename to packages/Nsd/framework/src/android/net/nsd/NsdServiceInfo.java
diff --git a/packages/Nsd/service/Android.bp b/packages/Nsd/service/Android.bp
new file mode 100644
index 0000000..529f58d
--- /dev/null
+++ b/packages/Nsd/service/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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 {
+ // See: http://go/android-license-faq
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+filegroup {
+ name: "services.connectivity-nsd-sources",
+ srcs: [
+ "src/**/*.java",
+ ],
+ path: "src",
+ visibility: [
+ "//frameworks/base/services/core",
+ ],
+}
diff --git a/services/core/java/com/android/server/INativeDaemonConnectorCallbacks.java b/packages/Nsd/service/src/com/android/server/INativeDaemonConnectorCallbacks.java
similarity index 100%
rename from services/core/java/com/android/server/INativeDaemonConnectorCallbacks.java
rename to packages/Nsd/service/src/com/android/server/INativeDaemonConnectorCallbacks.java
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/packages/Nsd/service/src/com/android/server/NativeDaemonConnector.java
similarity index 100%
rename from services/core/java/com/android/server/NativeDaemonConnector.java
rename to packages/Nsd/service/src/com/android/server/NativeDaemonConnector.java
diff --git a/services/core/java/com/android/server/NativeDaemonConnectorException.java b/packages/Nsd/service/src/com/android/server/NativeDaemonConnectorException.java
similarity index 100%
rename from services/core/java/com/android/server/NativeDaemonConnectorException.java
rename to packages/Nsd/service/src/com/android/server/NativeDaemonConnectorException.java
diff --git a/services/core/java/com/android/server/NativeDaemonEvent.java b/packages/Nsd/service/src/com/android/server/NativeDaemonEvent.java
similarity index 100%
rename from services/core/java/com/android/server/NativeDaemonEvent.java
rename to packages/Nsd/service/src/com/android/server/NativeDaemonEvent.java
diff --git a/services/core/java/com/android/server/NativeDaemonTimeoutException.java b/packages/Nsd/service/src/com/android/server/NativeDaemonTimeoutException.java
similarity index 100%
rename from services/core/java/com/android/server/NativeDaemonTimeoutException.java
rename to packages/Nsd/service/src/com/android/server/NativeDaemonTimeoutException.java
diff --git a/services/core/java/com/android/server/NsdService.java b/packages/Nsd/service/src/com/android/server/NsdService.java
similarity index 100%
rename from services/core/java/com/android/server/NsdService.java
rename to packages/Nsd/service/src/com/android/server/NsdService.java
diff --git a/services/tests/servicestests/src/com/android/server/NativeDaemonConnectorTest.java b/packages/Nsd/tests/unit/java/com/android/server/NativeDaemonConnectorTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/NativeDaemonConnectorTest.java
rename to packages/Nsd/tests/unit/java/com/android/server/NativeDaemonConnectorTest.java
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index f637ff8..d73e45e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -19,7 +19,6 @@
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.location.LocationManager;
import android.media.AudioManager;
@@ -44,6 +43,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.UserIcons;
+import com.android.launcher3.icons.BaseIconFactory.IconOptions;
import com.android.launcher3.icons.IconFactory;
import com.android.settingslib.drawable.UserIconDrawable;
import com.android.settingslib.fuelgauge.BatteryStatus;
@@ -525,9 +525,9 @@
/** Get the corresponding adaptive icon drawable. */
public static Drawable getBadgedIcon(Context context, Drawable icon, UserHandle user) {
try (IconFactory iconFactory = IconFactory.obtain(context)) {
- final Bitmap iconBmp = iconFactory.createBadgedIconBitmap(icon, user,
- true /* shrinkNonAdaptiveIcons */).icon;
- return new BitmapDrawable(context.getResources(), iconBmp);
+ return iconFactory
+ .createBadgedIconBitmap(icon, new IconOptions().setUser(user))
+ .newIcon(context);
}
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 2b311ee..867ab3c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -596,6 +596,9 @@
<!-- Permission required for CTS test - SettingsMultiPaneDeepLinkTest -->
<uses-permission android:name="android.permission.LAUNCH_MULTI_PANE_SETTINGS_DEEP_LINK" />
+ <!-- Permission required for ATS test - CarDevicePolicyManagerTest -->
+ <uses-permission android:name="android.permission.LOCK_DEVICE" />
+
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/res/drawable/media_ttt_chip_background.xml b/packages/SystemUI/res/drawable/media_ttt_chip_background.xml
new file mode 100644
index 0000000..3abf4d7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_ttt_chip_background.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ 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.
+ -->
+
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <solid android:color="?androidprv:attr/colorSurface" />
+ <corners android:radius="32dp" />
+</shape>
diff --git a/packages/SystemUI/res/drawable/media_ttt_undo_background.xml b/packages/SystemUI/res/drawable/media_ttt_undo_background.xml
new file mode 100644
index 0000000..ec74ee1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_ttt_undo_background.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<shape
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+ <solid android:color="?androidprv:attr/colorAccentPrimary" />
+ <corners android:radius="24dp" />
+</shape>
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 4a5b637..e90a644 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -34,6 +34,7 @@
<TextView
android:id="@+id/internet_dialog_title"
+ android:ellipsize="end"
android:gravity="center_vertical|center_horizontal"
android:layout_width="wrap_content"
android:layout_height="32dp"
@@ -154,12 +155,18 @@
<TextView
android:id="@+id/mobile_summary"
style="@style/InternetDialog.NetworkSummary"/>
+ <TextView
+ android:id="@+id/airplane_mode_summary"
+ android:text="@string/airplane_mode"
+ android:visibility="gone"
+ style="@style/InternetDialog.NetworkSummary"/>
</LinearLayout>
<View
android:id="@+id/mobile_toggle_divider"
android:layout_width="1dp"
android:layout_height="28dp"
+ android:layout_marginStart="7dp"
android:layout_marginEnd="16dp"
android:layout_gravity="center_vertical"
android:background="?android:attr/textColorSecondary"/>
@@ -370,27 +377,60 @@
android:clickable="true"/>
</LinearLayout>
</LinearLayout>
-
<FrameLayout
- android:id="@+id/done_layout"
- android:layout_width="67dp"
+ android:id="@+id/button_layout"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
android:layout_height="48dp"
- android:layout_marginTop="8dp"
+ android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
+ android:layout_marginTop="8dp"
android:layout_marginBottom="34dp"
- android:layout_gravity="end|center_vertical"
- android:clickable="true"
- android:focusable="true">
- <Button
- android:text="@string/inline_done_button"
- style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
- android:layout_width="match_parent"
- android:layout_height="36dp"
- android:layout_gravity="center"
- android:textAppearance="@style/TextAppearance.InternetDialog"
- android:textSize="14sp"
- android:background="@drawable/internet_dialog_footer_background"
- android:clickable="false"/>
+ android:clickable="false"
+ android:focusable="false">
+
+ <FrameLayout
+ android:id="@+id/apm_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:layout_gravity="start|center_vertical"
+ android:orientation="vertical">
+ <Button
+ android:text="@string/turn_off_airplane_mode"
+ android:ellipsize="end"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="wrap_content"
+ android:layout_height="36dp"
+ android:layout_gravity="start|center_vertical"
+ android:textAppearance="@style/TextAppearance.InternetDialog"
+ android:textSize="14sp"
+ android:background="@drawable/internet_dialog_footer_background"
+ android:clickable="false"/>
+ </FrameLayout>
+
+ <FrameLayout
+ android:id="@+id/done_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginStart="16dp"
+ android:clickable="true"
+ android:focusable="true"
+ android:layout_gravity="end|center_vertical"
+ android:orientation="vertical">
+ <Button
+ android:text="@string/inline_done_button"
+ android:ellipsize="end"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ android:layout_width="67dp"
+ android:layout_height="36dp"
+ android:layout_gravity="end|center_vertical"
+ android:textAppearance="@style/TextAppearance.InternetDialog"
+ android:textSize="14sp"
+ android:background="@drawable/internet_dialog_footer_background"
+ android:clickable="false"/>
+ </FrameLayout>
</FrameLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
diff --git a/packages/SystemUI/res/layout/media_ttt_chip.xml b/packages/SystemUI/res/layout/media_ttt_chip.xml
new file mode 100644
index 0000000..6fbc41c
--- /dev/null
+++ b/packages/SystemUI/res/layout/media_ttt_chip.xml
@@ -0,0 +1,64 @@
+<!--
+ ~ 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.
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="@dimen/media_ttt_chip_outer_padding"
+ android:background="@drawable/media_ttt_chip_background"
+ android:layout_marginTop="50dp"
+ android:clipToPadding="false"
+ android:gravity="center_vertical"
+ >
+
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/media_ttt_text_size"
+ android:textColor="?android:attr/textColorPrimary"
+ />
+
+ <ProgressBar
+ android:id="@+id/loading"
+ android:indeterminate="true"
+ android:layout_width="@dimen/media_ttt_icon_size"
+ android:layout_height="@dimen/media_ttt_icon_size"
+ android:layout_marginStart="12dp"
+ android:indeterminateTint="?androidprv:attr/colorAccentPrimaryVariant"
+ style="?android:attr/progressBarStyleSmall"
+ />
+
+ <TextView
+ android:id="@+id/undo"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/media_transfer_undo"
+ android:textColor="?androidprv:attr/textColorOnAccent"
+ android:layout_marginStart="12dp"
+ android:textSize="@dimen/media_ttt_text_size"
+ android:paddingStart="@dimen/media_ttt_chip_outer_padding"
+ android:paddingEnd="@dimen/media_ttt_chip_outer_padding"
+ android:paddingTop="@dimen/media_ttt_undo_button_vertical_padding"
+ android:paddingBottom="@dimen/media_ttt_undo_button_vertical_padding"
+ android:layout_marginTop="@dimen/media_ttt_undo_button_vertical_negative_margin"
+ android:layout_marginBottom="@dimen/media_ttt_undo_button_vertical_negative_margin"
+ android:background="@drawable/media_ttt_undo_background"
+ />
+
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index 91b11fc..3a0df28 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -37,8 +37,8 @@
android:layout_height="@dimen/qs_framed_avatar_size"
android:layout_marginBottom="7dp"
systemui:frameWidth="6dp"
- systemui:badgeDiameter="18dp"
- systemui:badgeMargin="1dp"
+ systemui:badgeDiameter="15dp"
+ systemui:badgeMargin="5dp"
systemui:framePadding="-1dp"
systemui:frameColor="@color/qs_user_avatar_frame"/>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index b3bbd87..ff748a9 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -98,6 +98,7 @@
as custom(package/class). Relative class name is supported. -->
<string-array name="config_quickSettingsAutoAdd" translatable="false">
<item>accessibility_display_inversion_enabled:inversion</item>
+ <item>one_handed_mode_enabled:onehanded</item>
</string-array>
<!-- Show indicator for Wifi on but not connected. -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a437ae6..0b56264 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -976,6 +976,13 @@
<dimen name="qs_aa_media_rec_album_margin_vert">4dp</dimen>
<dimen name="qq_aa_media_rec_header_text_size">16sp</dimen>
+ <!-- Media tap-to-transfer chip -->
+ <dimen name="media_ttt_chip_outer_padding">16dp</dimen>
+ <dimen name="media_ttt_text_size">16sp</dimen>
+ <dimen name="media_ttt_icon_size">16dp</dimen>
+ <dimen name="media_ttt_undo_button_vertical_padding">8dp</dimen>
+ <dimen name="media_ttt_undo_button_vertical_negative_margin">-8dp</dimen>
+
<!-- Window magnification -->
<dimen name="magnification_border_drag_size">35dp</dimen>
<dimen name="magnification_outer_border_margin">15dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 300cb2d3..4790412 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2173,6 +2173,14 @@
<!-- Description for Smartspace recommendation's media item which doesn't have artist info, including information for the media's title and the source app [CHAR LIMIT=NONE]-->
<string name="controls_media_smartspace_rec_item_no_artist_description">Play <xliff:g id="song_name" example="Daily mix">%1$s</xliff:g> from <xliff:g id="app_label" example="Spotify">%2$s</xliff:g></string>
+ <!--- ****** Media tap-to-transfer ****** -->
+ <!-- Text for a button to undo the media transfer. [CHAR LIMIT=20] -->
+ <string name="media_transfer_undo">Undo</string>
+ <!-- Text to ask the user to move their device closer to a different device (deviceName) in order to play music on the different device. [CHAR LIMIT=75] -->
+ <string name="media_move_closer_to_transfer">Move closer to play on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
+ <!-- Text informing the user that their media is now playing on a different device (deviceName). [CHAR LIMIT=50] -->
+ <string name="media_transfer_playing">Playing on <xliff:g id="deviceName" example="My Tablet">%1$s</xliff:g></string>
+
<!-- Error message indicating that a control timed out while waiting for an update [CHAR_LIMIT=30] -->
<string name="controls_error_timeout">Inactive, check app</string>
<!-- Error message indicating that the control is no longer available in the application [CHAR LIMIT=30] -->
@@ -2347,6 +2355,8 @@
<string name="to_switch_networks_disconnect_ethernet">To switch networks, disconnect ethernet</string>
<!-- Message to describe "Wi-Fi scan always available feature" when Wi-Fi is off and Wi-Fi scanning is on. [CHAR LIMIT=NONE] -->
<string name="wifi_scan_notify_message">To improve device experience, apps and services can still scan for Wi\u2011Fi networks at any time, even when Wi\u2011Fi is off. You can change this in Wi\u2011Fi scanning settings. <annotation id="link">Change</annotation></string>
+ <!-- Provider Model: Description of the airplane mode button. [CHAR LIMIT=60] -->
+ <string name="turn_off_airplane_mode">Turn off airplane mode</string>
<!-- Text for TileService request dialog. This is shown to the user that an app is requesting
user approval to add the shown tile to Quick Settings [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.java b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.java
deleted file mode 100644
index 543232d..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.unfold.util;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Manages progress listeners that can have smaller lifespan than the unfold animation.
- * Allows to limit getting transition updates to only when
- * {@link ScopedUnfoldTransitionProgressProvider#setReadyToHandleTransition} is called
- * with readyToHandleTransition = true
- *
- * If the transition has already started by the moment when the clients are ready to play
- * the transition then it will report transition started callback and current animation progress.
- */
-public final class ScopedUnfoldTransitionProgressProvider implements
- UnfoldTransitionProgressProvider, TransitionProgressListener {
-
- private static final float PROGRESS_UNSET = -1f;
-
- @Nullable
- private UnfoldTransitionProgressProvider mSource;
-
- private final List<TransitionProgressListener> mListeners = new ArrayList<>();
-
- private boolean mIsReadyToHandleTransition;
- private boolean mIsTransitionRunning;
- private float mLastTransitionProgress = PROGRESS_UNSET;
-
- public ScopedUnfoldTransitionProgressProvider() {
- this(null);
- }
-
- public ScopedUnfoldTransitionProgressProvider(
- @Nullable UnfoldTransitionProgressProvider source) {
- setSourceProvider(source);
- }
-
- /**
- * Sets the source for the unfold transition progress updates,
- * it replaces current provider if it is already set
- * @param provider transition provider that emits transition progress updates
- */
- public void setSourceProvider(@Nullable UnfoldTransitionProgressProvider provider) {
- if (mSource != null) {
- mSource.removeCallback(this);
- }
-
- if (provider != null) {
- mSource = provider;
- mSource.addCallback(this);
- } else {
- mSource = null;
- }
- }
-
- /**
- * Allows to notify this provide whether the listeners can play the transition or not.
- * Call this method with readyToHandleTransition = true when all listeners
- * are ready to consume the transition progress events.
- * Call it with readyToHandleTransition = false when listeners can't process the events.
- */
- public void setReadyToHandleTransition(boolean isReadyToHandleTransition) {
- if (mIsTransitionRunning) {
- if (isReadyToHandleTransition) {
- mListeners.forEach(TransitionProgressListener::onTransitionStarted);
-
- if (mLastTransitionProgress != PROGRESS_UNSET) {
- mListeners.forEach(listener ->
- listener.onTransitionProgress(mLastTransitionProgress));
- }
- } else {
- mIsTransitionRunning = false;
- mListeners.forEach(TransitionProgressListener::onTransitionFinished);
- }
- }
-
- mIsReadyToHandleTransition = isReadyToHandleTransition;
- }
-
- @Override
- public void addCallback(@NonNull TransitionProgressListener listener) {
- mListeners.add(listener);
- }
-
- @Override
- public void removeCallback(@NonNull TransitionProgressListener listener) {
- mListeners.remove(listener);
- }
-
- @Override
- public void destroy() {
- mSource.removeCallback(this);
- }
-
- @Override
- public void onTransitionStarted() {
- this.mIsTransitionRunning = true;
- if (mIsReadyToHandleTransition) {
- mListeners.forEach(TransitionProgressListener::onTransitionStarted);
- }
- }
-
- @Override
- public void onTransitionProgress(float progress) {
- if (mIsReadyToHandleTransition) {
- mListeners.forEach(listener -> listener.onTransitionProgress(progress));
- }
-
- mLastTransitionProgress = progress;
- }
-
- @Override
- public void onTransitionFinished() {
- if (mIsReadyToHandleTransition) {
- mListeners.forEach(TransitionProgressListener::onTransitionFinished);
- }
-
- mIsTransitionRunning = false;
- mLastTransitionProgress = PROGRESS_UNSET;
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
new file mode 100644
index 0000000..22698a8
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt
@@ -0,0 +1,119 @@
+/*
+ * 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.util
+
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider
+import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
+
+/**
+ * Manages progress listeners that can have smaller lifespan than the unfold animation.
+ * Allows to limit getting transition updates to only when
+ * [ScopedUnfoldTransitionProgressProvider.setReadyToHandleTransition] is called
+ * with readyToHandleTransition = true
+ *
+ * If the transition has already started by the moment when the clients are ready to play
+ * the transition then it will report transition started callback and current animation progress.
+ */
+class ScopedUnfoldTransitionProgressProvider @JvmOverloads constructor(
+ source: UnfoldTransitionProgressProvider? = null
+) : UnfoldTransitionProgressProvider, TransitionProgressListener {
+
+ private var source: UnfoldTransitionProgressProvider? = null
+
+ private val listeners: MutableList<TransitionProgressListener> = mutableListOf()
+
+ private var isReadyToHandleTransition = false
+ private var isTransitionRunning = false
+ private var lastTransitionProgress = PROGRESS_UNSET
+
+ init {
+ setSourceProvider(source)
+ }
+ /**
+ * Sets the source for the unfold transition progress updates,
+ * it replaces current provider if it is already set
+ * @param provider transition provider that emits transition progress updates
+ */
+ fun setSourceProvider(provider: UnfoldTransitionProgressProvider?) {
+ source?.removeCallback(this)
+
+ if (provider != null) {
+ source = provider
+ provider.addCallback(this)
+ } else {
+ source = null
+ }
+ }
+
+ /**
+ * Allows to notify this provide whether the listeners can play the transition or not.
+ * Call this method with readyToHandleTransition = true when all listeners
+ * are ready to consume the transition progress events.
+ * Call it with readyToHandleTransition = false when listeners can't process the events.
+ */
+ fun setReadyToHandleTransition(isReadyToHandleTransition: Boolean) {
+ if (isTransitionRunning) {
+ if (isReadyToHandleTransition) {
+ listeners.forEach { it.onTransitionStarted() }
+ if (lastTransitionProgress != PROGRESS_UNSET) {
+ listeners.forEach { it.onTransitionProgress(lastTransitionProgress) }
+ }
+ } else {
+ isTransitionRunning = false
+ listeners.forEach { it.onTransitionFinished() }
+ }
+ }
+ this.isReadyToHandleTransition = isReadyToHandleTransition
+ }
+
+ override fun addCallback(listener: TransitionProgressListener) {
+ listeners += listener
+ }
+
+ override fun removeCallback(listener: TransitionProgressListener) {
+ listeners -= listener
+ }
+
+ override fun destroy() {
+ source?.removeCallback(this)
+ }
+
+ override fun onTransitionStarted() {
+ isTransitionRunning = true
+ if (isReadyToHandleTransition) {
+ listeners.forEach { it.onTransitionStarted() }
+ }
+ }
+
+ override fun onTransitionProgress(progress: Float) {
+ if (isReadyToHandleTransition) {
+ listeners.forEach { it.onTransitionProgress(progress) }
+ }
+ lastTransitionProgress = progress
+ }
+
+ override fun onTransitionFinished() {
+ if (isReadyToHandleTransition) {
+ listeners.forEach { it.onTransitionFinished() }
+ }
+ isTransitionRunning = false
+ lastTransitionProgress = PROGRESS_UNSET
+ }
+
+ companion object {
+ private const val PROGRESS_UNSET = -1f
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 1d17fd8..c4a58db 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -52,6 +52,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
+import com.android.systemui.statusbar.QsFrameTranslateModule;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -112,6 +113,7 @@
LogModule.class,
PeopleHubModule.class,
PluginModule.class,
+ QsFrameTranslateModule.class,
ScreenshotModule.class,
SensorModule.class,
SettingsModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 237d077..cf679f0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -83,6 +83,6 @@
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
- return Optional.of(new MediaTttChipController(context, commandRegistry, windowManager));
+ return Optional.of(new MediaTttChipController(commandRegistry, context, windowManager));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
index 85e5b33..af8fc0e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
@@ -19,9 +19,14 @@
import android.content.Context
import android.graphics.PixelFormat
import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
import android.view.WindowManager
+import android.widget.LinearLayout
import android.widget.TextView
+import androidx.annotation.StringRes
import androidx.annotation.VisibleForTesting
+import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
@@ -36,8 +41,8 @@
*/
@SysUISingleton
class MediaTttChipController @Inject constructor(
- context: Context,
commandRegistry: CommandRegistry,
+ private val context: Context,
private val windowManager: WindowManager,
) {
init {
@@ -46,9 +51,9 @@
}
private val windowLayoutParams = WindowManager.LayoutParams().apply {
- width = WindowManager.LayoutParams.MATCH_PARENT
- height = WindowManager.LayoutParams.MATCH_PARENT
- gravity = Gravity.CENTER_HORIZONTAL
+ width = WindowManager.LayoutParams.WRAP_CONTENT
+ height = WindowManager.LayoutParams.WRAP_CONTENT
+ gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL)
type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY
title = "Media Tap-To-Transfer Chip View"
flags = (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
@@ -57,29 +62,71 @@
setTrustedOverlay()
}
- // TODO(b/203800327): Create a layout that matches UX.
- private val chipView: TextView = TextView(context).apply {
- text = "Media Tap-To-Transfer Chip"
- }
+ /** The chip view currently being displayed. Null if the chip is not being displayed. */
+ private var chipView: LinearLayout? = null
- private var chipDisplaying: Boolean = false
+ private fun displayChip(chipType: ChipType, otherDeviceName: String) {
+ val oldChipView = chipView
+ if (chipView == null) {
+ chipView = LayoutInflater
+ .from(context)
+ .inflate(R.layout.media_ttt_chip, null) as LinearLayout
+ }
+ val currentChipView = chipView!!
- private fun addChip() {
- if (chipDisplaying) { return }
- windowManager.addView(chipView, windowLayoutParams)
- chipDisplaying = true
+ // Text
+ currentChipView.requireViewById<TextView>(R.id.text).apply {
+ text = context.getString(chipType.chipText, otherDeviceName)
+ }
+
+ // Loading
+ val showLoading = chipType == ChipType.TRANSFER_INITIATED
+ currentChipView.requireViewById<View>(R.id.loading).visibility =
+ if (showLoading) { View.VISIBLE } else { View.GONE }
+
+ // Undo
+ val showUndo = chipType == ChipType.TRANSFER_SUCCEEDED
+ currentChipView.requireViewById<View>(R.id.undo).visibility =
+ if (showUndo) { View.VISIBLE } else { View.GONE }
+
+ if (oldChipView == null) {
+ windowManager.addView(chipView, windowLayoutParams)
+ }
}
private fun removeChip() {
- if (!chipDisplaying) { return }
+ if (chipView == null) { return }
windowManager.removeView(chipView)
- chipDisplaying = false
+ chipView = null
+ }
+
+ @VisibleForTesting
+ enum class ChipType(
+ @StringRes internal val chipText: Int
+ ) {
+ MOVE_CLOSER_TO_TRANSFER(R.string.media_move_closer_to_transfer),
+ TRANSFER_INITIATED(R.string.media_transfer_playing),
+ TRANSFER_SUCCEEDED(R.string.media_transfer_playing),
}
inner class AddChipCommand : Command {
- override fun execute(pw: PrintWriter, args: List<String>) = addChip()
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ val chipTypeArg = args[1]
+ ChipType.values().forEach {
+ if (it.name == chipTypeArg) {
+ displayChip(it, otherDeviceName = args[0])
+ return
+ }
+ }
+
+ pw.println("Chip type must be one of " +
+ ChipType.values().map { it.name }.reduce { acc, s -> "$acc, $s" })
+ }
+
override fun help(pw: PrintWriter) {
- pw.println("Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_TAG")
+ pw.println(
+ "Usage: adb shell cmd statusbar $ADD_CHIP_COMMAND_TAG <deviceName> <chipType>"
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 5afefa1..ab48a28 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui.navigationbar.gestural;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
+
import static com.android.systemui.classifier.Classifier.BACK_GESTURE;
import android.app.ActivityManager;
@@ -544,7 +546,8 @@
layoutParams.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
layoutParams.windowAnimations = 0;
layoutParams.privateFlags |=
- WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ (WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
+ | PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION);
layoutParams.setTitle(TAG + mContext.getDisplayId());
layoutParams.setFitInsetsTypes(0 /* types */);
layoutParams.setTrustedOverlay();
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
index a16b92f..097cf35 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java
@@ -33,6 +33,7 @@
import static android.util.TypedValue.COMPLEX_UNIT_DIP;
import static android.util.TypedValue.COMPLEX_UNIT_PX;
+import static com.android.launcher3.icons.FastBitmapDrawable.getDisabledColorFilter;
import static com.android.systemui.people.PeopleSpaceUtils.STARRED_CONTACT;
import static com.android.systemui.people.PeopleSpaceUtils.VALID_CONTACT;
import static com.android.systemui.people.PeopleSpaceUtils.convertDrawableToBitmap;
@@ -45,8 +46,6 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
import android.graphics.ImageDecoder;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -75,7 +74,6 @@
import androidx.core.math.MathUtils;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.systemui.R;
import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
@@ -339,8 +337,9 @@
views = new RemoteViews(mContext.getPackageName(),
R.layout.people_tile_suppressed_layout);
}
- Drawable appIcon = mContext.getDrawable(R.drawable.ic_conversation_icon);
- Bitmap disabledBitmap = convertDrawableToDisabledBitmap(appIcon);
+ Drawable appIcon = mContext.getDrawable(R.drawable.ic_conversation_icon).mutate();
+ appIcon.setColorFilter(getDisabledColorFilter());
+ Bitmap disabledBitmap = convertDrawableToBitmap(appIcon);
views.setImageViewBitmap(R.id.icon, disabledBitmap);
return views;
}
@@ -1262,8 +1261,9 @@
Context context, PeopleSpaceTile tile, int maxAvatarSize, boolean hasNewStory) {
Icon icon = tile.getUserIcon();
if (icon == null) {
- Drawable placeholder = context.getDrawable(R.drawable.ic_avatar_with_badge);
- return convertDrawableToDisabledBitmap(placeholder);
+ Drawable placeholder = context.getDrawable(R.drawable.ic_avatar_with_badge).mutate();
+ placeholder.setColorFilter(getDisabledColorFilter());
+ return convertDrawableToBitmap(placeholder);
}
PeopleStoryIconFactory storyIcon = new PeopleStoryIconFactory(context,
context.getPackageManager(),
@@ -1276,10 +1276,7 @@
hasNewStory);
if (isDndBlockingTileData(tile)) {
- // If DND is blocking the conversation, then display the icon in grayscale.
- ColorMatrix colorMatrix = new ColorMatrix();
- colorMatrix.setSaturation(0);
- personDrawable.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
+ personDrawable.setColorFilter(getDisabledColorFilter());
}
return convertDrawableToBitmap(personDrawable);
@@ -1375,11 +1372,4 @@
mAvatarSize = avatarSize;
}
}
-
- private static Bitmap convertDrawableToDisabledBitmap(Drawable icon) {
- Bitmap appIconAsBitmap = convertDrawableToBitmap(icon);
- FastBitmapDrawable drawable = new FastBitmapDrawable(appIconAsBitmap);
- drawable.setIsDisabled(true);
- return convertDrawableToBitmap(drawable);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 033fe1e..26c89ff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -120,10 +120,12 @@
private ImageView mSignalIcon;
private TextView mMobileTitleText;
private TextView mMobileSummaryText;
+ private TextView mAirplaneModeSummaryText;
private Switch mMobileDataToggle;
private View mMobileToggleDivider;
private Switch mWiFiToggle;
private FrameLayout mDoneLayout;
+ private FrameLayout mAirplaneModeLayout;
private Drawable mBackgroundOn;
private Drawable mBackgroundOff = null;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -210,9 +212,11 @@
mWifiRecyclerView = mDialogView.requireViewById(R.id.wifi_list_layout);
mSeeAllLayout = mDialogView.requireViewById(R.id.see_all_layout);
mDoneLayout = mDialogView.requireViewById(R.id.done_layout);
+ mAirplaneModeLayout = mDialogView.requireViewById(R.id.apm_layout);
mSignalIcon = mDialogView.requireViewById(R.id.signal_icon);
mMobileTitleText = mDialogView.requireViewById(R.id.mobile_title);
mMobileSummaryText = mDialogView.requireViewById(R.id.mobile_summary);
+ mAirplaneModeSummaryText = mDialogView.requireViewById(R.id.airplane_mode_summary);
mMobileToggleDivider = mDialogView.requireViewById(R.id.mobile_toggle_divider);
mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_toggle);
mWiFiToggle = mDialogView.requireViewById(R.id.wifi_toggle);
@@ -230,6 +234,8 @@
setOnClickListener();
mTurnWifiOnLayout.setBackground(null);
+ mAirplaneModeLayout.setVisibility(
+ mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
mWifiRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
mWifiRecyclerView.setAdapter(mAdapter);
}
@@ -269,6 +275,7 @@
mSeeAllLayout.setOnClickListener(null);
mWiFiToggle.setOnCheckedChangeListener(null);
mDoneLayout.setOnClickListener(null);
+ mAirplaneModeLayout.setOnClickListener(null);
mInternetDialogController.onStop();
mInternetDialogFactory.destroyDialog();
}
@@ -294,13 +301,17 @@
}
if (mInternetDialogController.isAirplaneModeEnabled()) {
mInternetDialogSubTitle.setVisibility(View.GONE);
+ mAirplaneModeLayout.setVisibility(View.VISIBLE);
} else {
+ mInternetDialogTitle.setText(getDialogTitleText());
+ mInternetDialogSubTitle.setVisibility(View.VISIBLE);
mInternetDialogSubTitle.setText(getSubtitleText());
+ mAirplaneModeLayout.setVisibility(View.GONE);
}
updateEthernet();
if (shouldUpdateMobileNetwork) {
- setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular()
- || mInternetDialogController.isCarrierNetworkActive());
+ setMobileDataLayout(mInternetDialogController.activeNetworkIsCellular(),
+ mInternetDialogController.isCarrierNetworkActive());
}
if (!mCanConfigWifi) {
@@ -343,6 +354,9 @@
mWifiManager.setWifiEnabled(isChecked);
});
mDoneLayout.setOnClickListener(v -> dismiss());
+ mAirplaneModeLayout.setOnClickListener(v -> {
+ mInternetDialogController.setAirplaneModeDisabled();
+ });
}
@MainThread
@@ -351,42 +365,60 @@
mInternetDialogController.hasEthernet() ? View.VISIBLE : View.GONE);
}
- private void setMobileDataLayout(boolean isCarrierNetworkConnected) {
- if (mInternetDialogController.isAirplaneModeEnabled()
- || !mInternetDialogController.hasCarrier()) {
+ private void setMobileDataLayout(boolean activeNetworkIsCellular,
+ boolean isCarrierNetworkActive) {
+ boolean isNetworkConnected = activeNetworkIsCellular || isCarrierNetworkActive;
+ // 1. Mobile network should be gone if airplane mode ON or the list of active
+ // subscriptionId is null.
+ // 2. Carrier network should be gone if airplane mode ON and Wi-Fi is OFF.
+ if (DEBUG) {
+ Log.d(TAG, "setMobileDataLayout, isCarrierNetworkActive = " + isCarrierNetworkActive);
+ }
+
+ if (!mInternetDialogController.hasActiveSubId()
+ && (!mWifiManager.isWifiEnabled() || !isCarrierNetworkActive)) {
mMobileNetworkLayout.setVisibility(View.GONE);
} else {
- mMobileDataToggle.setChecked(mInternetDialogController.isMobileDataEnabled());
mMobileNetworkLayout.setVisibility(View.VISIBLE);
+ mMobileDataToggle.setChecked(mInternetDialogController.isMobileDataEnabled());
mMobileTitleText.setText(getMobileNetworkTitle());
- if (!TextUtils.isEmpty(getMobileNetworkSummary())) {
+ String summary = getMobileNetworkSummary();
+ if (!TextUtils.isEmpty(summary)) {
mMobileSummaryText.setText(
- Html.fromHtml(getMobileNetworkSummary(), Html.FROM_HTML_MODE_LEGACY));
+ Html.fromHtml(summary, Html.FROM_HTML_MODE_LEGACY));
mMobileSummaryText.setVisibility(View.VISIBLE);
} else {
mMobileSummaryText.setVisibility(View.GONE);
}
-
mBackgroundExecutor.execute(() -> {
Drawable drawable = getSignalStrengthDrawable();
mHandler.post(() -> {
mSignalIcon.setImageDrawable(drawable);
});
});
- mMobileTitleText.setTextAppearance(isCarrierNetworkConnected
+ mMobileTitleText.setTextAppearance(isNetworkConnected
? R.style.TextAppearance_InternetDialog_Active
: R.style.TextAppearance_InternetDialog);
- mMobileSummaryText.setTextAppearance(isCarrierNetworkConnected
+ int secondaryRes = isNetworkConnected
? R.style.TextAppearance_InternetDialog_Secondary_Active
- : R.style.TextAppearance_InternetDialog_Secondary);
+ : R.style.TextAppearance_InternetDialog_Secondary;
+ mMobileSummaryText.setTextAppearance(secondaryRes);
+ // Set airplane mode to the summary for carrier network
+ if (mInternetDialogController.isAirplaneModeEnabled()) {
+ mAirplaneModeSummaryText.setVisibility(View.VISIBLE);
+ mAirplaneModeSummaryText.setText(mContext.getText(R.string.airplane_mode));
+ mAirplaneModeSummaryText.setTextAppearance(secondaryRes);
+ } else {
+ mAirplaneModeSummaryText.setVisibility(View.GONE);
+ }
mMobileNetworkLayout.setBackground(
- isCarrierNetworkConnected ? mBackgroundOn : mBackgroundOff);
+ isNetworkConnected ? mBackgroundOn : mBackgroundOff);
TypedArray array = mContext.obtainStyledAttributes(
R.style.InternetDialog_Divider_Active, new int[]{android.R.attr.background});
int dividerColor = Utils.getColorAttrDefaultColor(mContext,
android.R.attr.textColorSecondary);
- mMobileToggleDivider.setBackgroundColor(isCarrierNetworkConnected
+ mMobileToggleDivider.setBackgroundColor(isNetworkConnected
? array.getColor(0, dividerColor) : dividerColor);
array.recycle();
@@ -620,10 +652,13 @@
@WorkerThread
public void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
@Nullable WifiEntry connectedEntry) {
+ // Should update the carrier network layout when it is connected under airplane mode ON.
+ boolean shouldUpdateCarrierNetwork = mMobileNetworkLayout.getVisibility() == View.VISIBLE
+ && mInternetDialogController.isAirplaneModeEnabled();
mHandler.post(() -> {
mConnectedWifiEntry = connectedEntry;
mWifiEntriesCount = wifiEntries == null ? 0 : wifiEntries.size();
- updateDialog(false /* shouldUpdateMobileNetwork */);
+ updateDialog(shouldUpdateCarrierNetwork /* shouldUpdateMobileNetwork */);
mAdapter.setWifiEntries(wifiEntries, mWifiEntriesCount);
mAdapter.notifyDataSetChanged();
});
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 2a7d2c3..706f423 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
@@ -281,6 +281,10 @@
return mGlobalSettings.getInt(Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
}
+ void setAirplaneModeDisabled() {
+ mConnectivityManager.setAirplaneMode(false);
+ }
+
@VisibleForTesting
protected int getDefaultDataSubscriptionId() {
return mSubscriptionManager.getDefaultDataSubscriptionId();
@@ -352,7 +356,7 @@
if (DEBUG) {
Log.d(TAG, "No Wi-Fi item.");
}
- if (!hasCarrier() || (!isVoiceStateInService() && !isDataStateInService())) {
+ if (!hasActiveSubId() || (!isVoiceStateInService() && !isDataStateInService())) {
if (DEBUG) {
Log.d(TAG, "No carrier or service is out of service.");
}
@@ -403,15 +407,16 @@
return drawable;
}
- if (isDataStateInService() || isVoiceStateInService()) {
+ boolean isCarrierNetworkActive = isCarrierNetworkActive();
+ if (isDataStateInService() || isVoiceStateInService() || isCarrierNetworkActive) {
AtomicReference<Drawable> shared = new AtomicReference<>();
- shared.set(getSignalStrengthDrawableWithLevel());
+ shared.set(getSignalStrengthDrawableWithLevel(isCarrierNetworkActive));
drawable = shared.get();
}
int tintColor = Utils.getColorAttrDefaultColor(mContext,
android.R.attr.textColorTertiary);
- if (activeNetworkIsCellular() || isCarrierNetworkActive()) {
+ if (activeNetworkIsCellular() || isCarrierNetworkActive) {
tintColor = mContext.getColor(R.color.connected_network_primary_color);
}
drawable.setTint(tintColor);
@@ -426,12 +431,15 @@
*
* @return The Drawable which is a signal bar icon with level.
*/
- Drawable getSignalStrengthDrawableWithLevel() {
+ Drawable getSignalStrengthDrawableWithLevel(boolean isCarrierNetworkActive) {
final SignalStrength strength = mTelephonyManager.getSignalStrength();
int level = (strength == null) ? 0 : strength.getLevel();
int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
- if (mSubscriptionManager != null && shouldInflateSignalStrength(mDefaultDataSubId)) {
- level += 1;
+ if ((mSubscriptionManager != null && shouldInflateSignalStrength(mDefaultDataSubId))
+ || isCarrierNetworkActive) {
+ level = isCarrierNetworkActive
+ ? SignalStrength.NUM_SIGNAL_STRENGTH_BINS
+ : (level + 1);
numLevels += 1;
}
return getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON,
@@ -586,15 +594,18 @@
if (!isMobileDataEnabled()) {
return context.getString(R.string.mobile_data_off_summary);
}
- if (!isDataStateInService()) {
- return context.getString(R.string.mobile_data_no_connection);
- }
+
String summary = networkTypeDescription;
+ // Set network description for the carrier network when connecting to the carrier network
+ // under the airplane mode ON.
if (activeNetworkIsCellular() || isCarrierNetworkActive()) {
summary = context.getString(R.string.preference_summary_default_combination,
context.getString(R.string.mobile_data_connection_active),
networkTypeDescription);
+ } else if (!isDataStateInService()) {
+ summary = context.getString(R.string.mobile_data_no_connection);
}
+
return summary;
}
@@ -678,7 +689,7 @@
/**
* @return whether there is the carrier item in the slice.
*/
- boolean hasCarrier() {
+ boolean hasActiveSubId() {
if (mSubscriptionManager == null) {
if (DEBUG) {
Log.d(TAG, "SubscriptionManager is null, can not check carrier.");
@@ -888,7 +899,7 @@
if (mHasEthernet) {
count -= 1;
}
- if (hasCarrier()) {
+ if (hasActiveSubId()) {
count -= 1;
}
if (hasConnectedWifi) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateController.java
new file mode 100644
index 0000000..2e1762a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateController.java
@@ -0,0 +1,48 @@
+/*
+ * 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.statusbar;
+
+import android.view.View;
+
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+/**
+ * Calculates and moves the QS frame vertically.
+ */
+public abstract class QsFrameTranslateController {
+
+ protected StatusBar mStatusBar;
+
+ public QsFrameTranslateController(StatusBar statusBar) {
+ mStatusBar = statusBar;
+ }
+
+ /**
+ * Calculate and translate the QS Frame on the Y-axis.
+ */
+ public abstract void translateQsFrame(View qsFrame, QS qs, float overExpansion,
+ float qsTranslationForFullShadeTransition);
+
+ /**
+ * Calculate the top padding for notifications panel. This could be the supplied
+ * @param expansionHeight or recalculate it for a different value.
+ */
+ public abstract float getNotificationsTopPadding(float expansionHeight,
+ NotificationStackScrollLayoutController controller);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateImpl.java
new file mode 100644
index 0000000..c156797
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateImpl.java
@@ -0,0 +1,50 @@
+/*
+ * 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.statusbar;
+
+import android.view.View;
+
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.plugins.qs.QS;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import javax.inject.Inject;
+
+/**
+ * Default implementation of QS Translation. This by default does not do much.
+ */
+@SysUISingleton
+public class QsFrameTranslateImpl extends QsFrameTranslateController {
+
+ @Inject
+ public QsFrameTranslateImpl(StatusBar statusBar) {
+ super(statusBar);
+ }
+
+ @Override
+ public void translateQsFrame(View qsFrame, QS qs, float overExpansion,
+ float qsTranslationForFullShadeTransition) {
+ }
+
+ @Override
+ public float getNotificationsTopPadding(float expansionHeight,
+ NotificationStackScrollLayoutController controller) {
+
+ return expansionHeight;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateModule.java
new file mode 100644
index 0000000..075adfc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateModule.java
@@ -0,0 +1,30 @@
+/*
+ * 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.statusbar;
+
+import com.android.systemui.dagger.SysUISingleton;
+
+import dagger.Binds;
+import dagger.Module;
+
+@Module
+public interface QsFrameTranslateModule {
+
+ @Binds
+ @SysUISingleton
+ QsFrameTranslateController bindQsFrameTranslateController(QsFrameTranslateImpl impl);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
index 8b0252b..9e5dab1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
@@ -31,11 +31,6 @@
var parent: GroupEntry?,
/**
- * Identifies the notification order in the entire notification list
- */
- var stableIndex: Int = -1,
-
- /**
* The section that this ListEntry was sorted into. If the child of the group, this will be the
* parent's section. Null if not attached to the list.
*/
@@ -53,6 +48,11 @@
var promoter: NotifPromoter?,
/**
+ * If an entry's group was pruned from the list by NotifPipeline logic, the reason is here.
+ */
+ var groupPruneReason: String?,
+
+ /**
* If the [VisualStabilityManager] is suppressing group or section changes for this entry,
* suppressedChanges will contain the new parent or section that we would have assigned to
* the entry had it not been suppressed by the VisualStabilityManager.
@@ -60,14 +60,23 @@
var suppressedChanges: SuppressedAttachState
) {
+ /**
+ * Identifies the notification order in the entire notification list.
+ * NOTE: this property is intentionally excluded from equals calculation (by not making it a
+ * constructor arg) because its value changes based on the presence of other members in the
+ * list, rather than anything having to do with this entry's attachment.
+ */
+ var stableIndex: Int = -1
+
/** Copies the state of another instance. */
fun clone(other: ListAttachState) {
parent = other.parent
- stableIndex = other.stableIndex
section = other.section
excludingFilter = other.excludingFilter
promoter = other.promoter
+ groupPruneReason = other.groupPruneReason
suppressedChanges.clone(other.suppressedChanges)
+ stableIndex = other.stableIndex
}
/** Resets back to a "clean" state (the same as created by the factory method) */
@@ -76,20 +85,22 @@
section = null
excludingFilter = null
promoter = null
- stableIndex = -1
+ groupPruneReason = null
suppressedChanges.reset()
+ stableIndex = -1
}
companion object {
@JvmStatic
fun create(): ListAttachState {
return ListAttachState(
- null,
- -1,
- null,
- null,
- null,
- SuppressedAttachState.create())
+ null,
+ null,
+ null,
+ null,
+ null,
+ SuppressedAttachState.create()
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index 37eacad..915057f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -31,8 +31,6 @@
private final String mKey;
private final long mCreationTime;
- int mFirstAddedIteration = -1;
-
private final ListAttachState mPreviousAttachState = ListAttachState.create();
private final ListAttachState mAttachState = ListAttachState.create();
@@ -95,14 +93,6 @@
}
/**
- * True if this entry has been attached to the shade at least once in its lifetime (it may not
- * currently be attached).
- */
- public boolean hasBeenAttachedBefore() {
- return mFirstAddedIteration != -1;
- }
-
- /**
* Stores the current attach state into {@link #getPreviousAttachState()}} and then starts a
* fresh attach state (all entries will be null/default-initialized).
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 748c69d..120c722 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.collection;
+import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkState;
import static com.android.systemui.statusbar.notification.collection.GroupEntry.ROOT_ENTRY;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_BUILD_STARTED;
import static com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.STATE_FINALIZE_FILTERING;
@@ -34,6 +36,7 @@
import android.annotation.Nullable;
import android.os.Trace;
import android.util.ArrayMap;
+import android.util.ArraySet;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -50,6 +53,7 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeTransformGroupsListener;
import com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState;
import com.android.systemui.statusbar.notification.collection.listbuilder.ShadeListBuilderLogger;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.DefaultNotifStabilityManager;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
@@ -72,6 +76,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import javax.inject.Inject;
@@ -103,7 +108,7 @@
private final List<NotifFilter> mNotifFinalizeFilters = new ArrayList<>();
private final List<NotifComparator> mNotifComparators = new ArrayList<>();
private final List<NotifSection> mNotifSections = new ArrayList<>();
- @Nullable private NotifStabilityManager mNotifStabilityManager;
+ private NotifStabilityManager mNotifStabilityManager;
private final List<OnBeforeTransformGroupsListener> mOnBeforeTransformGroupsListeners =
new ArrayList<>();
@@ -228,7 +233,7 @@
mNotifSections.add(new NotifSection(DEFAULT_SECTIONER, mNotifSections.size()));
}
- void setNotifStabilityManager(NotifStabilityManager notifStabilityManager) {
+ void setNotifStabilityManager(@NonNull NotifStabilityManager notifStabilityManager) {
Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE);
@@ -244,6 +249,14 @@
mNotifStabilityManager.setInvalidationListener(this::onReorderingAllowedInvalidated);
}
+ @NonNull
+ private NotifStabilityManager getStabilityManager() {
+ if (mNotifStabilityManager == null) {
+ return DefaultNotifStabilityManager.INSTANCE;
+ }
+ return mNotifStabilityManager;
+ }
+
void setComparators(List<NotifComparator> comparators) {
Assert.isMainThread();
mPipelineState.requireState(STATE_IDLE);
@@ -460,10 +473,6 @@
for (NotificationEntry entry : mAllEntries) {
entry.beginNewAttachState();
-
- if (entry.mFirstAddedIteration == -1) {
- entry.mFirstAddedIteration = mIterationCount;
- }
}
mNotifList.clear();
@@ -519,7 +528,6 @@
GroupEntry group = mGroups.get(topLevelKey);
if (group == null) {
group = new GroupEntry(topLevelKey, mSystemClock.uptimeMillis());
- group.mFirstAddedIteration = mIterationCount;
mGroups.put(topLevelKey, group);
}
if (group.getParent() == null) {
@@ -569,7 +577,7 @@
}
private void stabilizeGroupingNotifs(List<ListEntry> topLevelList) {
- if (mNotifStabilityManager == null) {
+ if (getStabilityManager().isEveryChangeAllowed()) {
return;
}
Trace.beginSection("ShadeListBuilder.stabilizeGroupingNotifs");
@@ -612,7 +620,7 @@
final GroupEntry prevParent = entry.getPreviousAttachState().getParent();
final GroupEntry assignedParent = entry.getParent();
if (prevParent != assignedParent
- && !mNotifStabilityManager.isGroupChangeAllowed(entry.getRepresentativeEntry())) {
+ && !getStabilityManager().isGroupChangeAllowed(entry.getRepresentativeEntry())) {
entry.getAttachState().getSuppressedChanges().setParent(assignedParent);
entry.setParent(prevParent);
if (prevParent == ROOT_ENTRY) {
@@ -655,56 +663,60 @@
private void pruneIncompleteGroups(List<ListEntry> shadeList) {
Trace.beginSection("ShadeListBuilder.pruneIncompleteGroups");
+ // Any group which lost a child on this run to stability is exempt from being pruned or
+ // having its summary promoted, regardless of how many children it has
+ Set<String> groupsWithChildrenLostToStability =
+ getGroupsWithChildrenLostToStability(shadeList);
for (int i = 0; i < shadeList.size(); i++) {
final ListEntry tle = shadeList.get(i);
if (tle instanceof GroupEntry) {
final GroupEntry group = (GroupEntry) tle;
final List<NotificationEntry> children = group.getRawChildren();
+ final boolean hasSummary = group.getSummary() != null;
- if (group.getSummary() != null && children.size() == 0) {
- NotificationEntry summary = group.getSummary();
- summary.setParent(ROOT_ENTRY);
- // The list may be sorted; replace the group with the summary, in its place
- shadeList.set(i, summary);
-
- group.setSummary(null);
- annulAddition(group, shadeList);
+ if (hasSummary && children.size() == 0) {
+ if (groupsWithChildrenLostToStability.contains(group.getKey())) {
+ // This group lost a child on this run to stability, so it is exempt from
+ // having its summary promoted to the top level, so prune it.
+ // It has no children, so it will just vanish.
+ pruneGroupAtIndexAndPromoteAnyChildren(shadeList, group, i);
+ } else {
+ // For any other summary with no children, promote the summary.
+ pruneGroupAtIndexAndPromoteSummary(shadeList, group, i);
+ }
i--; // The node we visited is gone, so be sure to visit this index again.
- } else if (group.getSummary() == null
- || children.size() < MIN_CHILDREN_FOR_GROUP) {
+ } else if (!hasSummary) {
+ // If the group doesn't provide a summary, ignore it and add
+ // any children it may have directly to top-level.
+ pruneGroupAtIndexAndPromoteAnyChildren(shadeList, group, i);
- if (group.getSummary() != null
- && group.wasAttachedInPreviousPass()
- && mNotifStabilityManager != null
- && !mNotifStabilityManager.isGroupChangeAllowed(group.getSummary())) {
- // if this group was previously attached and group changes aren't
- // allowed, keep it around until group changes are allowed again
+ i--; // The node we visited is gone, so be sure to visit this index again.
+ } else if (children.size() < MIN_CHILDREN_FOR_GROUP) {
+ // This group has a summary and insufficient, but nonzero children.
+ checkState(hasSummary, "group must have summary at this point");
+ checkState(!children.isEmpty(), "empty group should have been promoted");
+
+ if (groupsWithChildrenLostToStability.contains(group.getKey())) {
+ // This group lost a child on this run to stability, so it is exempt from
+ // the "min children" requirement; keep it around in case more children are
+ // added before changes are allowed again.
+ group.getAttachState().getSuppressedChanges().setWasPruneSuppressed(true);
+ continue;
+ }
+ if (group.wasAttachedInPreviousPass()
+ && !getStabilityManager().isGroupChangeAllowed(group.getSummary())) {
+ checkState(!children.isEmpty(), "empty group should have been pruned");
+ // This group was previously attached and group changes aren't
+ // allowed; keep it around until group changes are allowed again.
group.getAttachState().getSuppressedChanges().setWasPruneSuppressed(true);
continue;
}
- // If the group doesn't provide a summary or is too small, ignore it and add
+ // The group is too small, ignore it and add
// its children (if any) directly to top-level.
-
- shadeList.remove(i);
-
- if (group.getSummary() != null) {
- final NotificationEntry summary = group.getSummary();
- group.setSummary(null);
- annulAddition(summary, shadeList);
- }
-
- for (int j = 0; j < children.size(); j++) {
- final NotificationEntry child = children.get(j);
- child.setParent(ROOT_ENTRY);
- // The list may be sorted, so add the children in order where the group was.
- shadeList.add(i + j, child);
- }
- children.clear();
-
- annulAddition(group, shadeList);
+ pruneGroupAtIndexAndPromoteAnyChildren(shadeList, group, i);
i--; // The node we visited is gone, so be sure to visit this index again.
}
@@ -713,6 +725,99 @@
Trace.endSection();
}
+ private void pruneGroupAtIndexAndPromoteSummary(List<ListEntry> shadeList,
+ GroupEntry group, int index) {
+ // Validate that the group has no children
+ checkArgument(group.getChildren().isEmpty(), "group should have no children");
+
+ NotificationEntry summary = group.getSummary();
+ summary.setParent(ROOT_ENTRY);
+ // The list may be sorted; replace the group with the summary, in its place
+ ListEntry oldEntry = shadeList.set(index, summary);
+
+ // Validate that the replaced entry was the group entry
+ checkState(oldEntry == group);
+
+ group.setSummary(null);
+ annulAddition(group, shadeList);
+ summary.getAttachState().setGroupPruneReason(
+ "SUMMARY with no children @ " + mPipelineState.getStateName());
+ }
+
+ private void pruneGroupAtIndexAndPromoteAnyChildren(List<ListEntry> shadeList,
+ GroupEntry group, int index) {
+ // REMOVE the GroupEntry at this index
+ ListEntry oldEntry = shadeList.remove(index);
+
+ // Validate that the replaced entry was the group entry
+ checkState(oldEntry == group);
+
+ List<NotificationEntry> children = group.getRawChildren();
+ boolean hasSummary = group.getSummary() != null;
+
+ // Remove the group summary, if present, and leave detached.
+ if (hasSummary) {
+ final NotificationEntry summary = group.getSummary();
+ group.setSummary(null);
+ annulAddition(summary, shadeList);
+ summary.getAttachState().setGroupPruneReason(
+ "SUMMARY with too few children @ " + mPipelineState.getStateName());
+ }
+
+ // Promote any children
+ if (!children.isEmpty()) {
+ // create the reason we will report on the child for why its group was pruned.
+ String childReason = hasSummary
+ ? ("CHILD with " + (children.size() - 1) + " siblings @ "
+ + mPipelineState.getStateName())
+ : ("CHILD with no summary @ " + mPipelineState.getStateName());
+
+ // Remove children from the group and add them to the shadeList.
+ for (int j = 0; j < children.size(); j++) {
+ final NotificationEntry child = children.get(j);
+ child.setParent(ROOT_ENTRY);
+ child.getAttachState().setGroupPruneReason(requireNonNull(childReason));
+ }
+ // The list may be sorted, so add the children in order where the group was.
+ shadeList.addAll(index, children);
+ children.clear();
+ }
+
+ annulAddition(group, shadeList);
+ }
+
+ /**
+ * Collect the keys of any groups which have already lost a child to stability this run.
+ *
+ * If stability is being enforced, then {@link #stabilizeGroupingNotifs(List)} might have
+ * detached some children from their groups and left them at the top level because the child was
+ * previously attached at the top level. Doing so would set the
+ * {@link SuppressedAttachState#getParent() suppressed parent} for the current attach state.
+ *
+ * If we've already removed a child from this group, we don't want to remove any more children
+ * from the group (even if that would leave only a single notification in the group) because
+ * that could cascade over multiple runs and allow a large group of notifications all show up as
+ * top level (ungrouped) notifications.
+ */
+ @NonNull
+ private Set<String> getGroupsWithChildrenLostToStability(List<ListEntry> shadeList) {
+ if (getStabilityManager().isEveryChangeAllowed()) {
+ return Collections.emptySet();
+ }
+ ArraySet<String> groupsWithChildrenLostToStability = new ArraySet<>();
+ for (int i = 0; i < shadeList.size(); i++) {
+ final ListEntry tle = shadeList.get(i);
+ final GroupEntry suppressedParent =
+ tle.getAttachState().getSuppressedChanges().getParent();
+ if (suppressedParent != null) {
+ // This top-level-entry was supposed to be attached to this group,
+ // so mark the group as having lost a child to stability.
+ groupsWithChildrenLostToStability.add(suppressedParent.getKey());
+ }
+ }
+ return groupsWithChildrenLostToStability;
+ }
+
/**
* If a ListEntry was added to the shade list and then later removed (e.g. because it was a
* group that was broken up), this method will erase any bookkeeping traces of that addition
@@ -727,10 +832,9 @@
// lot of them), it will put the system into an inconsistent state. So we check all of them
// here.
- if (entry.getParent() == null || entry.mFirstAddedIteration == -1) {
+ if (entry.getParent() == null) {
throw new IllegalStateException(
- "Cannot nullify addition of " + entry.getKey() + ": no such addition. ("
- + entry.getParent() + " " + entry.mFirstAddedIteration + ")");
+ "Cannot nullify addition of " + entry.getKey() + ": no parent.");
}
if (entry.getParent() == ROOT_ENTRY) {
@@ -771,9 +875,6 @@
entry.setParent(null);
entry.getAttachState().setSection(null);
entry.getAttachState().setPromoter(null);
- if (entry.mFirstAddedIteration == mIterationCount) {
- entry.mFirstAddedIteration = -1;
- }
}
private void assignSections() {
@@ -804,12 +905,12 @@
assignIndexes(mNotifList);
// Check for suppressed order changes
- if (!mNotifStabilityManager.isEveryChangeAllowed()) {
+ if (!getStabilityManager().isEveryChangeAllowed()) {
mForceReorderable = true;
boolean isSorted = isSorted(mNotifList, mTopLevelComparator);
mForceReorderable = false;
if (!isSorted) {
- mNotifStabilityManager.onEntryReorderSuppressed();
+ getStabilityManager().onEntryReorderSuppressed();
}
}
Trace.endSection();
@@ -897,12 +998,26 @@
curr.getParent());
}
+ if (curr.getSuppressedChanges().getSection() != null) {
+ mLogger.logSectionChangeSuppressed(
+ mIterationCount,
+ curr.getSuppressedChanges().getSection(),
+ curr.getSection());
+ }
+
if (curr.getSuppressedChanges().getWasPruneSuppressed()) {
mLogger.logGroupPruningSuppressed(
mIterationCount,
curr.getParent());
}
+ if (!Objects.equals(curr.getGroupPruneReason(), prev.getGroupPruneReason())) {
+ mLogger.logPrunedReasonChanged(
+ mIterationCount,
+ prev.getGroupPruneReason(),
+ curr.getGroupPruneReason());
+ }
+
if (curr.getExcludingFilter() != prev.getExcludingFilter()) {
mLogger.logFilterChanged(
mIterationCount,
@@ -927,20 +1042,11 @@
prev.getSection(),
curr.getSection());
}
-
- if (curr.getSuppressedChanges().getSection() != null) {
- mLogger.logSectionChangeSuppressed(
- mIterationCount,
- curr.getSuppressedChanges().getSection(),
- curr.getSection());
- }
}
}
private void onBeginRun() {
- if (mNotifStabilityManager != null) {
- mNotifStabilityManager.onBeginRun();
- }
+ getStabilityManager().onBeginRun();
}
private void cleanupPluggables() {
@@ -953,9 +1059,7 @@
mNotifSections.get(i).getSectioner().onCleanup();
}
- if (mNotifStabilityManager != null) {
- callOnCleanup(List.of(mNotifStabilityManager));
- }
+ callOnCleanup(List.of(getStabilityManager()));
}
private void callOnCleanup(List<? extends Pluggable<?>> pluggables) {
@@ -1015,7 +1119,7 @@
private boolean mForceReorderable = false;
private boolean canReorder(ListEntry entry) {
- return mForceReorderable || mNotifStabilityManager.isEntryReorderingAllowed(entry);
+ return mForceReorderable || getStabilityManager().isEntryReorderingAllowed(entry);
}
private boolean applyFilters(NotificationEntry entry, long now, List<NotifFilter> filters) {
@@ -1064,12 +1168,10 @@
NotifSection finalSection = newSection;
// have we seen this entry before and are we changing its section?
- if (mNotifStabilityManager != null
- && entry.wasAttachedInPreviousPass()
- && newSection != prevAttachState.getSection()) {
+ if (entry.wasAttachedInPreviousPass() && newSection != prevAttachState.getSection()) {
// are section changes allowed?
- if (!mNotifStabilityManager.isSectionChangeAllowed(entry.getRepresentativeEntry())) {
+ if (!getStabilityManager().isSectionChangeAllowed(entry.getRepresentativeEntry())) {
// record the section that we wanted to change to
entry.getAttachState().getSuppressedChanges().setSection(newSection);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index bbb97d1..ec4e039 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -385,7 +385,7 @@
}
private boolean shouldWaitForGroupToInflate(GroupEntry group, long now) {
- if (group == GroupEntry.ROOT_ENTRY || group.hasBeenAttachedBefore()) {
+ if (group == GroupEntry.ROOT_ENTRY || group.wasAttachedInPreviousPass()) {
return false;
}
if (isBeyondGroupInitializationWindow(group, now)) {
@@ -397,11 +397,12 @@
return true;
}
for (NotificationEntry child : group.getChildren()) {
- if (mInflatingNotifs.contains(child) && !child.hasBeenAttachedBefore()) {
+ if (mInflatingNotifs.contains(child) && !child.wasAttachedInPreviousPass()) {
mLogger.logDelayingGroupRelease(group.getKey(), child.getKey());
return true;
}
}
+ mLogger.logDoneWaitingForGroupInflation(group.getKey());
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
index dd4794f..f835250 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
@@ -41,11 +41,19 @@
})
}
+ fun logDoneWaitingForGroupInflation(groupKey: String) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ str1 = groupKey
+ }, {
+ "Finished inflating all members of group $str1, releasing group"
+ })
+ }
+
fun logGroupInflationTookTooLong(groupKey: String) {
buffer.log(TAG, LogLevel.WARNING, {
str1 = groupKey
}, {
- "Group inflation took too long far $str1, releasing children early"
+ "Group inflation took too long for $str1, releasing children early"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
index 75489b1..327876c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java
@@ -118,7 +118,7 @@
}
@Override
- public boolean isGroupChangeAllowed(NotificationEntry entry) {
+ public boolean isGroupChangeAllowed(@NonNull NotificationEntry entry) {
final boolean isGroupChangeAllowedForEntry =
mReorderingAllowed || mHeadsUpManager.isAlerting(entry.getKey());
mIsSuppressingGroupChange |= !isGroupChangeAllowedForEntry;
@@ -126,7 +126,7 @@
}
@Override
- public boolean isSectionChangeAllowed(NotificationEntry entry) {
+ public boolean isSectionChangeAllowed(@NonNull NotificationEntry entry) {
final boolean isSectionChangeAllowedForEntry =
mReorderingAllowed
|| mHeadsUpManager.isAlerting(entry.getKey())
@@ -138,7 +138,7 @@
}
@Override
- public boolean isEntryReorderingAllowed(ListEntry section) {
+ public boolean isEntryReorderingAllowed(@NonNull ListEntry section) {
return mReorderingAllowed;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
index 798bfe7..feb48da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/PipelineState.java
@@ -18,6 +18,8 @@
import android.annotation.IntDef;
+import androidx.annotation.NonNull;
+
import com.android.systemui.statusbar.notification.collection.ShadeListBuilder;
import java.lang.annotation.Retention;
@@ -35,6 +37,7 @@
return state == mState;
}
+ /** Get the current state's integer representation */
public @StateName int getState() {
return mState;
}
@@ -75,6 +78,41 @@
}
}
+ /** Get the current state's string representation */
+ @NonNull
+ public String getStateName() {
+ return getStateName(mState);
+ }
+
+ /** Get the given state's string representation */
+ @NonNull
+ public static String getStateName(@StateName int state) {
+ switch (state) {
+ case STATE_IDLE:
+ return "STATE_IDLE";
+ case STATE_BUILD_STARTED:
+ return "STATE_BUILD_STARTED";
+ case STATE_RESETTING:
+ return "STATE_RESETTING";
+ case STATE_PRE_GROUP_FILTERING:
+ return "STATE_PRE_GROUP_FILTERING";
+ case STATE_GROUPING:
+ return "STATE_GROUPING";
+ case STATE_TRANSFORMING:
+ return "STATE_TRANSFORMING";
+ case STATE_GROUP_STABILIZING:
+ return "STATE_GROUP_STABILIZING";
+ case STATE_SORTING:
+ return "STATE_SORTING";
+ case STATE_FINALIZE_FILTERING:
+ return "STATE_FINALIZE_FILTERING";
+ case STATE_FINALIZING:
+ return "STATE_FINALIZING";
+ default:
+ return "STATE:" + state;
+ }
+ }
+
public static final int STATE_IDLE = 0;
public static final int STATE_BUILD_STARTED = 1;
public static final int STATE_RESETTING = 2;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index 8fff905..ba3e855 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -37,9 +37,9 @@
})
}
- fun logEndBuildList(iterationCount: Int, topLevelEntries: Int, numChildren: Int) {
+ fun logEndBuildList(buildId: Int, topLevelEntries: Int, numChildren: Int) {
buffer.log(TAG, INFO, {
- long1 = iterationCount.toLong()
+ long1 = buildId.toLong()
int1 = topLevelEntries
int2 = numChildren
}, {
@@ -112,21 +112,21 @@
fun logDuplicateSummary(buildId: Int, groupKey: String, existingKey: String, newKey: String) {
buffer.log(TAG, WARNING, {
- int1 = buildId
+ long1 = buildId.toLong()
str1 = groupKey
str2 = existingKey
str3 = newKey
}, {
- """(Build $int1) Duplicate summary for group "$str1": "$str2" vs. "$str3""""
+ """(Build $long1) Duplicate summary for group "$str1": "$str2" vs. "$str3""""
})
}
fun logDuplicateTopLevelKey(buildId: Int, topLevelKey: String) {
buffer.log(TAG, WARNING, {
- int1 = buildId
+ long1 = buildId.toLong()
str1 = topLevelKey
}, {
- "(Build $int1) Duplicate top-level key: $str1"
+ "(Build $long1) Duplicate top-level key: $str1"
})
}
@@ -137,7 +137,7 @@
newParent: GroupEntry?
) {
buffer.log(TAG, INFO, {
- int1 = buildId
+ long1 = buildId.toLong()
str1 = key
str2 = prevParent?.key
str3 = newParent?.key
@@ -153,22 +153,22 @@
"MODIFIED (ATTACHED)"
}
- "(Build $int1) $action {$str1}"
+ "(Build $long1) $action {$str1}"
})
}
fun logParentChanged(buildId: Int, prevParent: GroupEntry?, newParent: GroupEntry?) {
buffer.log(TAG, INFO, {
- int1 = buildId
+ long1 = buildId.toLong()
str1 = prevParent?.key
str2 = newParent?.key
}, {
if (str1 == null && str2 != null) {
- "(Build $int1) Parent is {$str2}"
+ "(Build $long1) Parent is {$str2}"
} else if (str1 != null && str2 == null) {
- "(Build $int1) Parent was {$str1}"
+ "(Build $long1) Parent was {$str1}"
} else {
- "(Build $int1) Reparent: {$str1} -> {$str2}"
+ "(Build $long1) Reparent: {$str1} -> {$str2}"
}
})
}
@@ -179,7 +179,7 @@
keepingParent: GroupEntry?
) {
buffer.log(TAG, INFO, {
- int1 = buildId
+ long1 = buildId.toLong()
str1 = suppressedParent?.key
str2 = keepingParent?.key
}, {
@@ -192,24 +192,38 @@
keepingParent: GroupEntry?
) {
buffer.log(TAG, INFO, {
- int1 = buildId
+ long1 = buildId.toLong()
str1 = keepingParent?.key
}, {
"(Build $long1) Group pruning suppressed; keeping parent '$str1'"
})
}
+ fun logPrunedReasonChanged(
+ buildId: Int,
+ prevReason: String?,
+ newReason: String?
+ ) {
+ buffer.log(TAG, INFO, {
+ long1 = buildId.toLong()
+ str1 = prevReason
+ str2 = newReason
+ }, {
+ "(Build $long1) Pruned reason changed: $str1 -> $str2"
+ })
+ }
+
fun logFilterChanged(
buildId: Int,
prevFilter: NotifFilter?,
newFilter: NotifFilter?
) {
buffer.log(TAG, INFO, {
- int1 = buildId
+ long1 = buildId.toLong()
str1 = prevFilter?.name
str2 = newFilter?.name
}, {
- "(Build $int1) Filter changed: $str1 -> $str2"
+ "(Build $long1) Filter changed: $str1 -> $str2"
})
}
@@ -219,11 +233,11 @@
newPromoter: NotifPromoter?
) {
buffer.log(TAG, INFO, {
- int1 = buildId
+ long1 = buildId.toLong()
str1 = prevPromoter?.name
str2 = newPromoter?.name
}, {
- "(Build $int1) Promoter changed: $str1 -> $str2"
+ "(Build $long1) Promoter changed: $str1 -> $str2"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java
deleted file mode 100644
index cb2d3cb..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.java
+++ /dev/null
@@ -1,78 +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.systemui.statusbar.notification.collection.listbuilder.pluggable;
-
-import com.android.systemui.statusbar.notification.collection.ListEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-
-/**
- * Pluggable for participating in notif stabilization. In particular, suppressing group and
- * section changes.
- *
- * The stability manager should be invalidated when previously suppressed a group or
- * section change is now allowed.
- */
-public abstract class NotifStabilityManager extends Pluggable<NotifStabilityManager> {
-
- protected NotifStabilityManager(String name) {
- super(name);
- }
-
- /**
- * Called at the beginning of every pipeline run to perform any necessary cleanup from the
- * previous run.
- */
- public abstract void onBeginRun();
-
- /**
- * Returns whether this notification can currently change groups/parents.
- * Per iteration of the notification pipeline, locally stores this information until the next
- * run of the pipeline. When this method returns true, it's expected that a group change for
- * this entry is being suppressed.
- */
- public abstract boolean isGroupChangeAllowed(NotificationEntry entry);
-
- /**
- * Returns whether this notification entry can currently change sections.
- * Per iteration of the notification pipeline, locally stores this information until the next
- * run of the pipeline. When this method returns true, it's expected that a section change is
- * being suppressed.
- */
- public abstract boolean isSectionChangeAllowed(NotificationEntry entry);
-
- /**
- * Is a notification entry is allowed be reordered
- * @param entry
- * @return if can re-order
- */
- public abstract boolean isEntryReorderingAllowed(ListEntry entry);
-
- /**
- * Called by the pipeline to determine if every call to the other stability methods would
- * return true, regardless of parameters. This allows the pipeline to skip any pieces of
- * work related to stability.
- *
- * @return true if all other methods will return true for any parameters.
- */
- public abstract boolean isEveryChangeAllowed();
-
- /**
- * Called by the pipeline to inform the stability manager that an entry reordering was indeed
- * suppressed as the result of a previous call to {@link #isEntryReorderingAllowed(ListEntry)}.
- */
- public abstract void onEntryReorderSuppressed();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
new file mode 100644
index 0000000..60f557c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
@@ -0,0 +1,85 @@
+/*
+ * 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.systemui.statusbar.notification.collection.listbuilder.pluggable
+
+import com.android.systemui.statusbar.notification.collection.ListEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+
+/**
+ * Pluggable for participating in notif stabilization. In particular, suppressing group and
+ * section changes.
+ *
+ * The stability manager should be invalidated when previously suppressed a group or
+ * section change is now allowed.
+ */
+abstract class NotifStabilityManager protected constructor(name: String) :
+ Pluggable<NotifStabilityManager>(name) {
+ /**
+ * Called at the beginning of every pipeline run to perform any necessary cleanup from the
+ * previous run.
+ */
+ abstract fun onBeginRun()
+
+ /**
+ * Returns whether this notification can currently change groups/parents.
+ * Per iteration of the notification pipeline, locally stores this information until the next
+ * run of the pipeline. When this method returns false, it's expected that a group change for
+ * this entry is being suppressed.
+ */
+ abstract fun isGroupChangeAllowed(entry: NotificationEntry): Boolean
+
+ /**
+ * Returns whether this notification entry can currently change sections.
+ * Per iteration of the notification pipeline, locally stores this information until the next
+ * run of the pipeline. When this method returns false, it's expected that a section change is
+ * being suppressed.
+ */
+ abstract fun isSectionChangeAllowed(entry: NotificationEntry): Boolean
+
+ /**
+ * Returns whether this list entry is allowed to be reordered within its section.
+ * Unlike [isGroupChangeAllowed] or [isSectionChangeAllowed], this method is called on every
+ * entry, so an implementation may not assume that returning false means an order change is
+ * being suppressed. However, if an order change is suppressed, that will be reported to ths
+ * implementation by calling [onEntryReorderSuppressed] after ordering is complete.
+ */
+ abstract fun isEntryReorderingAllowed(entry: ListEntry): Boolean
+
+ /**
+ * Called by the pipeline to determine if every call to the other stability methods would
+ * return true, regardless of parameters. This allows the pipeline to skip any pieces of
+ * work related to stability.
+ *
+ * @return true if all other methods will return true for any parameters.
+ */
+ abstract fun isEveryChangeAllowed(): Boolean
+
+ /**
+ * Called by the pipeline to inform the stability manager that an entry reordering was indeed
+ * suppressed as the result of a previous call to [.isEntryReorderingAllowed].
+ */
+ abstract fun onEntryReorderSuppressed()
+}
+
+/** The default, no-op instance of the stability manager which always allows all changes */
+object DefaultNotifStabilityManager : NotifStabilityManager("DefaultNotifStabilityManager") {
+ override fun onBeginRun() {}
+ override fun isGroupChangeAllowed(entry: NotificationEntry): Boolean = true
+ override fun isSectionChangeAllowed(entry: NotificationEntry): Boolean = true
+ override fun isEntryReorderingAllowed(entry: ListEntry): Boolean = true
+ override fun isEveryChangeAllowed(): Boolean = true
+ override fun onEntryReorderSuppressed() {}
+}
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 db68ecf1..275074d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -157,6 +157,7 @@
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.QsFrameTranslateController;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
@@ -339,6 +340,7 @@
private KeyguardStatusBarViewController mKeyguardStatusBarViewController;
@VisibleForTesting QS mQs;
private FrameLayout mQsFrame;
+ private QsFrameTranslateController mQsFrameTranslateController;
@Nullable
private CommunalHostViewController mCommunalViewController;
private KeyguardStatusViewController mKeyguardStatusViewController;
@@ -775,7 +777,8 @@
NotificationRemoteInputManager remoteInputManager,
Optional<SysUIUnfoldComponent> unfoldComponent,
ControlsComponent controlsComponent,
- InteractionJankMonitor interactionJankMonitor) {
+ InteractionJankMonitor interactionJankMonitor,
+ QsFrameTranslateController qsFrameTranslateController) {
super(view,
falsingManager,
dozeLog,
@@ -900,7 +903,6 @@
mMaxKeyguardNotifications = resources.getInteger(R.integer.keyguard_max_notification_count);
mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition());
-
mCommunalSourceCallback = () -> {
mUiExecutor.execute(() -> setCommunalSource(null /*source*/));
};
@@ -908,7 +910,7 @@
mCommunalSourceMonitorCallback = (source) -> {
mUiExecutor.execute(() -> setCommunalSource(source));
};
-
+ mQsFrameTranslateController = qsFrameTranslateController;
updateUserSwitcherFlags();
onFinishInflate();
}
@@ -2667,7 +2669,8 @@
(float) (mQsMaxExpansionHeight),
computeQsExpansionFraction());
} else {
- return mQsExpansionHeight;
+ return mQsFrameTranslateController.getNotificationsTopPadding(mQsExpansionHeight,
+ mNotificationStackScrollLayoutController);
}
}
@@ -3251,8 +3254,8 @@
}
private void updateQsFrameTranslation() {
- float translation = mOverExpansion / 2.0f + mQsTranslationForFullShadeTransition;
- mQsFrame.setTranslationY(translation);
+ mQsFrameTranslateController.translateQsFrame(mQsFrame, mQs, mOverExpansion,
+ mQsTranslationForFullShadeTransition);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 256b069..ec7e93b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -43,7 +43,6 @@
val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side)
val systemIconArea: ViewGroup = mView.findViewById(R.id.system_icon_area)
- val viewCenterProvider = StatusBarViewsCenterProvider()
val viewsToAnimate = arrayOf(
statusBarLeftSide,
systemIconArea
@@ -52,7 +51,7 @@
mView.viewTreeObserver.addOnPreDrawListener(object :
ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
- animationController.onViewsReady(viewsToAnimate, viewCenterProvider)
+ animationController.onViewsReady(viewsToAnimate)
mView.viewTreeObserver.removeOnPreDrawListener(this)
return true
}
@@ -82,7 +81,7 @@
mView.importantForAccessibility = mode
}
- private class StatusBarViewsCenterProvider : UnfoldMoveFromCenterAnimator.ViewCenterProvider {
+ class StatusBarViewsCenterProvider : UnfoldMoveFromCenterAnimator.ViewCenterProvider {
override fun getViewCenter(view: View, outPoint: Point) =
when (view.id) {
R.id.status_bar_left_side -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
index 805ddf5..6d033477 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
@@ -18,7 +18,7 @@
import android.view.View
import android.view.WindowManager
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
-import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator.ViewCenterProvider
+import com.android.systemui.statusbar.phone.PhoneStatusBarViewController.StatusBarViewsCenterProvider
import com.android.systemui.unfold.SysUIUnfoldScope
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
@@ -27,20 +27,18 @@
@SysUIUnfoldScope
class StatusBarMoveFromCenterAnimationController @Inject constructor(
private val progressProvider: ScopedUnfoldTransitionProgressProvider,
- private val windowManager: WindowManager
+ windowManager: WindowManager
) {
private val transitionListener = TransitionListener()
- private var moveFromCenterAnimator: UnfoldMoveFromCenterAnimator? = null
+ private val moveFromCenterAnimator = UnfoldMoveFromCenterAnimator(windowManager,
+ viewCenterProvider = StatusBarViewsCenterProvider())
- fun onViewsReady(viewsToAnimate: Array<View>, viewCenterProvider: ViewCenterProvider) {
- moveFromCenterAnimator = UnfoldMoveFromCenterAnimator(windowManager,
- viewCenterProvider = viewCenterProvider)
-
- moveFromCenterAnimator?.updateDisplayProperties()
+ fun onViewsReady(viewsToAnimate: Array<View>) {
+ moveFromCenterAnimator.updateDisplayProperties()
viewsToAnimate.forEach {
- moveFromCenterAnimator?.registerViewForAnimation(it)
+ moveFromCenterAnimator.registerViewForAnimation(it)
}
progressProvider.addCallback(transitionListener)
@@ -48,24 +46,23 @@
fun onViewDetached() {
progressProvider.removeCallback(transitionListener)
- moveFromCenterAnimator?.clearRegisteredViews()
- moveFromCenterAnimator = null
+ moveFromCenterAnimator.clearRegisteredViews()
}
fun onStatusBarWidthChanged() {
- moveFromCenterAnimator?.updateDisplayProperties()
- moveFromCenterAnimator?.updateViewPositions()
+ moveFromCenterAnimator.updateDisplayProperties()
+ moveFromCenterAnimator.updateViewPositions()
}
private inner class TransitionListener : TransitionProgressListener {
override fun onTransitionProgress(progress: Float) {
- moveFromCenterAnimator?.onTransitionProgress(progress)
+ moveFromCenterAnimator.onTransitionProgress(progress)
}
override fun onTransitionFinished() {
// Reset translations when transition is stopped/cancelled
// (e.g. the transition could be cancelled mid-way when rotating the screen)
- moveFromCenterAnimator?.onTransitionProgress(1f)
+ moveFromCenterAnimator.onTransitionProgress(1f)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
index 8b394bf..97fce51 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
@@ -30,8 +30,6 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.UserHandle;
@@ -42,6 +40,7 @@
import android.widget.TextView;
import com.android.internal.R;
+import com.android.launcher3.icons.BaseIconFactory.IconOptions;
import com.android.launcher3.icons.IconFactory;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
@@ -280,9 +279,14 @@
final ApplicationInfo appInfo = appEntry.info;
UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid);
IconFactory iconFactory = IconFactory.obtain(context);
- Bitmap iconBmp = iconFactory.createBadgedIconBitmap(
- appInfo.loadUnbadgedIcon(packageManager), user, true).icon;
- return new BitmapDrawable(context.getResources(), iconBmp);
+ try {
+ return iconFactory.createBadgedIconBitmap(
+ appInfo.loadUnbadgedIcon(packageManager),
+ new IconOptions().setUser(user))
+ .newIcon(context);
+ } finally {
+ iconFactory.recycle();
+ }
}
private static boolean showApplicationIcon(ApplicationInfo appInfo,
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 e7ae6cb..fb8efa9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
@@ -20,14 +20,13 @@
import static org.mockito.Mockito.verify;
import android.app.communal.CommunalManager;
-import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
import org.junit.Before;
import org.junit.Test;
@@ -42,15 +41,15 @@
private CommunalSourceMonitor mMonitor;
@Mock
private CommunalManager mCommunalManager;
+ @Mock
+ private CommunalConditionsMonitor mCommunalConditionsMonitor;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext.addMockSystemService(CommunalManager.class, mCommunalManager);
- mMonitor = new CommunalSourceMonitor(
- Handler.createAsync(TestableLooper.get(this).getLooper()),
- new FakeSettings());
+ mMonitor = new CommunalSourceMonitor(mCommunalConditionsMonitor);
final CommunalManagerUpdater updater = new CommunalManagerUpdater(mContext, mMonitor);
updater.start();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
index efb4931..923f018 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
@@ -16,14 +16,20 @@
package com.android.systemui.media.taptotransfer
+import android.view.View
import android.view.WindowManager
+import android.widget.LinearLayout
+import android.widget.TextView
import androidx.test.filters.SmallTest
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.util.mockito.any
+import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
+import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
@@ -48,7 +54,7 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- mediaTttChipController = MediaTttChipController(context, commandRegistry, windowManager)
+ mediaTttChipController = MediaTttChipController(commandRegistry, context, windowManager)
}
@Test(expected = IllegalStateException::class)
@@ -71,24 +77,24 @@
@Test
fun addChipCommand_chipAdded() {
- commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.ADD_CHIP_COMMAND_TAG))
+ commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
verify(windowManager).addView(any(), any())
}
@Test
fun addChipCommand_twice_chipNotAddedTwice() {
- commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.ADD_CHIP_COMMAND_TAG))
+ commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
reset(windowManager)
- commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.ADD_CHIP_COMMAND_TAG))
+ commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
verify(windowManager, never()).addView(any(), any())
}
@Test
fun removeChipCommand_chipRemoved() {
// First, add the chip
- commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.ADD_CHIP_COMMAND_TAG))
+ commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
// Then, remove it
commandRegistry.onShellCommand(pw, arrayOf(MediaTttChipController.REMOVE_CHIP_COMMAND_TAG))
@@ -103,6 +109,104 @@
verify(windowManager, never()).removeView(any())
}
+ @Test
+ fun moveCloserToTransfer_chipTextContainsDeviceName_noLoadingIcon_noUndo() {
+ commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+
+ val chipView = getChipView()
+ assertThat(chipView.getChipText()).contains(DEVICE_NAME)
+ assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
+ assertThat(chipView.getUndoButtonVisibility()).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun transferInitiated_chipTextContainsDeviceName_loadingIcon_noUndo() {
+ commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
+
+ val chipView = getChipView()
+ assertThat(chipView.getChipText()).contains(DEVICE_NAME)
+ assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
+ assertThat(chipView.getUndoButtonVisibility()).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun transferSucceeded_chipTextContainsDeviceName_noLoadingIcon_undo() {
+ commandRegistry.onShellCommand(pw, getTransferSucceededCommand())
+
+ val chipView = getChipView()
+ assertThat(chipView.getChipText()).contains(DEVICE_NAME)
+ assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
+ assertThat(chipView.getUndoButtonVisibility()).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun changeFromCloserToTransferToTransferInitiated_loadingIconAppears() {
+ commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+ commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
+
+ assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun changeFromTransferInitiatedToTransferSucceeded_loadingIconDisappears() {
+ commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
+ commandRegistry.onShellCommand(pw, getTransferSucceededCommand())
+
+ assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.GONE)
+ }
+
+ @Test
+ fun changeFromTransferInitiatedToTransferSucceeded_undoButtonAppears() {
+ commandRegistry.onShellCommand(pw, getTransferInitiatedCommand())
+ commandRegistry.onShellCommand(pw, getTransferSucceededCommand())
+
+ assertThat(getChipView().getUndoButtonVisibility()).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun changeFromTransferSucceededToMoveCloser_undoButtonDisappears() {
+ commandRegistry.onShellCommand(pw, getTransferSucceededCommand())
+ commandRegistry.onShellCommand(pw, getMoveCloserToTransferCommand())
+
+ assertThat(getChipView().getUndoButtonVisibility()).isEqualTo(View.GONE)
+ }
+
+ private fun getMoveCloserToTransferCommand(): Array<String> =
+ arrayOf(
+ MediaTttChipController.ADD_CHIP_COMMAND_TAG,
+ DEVICE_NAME,
+ MediaTttChipController.ChipType.MOVE_CLOSER_TO_TRANSFER.name
+ )
+
+ private fun getTransferInitiatedCommand(): Array<String> =
+ arrayOf(
+ MediaTttChipController.ADD_CHIP_COMMAND_TAG,
+ DEVICE_NAME,
+ MediaTttChipController.ChipType.TRANSFER_INITIATED.name
+ )
+
+ private fun getTransferSucceededCommand(): Array<String> =
+ arrayOf(
+ MediaTttChipController.ADD_CHIP_COMMAND_TAG,
+ DEVICE_NAME,
+ MediaTttChipController.ChipType.TRANSFER_SUCCEEDED.name
+ )
+
+ private fun LinearLayout.getChipText(): String =
+ (this.requireViewById<TextView>(R.id.text)).text as String
+
+ private fun LinearLayout.getLoadingIconVisibility(): Int =
+ this.requireViewById<View>(R.id.loading).visibility
+
+ private fun LinearLayout.getUndoButtonVisibility(): Int =
+ this.requireViewById<View>(R.id.undo).visibility
+
+ private fun getChipView(): LinearLayout {
+ val viewCaptor = ArgumentCaptor.forClass(View::class.java)
+ verify(windowManager).addView(viewCaptor.capture(), any())
+ return viewCaptor.value as LinearLayout
+ }
+
class EmptyCommand : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
}
@@ -111,3 +215,5 @@
}
}
}
+
+private const val DEVICE_NAME = "My Tablet"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
index 339d5bb..0cf063f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
@@ -80,6 +80,7 @@
private RecyclerView mWifiList;
private LinearLayout mSeeAll;
private LinearLayout mWifiScanNotify;
+ private TextView mAirplaneModeSummaryText;
@Before
public void setUp() {
@@ -114,6 +115,7 @@
mWifiList = mDialogView.requireViewById(R.id.wifi_list_layout);
mSeeAll = mDialogView.requireViewById(R.id.see_all_layout);
mWifiScanNotify = mDialogView.requireViewById(R.id.wifi_scan_notify_layout);
+ mAirplaneModeSummaryText = mDialogView.requireViewById(R.id.airplane_mode_summary);
}
@After
@@ -191,7 +193,41 @@
}
@Test
- public void updateDialog_withApmOn_mobileDataLayoutGone() {
+ public void updateDialog_apmOffAndNotCarrierNetwork_mobileDataLayoutGone() {
+ // Mobile network should be gone if the list of active subscriptionId is null.
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(false);
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+ when(mInternetDialogController.hasActiveSubId()).thenReturn(false);
+
+ mInternetDialog.updateDialog(true);
+
+ assertThat(mMobileDataToggle.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_apmOnWithCarrierNetworkAndWifiStatus_mobileDataLayout() {
+ // Carrier network should be gone if airplane mode ON and Wi-Fi is off.
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+ when(mWifiManager.isWifiEnabled()).thenReturn(false);
+
+ mInternetDialog.updateDialog(true);
+
+ assertThat(mMobileDataToggle.getVisibility()).isEqualTo(View.GONE);
+
+ // Carrier network should be visible if airplane mode ON and Wi-Fi is ON.
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+ when(mWifiManager.isWifiEnabled()).thenReturn(true);
+
+ mInternetDialog.updateDialog(true);
+
+ assertThat(mMobileDataToggle.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void updateDialog_apmOnAndNoCarrierNetwork_mobileDataLayoutGone() {
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(false);
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
mInternetDialog.updateDialog(true);
@@ -200,6 +236,51 @@
}
@Test
+ public void updateDialog_apmOnAndWifiOnHasCarrierNetwork_showAirplaneSummary() {
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+ mInternetDialog.mConnectedWifiEntry = null;
+ doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
+
+ mInternetDialog.updateDialog(true);
+
+ assertThat(mMobileDataToggle.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
+ }
+
+ @Test
+ public void updateDialog_apmOffAndWifiOnHasCarrierNetwork_notShowApmSummary() {
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+ mInternetDialog.mConnectedWifiEntry = null;
+ doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
+
+ mInternetDialog.updateDialog(true);
+
+ assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_apmOffAndHasCarrierNetwork_notShowApmSummary() {
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
+
+ mInternetDialog.updateDialog(true);
+
+ assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
+ public void updateDialog_apmOnAndNoCarrierNetwork_notShowApmSummary() {
+ when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(false);
+ when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
+
+ mInternetDialog.updateDialog(true);
+
+ assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
public void updateDialog_wifiOnAndHasInternetWifi_showConnectedWifi() {
// The preconditions WiFi ON and Internet WiFi are already in setUp()
doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 82cd9fa..b832577 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -114,6 +114,7 @@
private List<NotificationEntry> mEntrySet = new ArrayList<>();
private List<ListEntry> mBuiltList;
private TestableStabilityManager mStabilityManager;
+ private TestableNotifFilter mFinalizeFilter;
private Map<String, Integer> mNextIdMap = new ArrayMap<>();
private int mNextRank = 0;
@@ -136,6 +137,8 @@
mStabilityManager = spy(new TestableStabilityManager());
mListBuilder.setNotifStabilityManager(mStabilityManager);
+ mFinalizeFilter = spy(new TestableNotifFilter());
+ mListBuilder.addFinalizeFilter(mFinalizeFilter);
Mockito.verify(mNotifCollection).setBuildListener(mBuildListenerCaptor.capture());
mReadyForBuildListener = Objects.requireNonNull(mBuildListenerCaptor.getValue());
@@ -408,7 +411,6 @@
// THEN the summary has a null parent and an unset firstAddedIteration
assertNull(mEntrySet.get(1).getParent());
- assertEquals(-1, mEntrySet.get(1).mFirstAddedIteration);
}
@Test
@@ -1037,7 +1039,7 @@
}
@Test
- public void testStabilizeGroupsDoesNotAllowGrouping() {
+ public void testStabilizeGroupsDoesNotAllowGroupingExistingNotifications() {
// GIVEN one group child without a summary yet
addGroupChild(0, PACKAGE_1, GROUP_1);
@@ -1056,7 +1058,10 @@
// because group changes aren't allowed by the stability manager
verifyBuiltList(
notif(0),
- notif(2)
+ group(
+ summary(1),
+ child(2)
+ )
);
}
@@ -1110,11 +1115,13 @@
dispatchBuild();
- // THEN all notifications are top-level and the summary doesn't show yet
- // because group changes aren't allowed by the stability manager
+ // THEN first notification stays top-level but the other notifications are grouped.
verifyBuiltList(
notif(0),
- notif(2),
+ group(
+ summary(1),
+ child(2)
+ ),
group(
summary(3),
child(4),
@@ -1209,6 +1216,140 @@
}
@Test
+ public void testStabilityIsolationAllowsGroupToHaveSingleChild() {
+ // GIVEN a group with only one child was already drawn
+ addGroupSummary(0, PACKAGE_1, GROUP_1);
+ addGroupChild(1, PACKAGE_1, GROUP_1);
+
+ dispatchBuild();
+ // NOTICE that the group is pruned and the child is moved to the top level
+ verifyBuiltList(
+ notif(1) // group with only one child is promoted
+ );
+
+ // WHEN another child is added while group changes are disabled.
+ mStabilityManager.setAllowGroupChanges(false);
+ addGroupChild(2, PACKAGE_1, GROUP_1);
+
+ dispatchBuild();
+
+ // THEN the new child should be added to the group
+ verifyBuiltList(
+ group(
+ summary(0),
+ child(2)
+ ),
+ notif(1)
+ );
+ }
+
+ @Test
+ public void testStabilityIsolationExemptsGroupWithFinalizeFilteredChildFromShowingSummary() {
+ // GIVEN a group with only one child was already drawn
+ addGroupSummary(0, PACKAGE_1, GROUP_1);
+ addGroupChild(1, PACKAGE_1, GROUP_1);
+
+ dispatchBuild();
+ // NOTICE that the group is pruned and the child is moved to the top level
+ verifyBuiltList(
+ notif(1) // group with only one child is promoted
+ );
+
+ // WHEN another child is added but still filtered while group changes are disabled.
+ mStabilityManager.setAllowGroupChanges(false);
+ mFinalizeFilter.mIndicesToFilter.add(2);
+ addGroupChild(2, PACKAGE_1, GROUP_1);
+
+ dispatchBuild();
+
+ // THEN the new child should be shown without the summary
+ verifyBuiltList(
+ notif(1) // previously promoted child
+ );
+ }
+
+ @Test
+ public void testStabilityIsolationOfRemovedChildDoesNotExemptGroupFromPrune() {
+ // GIVEN a group with only one child was already drawn
+ addGroupSummary(0, PACKAGE_1, GROUP_1);
+ addGroupChild(1, PACKAGE_1, GROUP_1);
+
+ dispatchBuild();
+ // NOTICE that the group is pruned and the child is moved to the top level
+ verifyBuiltList(
+ notif(1) // group with only one child is promoted
+ );
+
+ // WHEN a new child is added and the old one gets filtered while group changes are disabled.
+ mStabilityManager.setAllowGroupChanges(false);
+ mFinalizeFilter.mIndicesToFilter.add(1);
+ addGroupChild(2, PACKAGE_1, GROUP_1);
+
+ dispatchBuild();
+
+ // THEN the new child should be shown without a group
+ verifyBuiltList(
+ notif(2) // previously promoted child
+ );
+ }
+
+ @Test
+ public void testFinalizeFilteredSummaryPromotesChildren() {
+ // GIVEN a group with only one child was already drawn
+ addGroupSummary(0, PACKAGE_1, GROUP_1);
+ addGroupChild(1, PACKAGE_1, GROUP_1);
+ addGroupChild(2, PACKAGE_1, GROUP_1);
+
+ // WHEN the parent is filtered out at the finalize step
+ mFinalizeFilter.mIndicesToFilter.add(0);
+
+ dispatchBuild();
+
+ // THEN the children should be promoted to the top level
+ verifyBuiltList(
+ notif(1),
+ notif(2)
+ );
+ }
+
+ @Test
+ public void testFinalizeFilteredChildrenPromotesSummary() {
+ // GIVEN a group with only one child was already drawn
+ addGroupSummary(0, PACKAGE_1, GROUP_1);
+ addGroupChild(1, PACKAGE_1, GROUP_1);
+ addGroupChild(2, PACKAGE_1, GROUP_1);
+
+ // WHEN the parent is filtered out at the finalize step
+ mFinalizeFilter.mIndicesToFilter.add(1);
+ mFinalizeFilter.mIndicesToFilter.add(2);
+
+ dispatchBuild();
+
+ // THEN the children should be promoted to the top level
+ verifyBuiltList(
+ notif(0)
+ );
+ }
+
+ @Test
+ public void testFinalizeFilteredChildPromotesSibling() {
+ // GIVEN a group with only one child was already drawn
+ addGroupSummary(0, PACKAGE_1, GROUP_1);
+ addGroupChild(1, PACKAGE_1, GROUP_1);
+ addGroupChild(2, PACKAGE_1, GROUP_1);
+
+ // WHEN the parent is filtered out at the finalize step
+ mFinalizeFilter.mIndicesToFilter.add(1);
+
+ dispatchBuild();
+
+ // THEN the children should be promoted to the top level
+ verifyBuiltList(
+ notif(2)
+ );
+ }
+
+ @Test
public void testBrokenGroupNotificationOrdering() {
// GIVEN two group children with different sections & without a summary yet
addGroupChild(0, PACKAGE_2, GROUP_1);
@@ -1228,30 +1369,6 @@
}
@Test
- public void testStabilizeGroupsHidesGroupSummary() {
- // GIVEN one group child with a summary
- addGroupChild(0, PACKAGE_1, GROUP_1);
- addGroupSummary(1, PACKAGE_1, GROUP_1);
-
- dispatchBuild(); // group summary is hidden because it needs at least 2 children to group
-
- // GIVEN visual stability manager doesn't allow any group changes
- mStabilityManager.setAllowGroupChanges(false);
-
- // WHEN we run the pipeline with the addition of a child
- addGroupChild(2, PACKAGE_1, GROUP_1);
-
- dispatchBuild();
-
- // THEN the children notifications are top-level and the summary still doesn't show yet
- // because group changes aren't allowed by the stability manager
- verifyBuiltList(
- notif(0),
- notif(2)
- );
- }
-
- @Test
public void testStabilizeGroupsDelayedSummaryRendersAllNotifsTopLevel() {
// GIVEN group children posted without a summary
addGroupChild(0, PACKAGE_1, GROUP_1);
@@ -1269,13 +1386,12 @@
dispatchBuild();
- // THEN all entries are top-level since group changes aren't allowed
+ // THEN all entries are top-level, but summary is suppressed
verifyBuiltList(
notif(0),
notif(1),
notif(2),
- notif(3),
- notif(4)
+ notif(3)
);
// WHEN visual stability manager allows group changes again
@@ -1907,6 +2023,19 @@
}
}
+ private class TestableNotifFilter extends NotifFilter {
+ ArrayList<Integer> mIndicesToFilter = new ArrayList<>();
+
+ protected TestableNotifFilter() {
+ super("TestFilter");
+ }
+
+ @Override
+ public boolean shouldFilterOut(@NonNull NotificationEntry entry, long now) {
+ return mIndicesToFilter.stream().anyMatch(i -> notif(i).entry == entry);
+ }
+ }
+
private static class TestableStabilityManager extends NotifStabilityManager {
boolean mAllowGroupChanges = true;
boolean mAllowSectionChanges = true;
@@ -1937,17 +2066,17 @@
}
@Override
- public boolean isGroupChangeAllowed(NotificationEntry entry) {
+ public boolean isGroupChangeAllowed(@NonNull NotificationEntry entry) {
return mAllowGroupChanges;
}
@Override
- public boolean isSectionChangeAllowed(NotificationEntry entry) {
+ public boolean isSectionChangeAllowed(@NonNull NotificationEntry entry) {
return mAllowSectionChanges;
}
@Override
- public boolean isEntryReorderingAllowed(ListEntry entry) {
+ public boolean isEntryReorderingAllowed(@NonNull ListEntry entry) {
return mAllowEntryReodering;
}
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 3ec9629..99800e4 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
@@ -120,6 +120,7 @@
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.PulseExpansionHandler;
+import com.android.systemui.statusbar.QsFrameTranslateController;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
@@ -354,6 +355,8 @@
private InteractionJankMonitor mInteractionJankMonitor;
@Mock
private NotificationsQSContainerController mNotificationsQSContainerController;
+ @Mock
+ private QsFrameTranslateController mQsFrameTranslateController;
private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
@@ -530,7 +533,8 @@
mNotificationRemoteInputManager,
mSysUIUnfoldComponent,
mControlsComponent,
- mInteractionJankMonitor);
+ mInteractionJankMonitor,
+ mQsFrameTranslateController);
mNotificationPanelViewController.initDependencies(
mStatusBar,
() -> {},
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index dc32007..7d266e9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -102,7 +102,7 @@
verify(view.viewTreeObserver).addOnPreDrawListener(argumentCaptor.capture())
argumentCaptor.value.onPreDraw()
- verify(moveFromCenterAnimation).onViewsReady(any(), any())
+ verify(moveFromCenterAnimation).onViewsReady(any())
}
private fun createViewMock(): PhoneStatusBarView {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
new file mode 100644
index 0000000..1ce7ff4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
@@ -0,0 +1,104 @@
+package com.android.systemui.statusbar.phone
+
+import android.graphics.Point
+import android.view.Display
+import android.view.Surface
+import android.view.View
+import android.view.WindowManager
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.unfold.TestUnfoldTransitionProvider
+import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class StatusBarMoveFromCenterAnimationControllerTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var windowManager: WindowManager
+
+ @Mock
+ private lateinit var display: Display
+
+ private val view: View = View(context)
+ private val progressProvider = TestUnfoldTransitionProvider()
+ private val scopedProvider = ScopedUnfoldTransitionProgressProvider(progressProvider)
+
+ private lateinit var controller: StatusBarMoveFromCenterAnimationController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ `when`(windowManager.defaultDisplay).thenReturn(display)
+ `when`(display.rotation).thenReturn(Surface.ROTATION_0)
+ `when`(display.getSize(any())).thenAnswer {
+ val point = it.arguments[0] as Point
+ point.x = 100
+ point.y = 100
+ Unit
+ }
+
+ scopedProvider.setReadyToHandleTransition(true)
+
+ controller = StatusBarMoveFromCenterAnimationController(scopedProvider, windowManager)
+ }
+
+ @Test
+ fun onTransitionProgressAndFinished_resetsTranslations() {
+ controller.onViewsReady(arrayOf(view))
+
+ progressProvider.onTransitionProgress(0.5f)
+ progressProvider.onTransitionFinished()
+
+ assertThat(view.translationX).isZero()
+ }
+
+ @Test
+ fun onTransitionProgress_updatesTranslations() {
+ controller.onViewsReady(arrayOf(view))
+
+ progressProvider.onTransitionProgress(0.5f)
+
+ assertThat(view.translationX).isNonZero()
+ }
+
+ @Test
+ fun onTransitionProgress_whenDetached_doesNotUpdateTranslations() {
+ controller.onViewsReady(arrayOf(view))
+ controller.onViewDetached()
+
+ progressProvider.onTransitionProgress(0.5f)
+
+ assertThat(view.translationX).isZero()
+ }
+
+ @Test
+ fun detachedAfterProgress_resetsTranslations() {
+ controller.onViewsReady(arrayOf(view))
+ progressProvider.onTransitionProgress(0.5f)
+
+ controller.onViewDetached()
+
+ assertThat(view.translationX).isZero()
+ }
+
+ @Test
+ fun transitionFinished_viewReAttached_noChangesToTranslation() {
+ controller.onViewsReady(arrayOf(view))
+ progressProvider.onTransitionProgress(0.5f)
+ progressProvider.onTransitionFinished()
+ controller.onViewDetached()
+
+ controller.onViewsReady(arrayOf(view))
+ controller.onStatusBarWidthChanged()
+
+ assertThat(view.translationX).isZero()
+ }
+}
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 dda1c4f..cd338a4 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -16,9 +16,9 @@
package com.android.server.accessibility.magnification;
-import static android.accessibilityservice.MagnificationConfig.DEFAULT_MODE;
-import static android.accessibilityservice.MagnificationConfig.FULLSCREEN_MODE;
-import static android.accessibilityservice.MagnificationConfig.WINDOW_MODE;
+import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_DEFAULT;
+import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
+import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_WINDOW;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
@@ -66,14 +66,14 @@
public @NonNull MagnificationConfig getMagnificationConfig(int displayId) {
final int mode = getControllingMode(displayId);
MagnificationConfig.Builder builder = new MagnificationConfig.Builder();
- if (mode == FULLSCREEN_MODE) {
+ if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
final FullScreenMagnificationController fullScreenMagnificationController =
mController.getFullScreenMagnificationController();
builder.setMode(mode)
.setScale(fullScreenMagnificationController.getScale(displayId))
.setCenterX(fullScreenMagnificationController.getCenterX(displayId))
.setCenterY(fullScreenMagnificationController.getCenterY(displayId));
- } else if (mode == WINDOW_MODE) {
+ } else if (mode == MAGNIFICATION_MODE_WINDOW) {
final WindowMagnificationManager windowMagnificationManager =
mController.getWindowMagnificationMgr();
builder.setMode(mode)
@@ -103,14 +103,14 @@
}
int configMode = config.getMode();
- if (configMode == DEFAULT_MODE) {
+ if (configMode == MAGNIFICATION_MODE_DEFAULT) {
configMode = getControllingMode(displayId);
}
- if (configMode == FULLSCREEN_MODE) {
+ if (configMode == MAGNIFICATION_MODE_FULLSCREEN) {
return setScaleAndCenterForFullScreenMagnification(displayId, config.getScale(),
config.getCenterX(), config.getCenterY(),
animate, id);
- } else if (configMode == WINDOW_MODE) {
+ } else if (configMode == MAGNIFICATION_MODE_WINDOW) {
return mController.getWindowMagnificationMgr().enableWindowMagnification(displayId,
config.getScale(), config.getCenterX(), config.getCenterY());
}
@@ -141,9 +141,9 @@
*/
public float getScale(int displayId) {
int mode = getControllingMode(displayId);
- if (mode == FULLSCREEN_MODE) {
+ if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
return mController.getFullScreenMagnificationController().getScale(displayId);
- } else if (mode == WINDOW_MODE) {
+ } else if (mode == MAGNIFICATION_MODE_WINDOW) {
return mController.getWindowMagnificationMgr().getScale(displayId);
}
return 0;
@@ -161,7 +161,7 @@
*/
public float getCenterX(int displayId, boolean canControlMagnification) {
int mode = getControllingMode(displayId);
- if (mode == FULLSCREEN_MODE) {
+ if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
canControlMagnification);
try {
@@ -171,7 +171,7 @@
unregister(displayId);
}
}
- } else if (mode == WINDOW_MODE) {
+ } else if (mode == MAGNIFICATION_MODE_WINDOW) {
return mController.getWindowMagnificationMgr().getCenterX(displayId);
}
return 0;
@@ -189,7 +189,7 @@
*/
public float getCenterY(int displayId, boolean canControlMagnification) {
int mode = getControllingMode(displayId);
- if (mode == FULLSCREEN_MODE) {
+ if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
boolean registeredJustForThisCall = registerDisplayMagnificationIfNeeded(displayId,
canControlMagnification);
try {
@@ -199,7 +199,7 @@
unregister(displayId);
}
}
- } else if (mode == WINDOW_MODE) {
+ } else if (mode == MAGNIFICATION_MODE_WINDOW) {
return mController.getWindowMagnificationMgr().getCenterY(displayId);
}
return 0;
@@ -221,9 +221,9 @@
public Region getMagnificationRegion(int displayId, @NonNull Region outRegion,
boolean canControlMagnification) {
int mode = getControllingMode(displayId);
- if (mode == FULLSCREEN_MODE) {
+ if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
getFullscreenMagnificationRegion(displayId, outRegion, canControlMagnification);
- } else if (mode == WINDOW_MODE) {
+ } else if (mode == MAGNIFICATION_MODE_WINDOW) {
mController.getWindowMagnificationMgr().getMagnificationSourceBounds(displayId,
outRegion);
}
@@ -268,9 +268,9 @@
*/
public boolean reset(int displayId, boolean animate) {
int mode = getControllingMode(displayId);
- if (mode == FULLSCREEN_MODE) {
+ if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
return mController.getFullScreenMagnificationController().reset(displayId, animate);
- } else if (mode == WINDOW_MODE) {
+ } else if (mode == MAGNIFICATION_MODE_WINDOW) {
return mController.getWindowMagnificationMgr().reset(displayId);
}
return false;
@@ -290,9 +290,9 @@
*/
public boolean isMagnifying(int displayId) {
int mode = getControllingMode(displayId);
- if (mode == FULLSCREEN_MODE) {
+ if (mode == MAGNIFICATION_MODE_FULLSCREEN) {
return mController.getFullScreenMagnificationController().isMagnifying(displayId);
- } else if (mode == WINDOW_MODE) {
+ } else if (mode == MAGNIFICATION_MODE_WINDOW) {
return mController.getWindowMagnificationMgr().isWindowMagnifierEnabled(displayId);
}
return false;
@@ -308,14 +308,14 @@
public int getControllingMode(int displayId) {
if (mController.isActivated(displayId,
ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)) {
- return WINDOW_MODE;
+ return MAGNIFICATION_MODE_WINDOW;
} else if (mController.isActivated(displayId,
ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)) {
- return FULLSCREEN_MODE;
+ return MAGNIFICATION_MODE_FULLSCREEN;
} else {
return (mController.getLastActivatedMode() == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)
- ? WINDOW_MODE
- : FULLSCREEN_MODE;
+ ? MAGNIFICATION_MODE_WINDOW
+ : MAGNIFICATION_MODE_FULLSCREEN;
}
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 9351415..ba6854b 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -121,6 +121,7 @@
"java/com/android/server/am/EventLogTags.logtags",
"java/com/android/server/wm/EventLogTags.logtags",
"java/com/android/server/policy/EventLogTags.logtags",
+ ":services.connectivity-nsd-sources",
],
libs: [
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 1929df8..c7f4b4d 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -1598,6 +1598,8 @@
} else if (vol.type == VolumeInfo.TYPE_STUB) {
if (vol.disk.isStubVisible()) {
vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_WRITE;
+ } else {
+ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE_FOR_READ;
}
vol.mountUserId = mCurrentUserId;
mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
@@ -3842,14 +3844,15 @@
final boolean primary = false;
final boolean removable = false;
final boolean emulated = true;
+ final boolean stub = false;
final boolean allowMassStorage = false;
final long maxFileSize = 0;
final UserHandle user = new UserHandle(userId);
final String envState = Environment.MEDIA_MOUNTED_READ_ONLY;
final String description = mContext.getString(android.R.string.unknownName);
- res.add(new StorageVolume(id, path, path, description, primary, removable,
- emulated, allowMassStorage, maxFileSize, user, null /*uuid */, id, envState));
+ res.add(new StorageVolume(id, path, path, description, primary, removable, emulated,
+ stub, allowMassStorage, maxFileSize, user, null /*uuid */, id, envState));
}
if (!foundPrimary) {
@@ -3864,6 +3867,7 @@
final boolean primary = true;
final boolean removable = primaryPhysical;
final boolean emulated = !primaryPhysical;
+ final boolean stub = false;
final boolean allowMassStorage = false;
final long maxFileSize = 0L;
final UserHandle owner = new UserHandle(userId);
@@ -3872,7 +3876,7 @@
final String state = Environment.MEDIA_REMOVED;
res.add(0, new StorageVolume(id, path, path,
- description, primary, removable, emulated,
+ description, primary, removable, emulated, stub,
allowMassStorage, maxFileSize, owner, uuid, fsUuid, state));
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 03a4d84..5fc11e8 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -130,6 +130,7 @@
private static IBatteryStats sService;
+ private final PowerProfile mPowerProfile;
final BatteryStatsImpl mStats;
private final BatteryUsageStatsStore mBatteryUsageStatsStore;
private final BatteryStatsImpl.UserInfoProvider mUserManagerUserInfoProvider;
@@ -343,13 +344,15 @@
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
+ mPowerProfile = new PowerProfile(context);
+
mStats = new BatteryStatsImpl(systemDir, handler, this,
this, mUserManagerUserInfoProvider);
mWorker = new BatteryExternalStatsWorker(context, mStats);
mStats.setExternalStatsSyncLocked(mWorker);
mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
- mStats.setPowerProfileLocked(new PowerProfile(context));
+ mStats.setPowerProfileLocked(mPowerProfile);
mStats.startTrackingSystemServerCpuTime();
if (BATTERY_USAGE_STORE_ENABLED) {
@@ -2464,6 +2467,7 @@
BatteryStatsImpl checkinStats = new BatteryStatsImpl(
null, mStats.mHandler, null, null,
mUserManagerUserInfoProvider);
+ checkinStats.setPowerProfileLocked(mPowerProfile);
checkinStats.readSummaryFromParcel(in);
in.recycle();
checkinStats.dumpProtoLocked(
@@ -2504,6 +2508,7 @@
BatteryStatsImpl checkinStats = new BatteryStatsImpl(
null, mStats.mHandler, null, null,
mUserManagerUserInfoProvider);
+ checkinStats.setPowerProfileLocked(mPowerProfile);
checkinStats.readSummaryFromParcel(in);
in.recycle();
checkinStats.dumpCheckinLocked(mContext, pw, apps, flags,
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 47e24b1..5a54332 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1500,7 +1500,7 @@
int schedGroup;
int procState;
int cachedAdjSeq;
- int capability = 0;
+ int capability = cycleReEval ? app.mState.getCurCapability() : 0;
boolean foregroundActivities = false;
boolean hasVisibleActivities = false;
@@ -1891,10 +1891,6 @@
}
if ((cr.flags & Context.BIND_WAIVE_PRIORITY) == 0) {
- if (shouldSkipDueToCycle(app, cstate, procState, adj, cycleReEval)) {
- continue;
- }
-
if (cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)) {
capability |= cstate.getCurCapability();
}
@@ -1915,6 +1911,10 @@
}
}
+ if (shouldSkipDueToCycle(app, cstate, procState, adj, cycleReEval)) {
+ continue;
+ }
+
if (clientProcState >= PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
// we are going to consider it empty. The specific cached state
diff --git a/services/core/java/com/android/server/audio/TEST_MAPPING b/services/core/java/com/android/server/audio/TEST_MAPPING
index 90246f8..5a6c6a5 100644
--- a/services/core/java/com/android/server/audio/TEST_MAPPING
+++ b/services/core/java/com/android/server/audio/TEST_MAPPING
@@ -1,13 +1,13 @@
{
"presubmit-large": [
{
- "name": "CtsMediaTestCases",
+ "name": "CtsMediaAudioTestCases",
"options": [
{
- "include-filter": "android.media.cts.AudioManagerTest"
+ "include-filter": "android.media.audio.cts.AudioManagerTest"
},
{
- "include-filter": "android.media.cts.AudioFocusTest"
+ "include-filter": "android.media.audio.cts.AudioFocusTest"
}
]
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 9499e51..3c6b096 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -2750,7 +2750,7 @@
return;
}
final IBinder targetWindow = mImeTargetWindowMap.get(startInputToken);
- if (targetWindow != null && mLastImeTargetWindow != targetWindow) {
+ if (targetWindow != null) {
mWindowManagerInternal.updateInputMethodTargetWindow(token, targetWindow);
}
mLastImeTargetWindow = targetWindow;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 99fdb2d..f65deeb 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5073,7 +5073,7 @@
final DumpFilter filter = DumpFilter.parseFromArguments(args);
final long token = Binder.clearCallingIdentity();
try {
- final ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions =
+ final ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions =
getAllUsersNotificationPermissions();
if (filter.stats) {
dumpJson(pw, filter, pkgPermissions);
@@ -5911,17 +5911,18 @@
// Returns a single map containing that info keyed by (uid, package name) for all users.
// Because this calls into mPermissionHelper, this method must never be called with a lock held.
@VisibleForTesting
- protected ArrayMap<Pair<Integer, String>, Boolean> getAllUsersNotificationPermissions() {
+ protected ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>>
+ getAllUsersNotificationPermissions() {
// don't bother if migration is not enabled
if (!mEnableAppSettingMigration) {
return null;
}
- ArrayMap<Pair<Integer, String>, Boolean> allPermissions = new ArrayMap<>();
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> allPermissions = new ArrayMap<>();
final List<UserInfo> allUsers = mUm.getUsers();
// for each of these, get the package notification permissions that are associated
// with this user and add it to the map
for (UserInfo ui : allUsers) {
- ArrayMap<Pair<Integer, String>, Boolean> userPermissions =
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> userPermissions =
mPermissionHelper.getNotificationPermissionValues(
ui.getUserHandle().getIdentifier());
for (Pair<Integer, String> pair : userPermissions.keySet()) {
@@ -5932,7 +5933,7 @@
}
private void dumpJson(PrintWriter pw, @NonNull DumpFilter filter,
- ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
JSONObject dump = new JSONObject();
try {
dump.put("service", "Notification Manager");
@@ -5956,7 +5957,7 @@
}
private void dumpProto(FileDescriptor fd, @NonNull DumpFilter filter,
- ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
final ProtoOutputStream proto = new ProtoOutputStream(fd);
synchronized (mNotificationLock) {
int N = mNotificationList.size();
@@ -6047,7 +6048,7 @@
}
void dumpImpl(PrintWriter pw, @NonNull DumpFilter filter,
- ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
pw.print("Current Notification Manager state");
if (filter.filtered) {
pw.print(" (filtered to "); pw.print(filter); pw.print(")");
diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java
index f53bb75..e64ec77 100644
--- a/services/core/java/com/android/server/notification/PermissionHelper.java
+++ b/services/core/java/com/android/server/notification/PermissionHelper.java
@@ -36,10 +36,8 @@
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -139,15 +137,17 @@
return granted;
}
+ // Key: (uid, package name); Value: (granted, user set)
public @NonNull
- ArrayMap<Pair<Integer, String>, Boolean> getNotificationPermissionValues(
- int userId) {
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>>
+ getNotificationPermissionValues(int userId) {
assertFlag();
- ArrayMap<Pair<Integer, String>, Boolean> notifPermissions = new ArrayMap<>();
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> notifPermissions = new ArrayMap<>();
Set<Pair<Integer, String>> allRequestingUids = getAppsRequestingPermission(userId);
Set<Pair<Integer, String>> allApprovedUids = getAppsGrantedPermission(userId);
for (Pair<Integer, String> pair : allRequestingUids) {
- notifPermissions.put(pair, allApprovedUids.contains(pair));
+ notifPermissions.put(pair, new Pair(allApprovedUids.contains(pair),
+ isPermissionUserSet(pair.second /* package name */, userId)));
}
return notifPermissions;
}
@@ -196,6 +196,18 @@
return false;
}
+ boolean isPermissionUserSet(String packageName, @UserIdInt int userId) {
+ assertFlag();
+ try {
+ int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION,
+ userId);
+ return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not reach system server", e);
+ }
+ return false;
+ }
+
private void assertFlag() {
if (!mMigrationEnabled) {
throw new IllegalStateException("Method called without checking flag value");
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 4e1279c..49e223c 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -557,7 +557,7 @@
out.attributeBoolean(null, ATT_HIDE_SILENT, mHideSilentStatusBarIcons);
out.endTag(null, TAG_STATUS_ICONS);
}
- ArrayMap<Pair<Integer, String>, Boolean> notifPermissions = new ArrayMap<>();
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> notifPermissions = new ArrayMap<>();
if (mPermissionHelper.isMigrationEnabled() && forBackup) {
notifPermissions = mPermissionHelper.getNotificationPermissionValues(userId);
}
@@ -573,9 +573,8 @@
out.attribute(null, ATT_NAME, r.pkg);
if (!notifPermissions.isEmpty()) {
Pair<Integer, String> app = new Pair(r.uid, r.pkg);
- out.attributeInt(null, ATT_IMPORTANCE, notifPermissions.get(app)
- ? IMPORTANCE_DEFAULT
- : IMPORTANCE_NONE);
+ out.attributeInt(null, ATT_IMPORTANCE,
+ notifPermissions.get(app).first ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
notifPermissions.remove(app);
} else {
if (r.importance != DEFAULT_IMPORTANCE) {
@@ -642,7 +641,7 @@
out.startTag(null, TAG_PACKAGE);
out.attribute(null, ATT_NAME, app.second);
out.attributeInt(null, ATT_IMPORTANCE,
- notifPermissions.get(app) ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
+ notifPermissions.get(app).first ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
out.endTag(null, TAG_PACKAGE);
}
}
@@ -1932,7 +1931,7 @@
public void dump(PrintWriter pw, String prefix,
@NonNull NotificationManagerService.DumpFilter filter,
- ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
pw.print(prefix);
pw.println("per-package config version: " + XML_VERSION);
@@ -1946,7 +1945,7 @@
public void dump(ProtoOutputStream proto,
@NonNull NotificationManagerService.DumpFilter filter,
- ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
synchronized (mPackagePreferences) {
dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS, filter,
mPackagePreferences, pkgPermissions);
@@ -1958,7 +1957,7 @@
private void dumpPackagePreferencesLocked(PrintWriter pw, String prefix,
@NonNull NotificationManagerService.DumpFilter filter,
ArrayMap<String, PackagePreferences> packagePreferences,
- ArrayMap<Pair<Integer, String>, Boolean> packagePermissions) {
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> packagePermissions) {
// Used for tracking which package preferences we've seen already for notification
// permission reasons; after handling packages with local preferences, we'll want to dump
// the ones with notification permissions set but not local prefs.
@@ -1987,8 +1986,10 @@
if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
pw.print(" importance=");
pw.print(NotificationListenerService.Ranking.importanceToString(
- packagePermissions.get(key)
+ packagePermissions.get(key).first
? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
+ pw.print(" userSet=");
+ pw.print(packagePermissions.get(key).second);
pkgsWithPermissionsToHandle.remove(key);
}
}
@@ -2042,7 +2043,10 @@
pw.print(')');
pw.print(" importance=");
pw.print(NotificationListenerService.Ranking.importanceToString(
- packagePermissions.get(p) ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
+ packagePermissions.get(p).first
+ ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
+ pw.print(" userSet=");
+ pw.print(packagePermissions.get(p).second);
pw.println();
}
}
@@ -2052,7 +2056,7 @@
private void dumpPackagePreferencesLocked(ProtoOutputStream proto, long fieldId,
@NonNull NotificationManagerService.DumpFilter filter,
ArrayMap<String, PackagePreferences> packagePreferences,
- ArrayMap<Pair<Integer, String>, Boolean> packagePermissions) {
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> packagePermissions) {
Set<Pair<Integer, String>> pkgsWithPermissionsToHandle = null;
if (packagePermissions != null) {
pkgsWithPermissionsToHandle = packagePermissions.keySet();
@@ -2071,7 +2075,8 @@
Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
if (packagePermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
proto.write(RankingHelperProto.RecordProto.IMPORTANCE,
- packagePermissions.get(key) ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
+ packagePermissions.get(key).first
+ ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
pkgsWithPermissionsToHandle.remove(key);
}
} else {
@@ -2099,7 +2104,8 @@
proto.write(RankingHelperProto.RecordProto.PACKAGE, p.second);
proto.write(RankingHelperProto.RecordProto.UID, p.first);
proto.write(RankingHelperProto.RecordProto.IMPORTANCE,
- packagePermissions.get(p) ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
+ packagePermissions.get(p).first
+ ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
proto.end(fToken);
}
}
@@ -2110,7 +2116,7 @@
* Fills out {@link PackageNotificationPreferences} proto and wraps it in a {@link StatsEvent}.
*/
public void pullPackagePreferencesStats(List<StatsEvent> events,
- ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
Set<Pair<Integer, String>> pkgsWithPermissionsToHandle = null;
if (pkgPermissions != null) {
pkgsWithPermissionsToHandle = pkgPermissions.keySet();
@@ -2134,7 +2140,8 @@
int importance = IMPORTANCE_NONE;
Pair<Integer, String> key = new Pair<>(r.uid, r.pkg);
if (pkgPermissions != null && pkgsWithPermissionsToHandle.contains(key)) {
- importance = pkgPermissions.get(key) ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE;
+ importance = pkgPermissions.get(key).first
+ ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE;
pkgsWithPermissionsToHandle.remove(key);
}
event.writeInt(importance);
@@ -2158,7 +2165,7 @@
.setAtomId(PACKAGE_NOTIFICATION_PREFERENCES);
event.writeInt(p.first);
event.addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
- event.writeInt(pkgPermissions.get(p) ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
+ event.writeInt(pkgPermissions.get(p).first ? IMPORTANCE_DEFAULT : IMPORTANCE_NONE);
// fill out the rest of the fields with default values so as not to confuse the
// builder
@@ -2236,7 +2243,7 @@
}
public JSONObject dumpJson(NotificationManagerService.DumpFilter filter,
- ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
JSONObject ranking = new JSONObject();
JSONArray PackagePreferencess = new JSONArray();
try {
@@ -2266,7 +2273,7 @@
&& pkgsWithPermissionsToHandle.contains(key)) {
PackagePreferences.put("importance",
NotificationListenerService.Ranking.importanceToString(
- pkgPermissions.get(key)
+ pkgPermissions.get(key).first
? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
pkgsWithPermissionsToHandle.remove(key);
}
@@ -2316,7 +2323,7 @@
PackagePreferences.put("packageName", p.second);
PackagePreferences.put("importance",
NotificationListenerService.Ranking.importanceToString(
- pkgPermissions.get(p)
+ pkgPermissions.get(p).first
? IMPORTANCE_DEFAULT : IMPORTANCE_NONE));
} catch (JSONException e) {
// pass
@@ -2344,7 +2351,7 @@
* @return
*/
public JSONArray dumpBansJson(NotificationManagerService.DumpFilter filter,
- ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
JSONArray bans = new JSONArray();
Map<Integer, String> packageBans = mPermissionHelper.isMigrationEnabled()
? getPermissionBasedPackageBans(pkgPermissions) : getPackageBans();
@@ -2383,11 +2390,11 @@
// Same functionality as getPackageBans by extracting the set of packages from the provided
// map that are disallowed from sending notifications.
protected Map<Integer, String> getPermissionBasedPackageBans(
- ArrayMap<Pair<Integer, String>, Boolean> pkgPermissions) {
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> pkgPermissions) {
ArrayMap<Integer, String> packageBans = new ArrayMap<>();
if (pkgPermissions != null) {
for (Pair<Integer, String> p : pkgPermissions.keySet()) {
- if (!pkgPermissions.get(p)) {
+ if (!pkgPermissions.get(p).first) {
packageBans.put(p.first, p.second);
}
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 37cb8a9..fb77d10 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -1081,6 +1081,10 @@
} catch (RemoteException e) {
throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
"apexservice not available");
+ } catch (PackageManagerException e) {
+ // Catching it in order not to fall back to Exception which rethrows the
+ // PackageManagerException with a common error code.
+ throw e;
} catch (Exception e) {
// TODO(b/187864524): is INSTALL_FAILED_INTERNAL_ERROR is the right error code here?
throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR,
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index b605c93..9b21790 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -2347,7 +2347,7 @@
@Override
public final boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
int userId, @PackageManager.ComponentInfoFlags long flags) {
- return filterStaticSharedLibPackage(ps, uid, userId, flags) && filterSdkLibPackage(ps, uid,
+ return filterStaticSharedLibPackage(ps, uid, userId, flags) || filterSdkLibPackage(ps, uid,
userId, flags);
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 28204ea..55a0c96 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -706,10 +706,9 @@
if (mCommitted.get()) {
mStagingManager.abortCommittedSession(this);
}
- destroy();
+ destroyInternal();
dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
- maybeFinishChildSessions(INSTALL_FAILED_ABORTED,
- "Session was abandoned because the parent session is abandoned");
+ maybeCleanUpChildSessions();
};
if (mStageDirInUse) {
// Pre-reboot verification is ongoing, not safe to clean up the session yet.
@@ -2132,7 +2131,6 @@
destroy();
// Dispatch message to remove session from PackageInstallerService.
dispatchSessionFinished(error, msg, null);
- maybeFinishChildSessions(error, msg);
}
}
@@ -3648,19 +3646,20 @@
throw new SecurityException("Must be sealed to accept permissions");
}
- PackageInstallerSession root = hasParentSessionId()
- ? mSessionProvider.getSession(getParentSessionId()) : this;
-
if (accepted) {
// Mark and kick off another install pass
synchronized (mLock) {
mPermissionsManuallyAccepted = true;
}
+
+ PackageInstallerSession root =
+ (hasParentSessionId())
+ ? mSessionProvider.getSession(getParentSessionId())
+ : this;
root.mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
} else {
- root.destroy();
- root.dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
- root.maybeFinishChildSessions(INSTALL_FAILED_ABORTED, "User rejected permissions");
+ destroyInternal();
+ dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
}
}
@@ -3711,12 +3710,27 @@
}
/**
- * Calls dispatchSessionFinished() on all child sessions with the given error code and
- * error message to prevent orphaned child sessions.
+ * Cleans up the relevant stored files and information of all child sessions.
+ * <p>Cleaning up the stored files and session information is necessary for
+ * preventing the orphan children sessions.
+ * <ol>
+ * <li>To call {@link #destroyInternal()} cleans up the stored files.</li>
+ * <li>To call {@link #dispatchSessionFinished(int, String, Bundle)} to trigger the
+ * procedure to clean up the information in PackageInstallerService.</li>
+ * </ol></p>
*/
- private void maybeFinishChildSessions(int returnCode, String msg) {
- for (PackageInstallerSession child : getChildSessions()) {
- child.dispatchSessionFinished(returnCode, msg, null);
+ private void maybeCleanUpChildSessions() {
+ if (!isMultiPackage()) {
+ return;
+ }
+
+ final List<PackageInstallerSession> childSessions = getChildSessions();
+ final int size = childSessions.size();
+ for (int i = 0; i < size; ++i) {
+ final PackageInstallerSession session = childSessions.get(i);
+ session.destroyInternal();
+ session.dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned"
+ + " because the parent session is abandoned", null);
}
}
@@ -3728,12 +3742,10 @@
if (LOGD) Slog.d(TAG, "Ignoring abandon for staging files are in use");
return;
}
- mDestroyed = true;
+ destroyInternal();
}
- destroy();
dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
- maybeFinishChildSessions(INSTALL_FAILED_ABORTED,
- "Session was abandoned because the parent session is abandoned");
+ maybeCleanUpChildSessions();
}
private void assertNotChild(String cookie) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index b89017ed..0564e85 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -878,6 +878,9 @@
if (userId == UserHandle.USER_ALL) {
getFlags |= PackageManager.MATCH_KNOWN_PACKAGES;
}
+ if (showSdks) {
+ getFlags |= PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES;
+ }
final int translatedUserId =
translateUserId(userId, UserHandle.USER_SYSTEM, "runListPackages");
@SuppressWarnings("unchecked")
diff --git a/services/core/java/com/android/server/policy/SideFpsEventHandler.java b/services/core/java/com/android/server/policy/SideFpsEventHandler.java
index 7c0005c..f368698 100644
--- a/services/core/java/com/android/server/policy/SideFpsEventHandler.java
+++ b/services/core/java/com/android/server/policy/SideFpsEventHandler.java
@@ -16,14 +16,19 @@
package com.android.server.policy;
+import static android.hardware.fingerprint.FingerprintStateListener.STATE_BP_AUTH;
import static android.hardware.fingerprint.FingerprintStateListener.STATE_ENROLLING;
import static android.hardware.fingerprint.FingerprintStateListener.STATE_IDLE;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -34,9 +39,11 @@
import android.view.WindowManager;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Supplier;
/**
* Defines behavior for handling interactions between power button events and
@@ -44,68 +51,115 @@
* lives on the power button.
*/
public class SideFpsEventHandler {
+
+ private static final int DEBOUNCE_DELAY_MILLIS = 500;
+
@NonNull private final Context mContext;
@NonNull private final Handler mHandler;
@NonNull private final PowerManager mPowerManager;
- @NonNull private final AtomicBoolean mIsSideFps;
+ @NonNull private final Supplier<AlertDialog.Builder> mDialogSupplier;
@NonNull private final AtomicBoolean mSideFpsEventHandlerReady;
+ @Nullable private Dialog mDialog;
+ @NonNull private final DialogInterface.OnDismissListener mDialogDismissListener = (dialog) -> {
+ if (mDialog == dialog) {
+ mDialog = null;
+ }
+ };
+
private @FingerprintStateListener.State int mFingerprintState;
SideFpsEventHandler(Context context, Handler handler, PowerManager powerManager) {
+ this(context, handler, powerManager, () -> new AlertDialog.Builder(context));
+ }
+
+ @VisibleForTesting
+ SideFpsEventHandler(Context context, Handler handler, PowerManager powerManager,
+ Supplier<AlertDialog.Builder> dialogSupplier) {
mContext = context;
mHandler = handler;
mPowerManager = powerManager;
+ mDialogSupplier = dialogSupplier;
mFingerprintState = STATE_IDLE;
- mIsSideFps = new AtomicBoolean(false);
mSideFpsEventHandlerReady = new AtomicBoolean(false);
+
+ // ensure dialog is dismissed if screen goes off for unrelated reasons
+ context.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (mDialog != null) {
+ mDialog.dismiss();
+ mDialog = null;
+ }
+ }
+ }, new IntentFilter(Intent.ACTION_SCREEN_OFF));
}
/**
- * Called from {@link PhoneWindowManager} after power button is pressed. Checks fingerprint
- * sensor state and if mFingerprintState = STATE_ENROLLING, displays a dialog confirming intent
- * to turn screen off. If confirmed, the device goes to sleep, and if canceled, the dialog is
- * dismissed.
+ * Called from {@link PhoneWindowManager} after the power button is pressed and displays a
+ * dialog confirming the user's intent to turn screen off if a fingerprint operation is
+ * active. The device goes to sleep if confirmed otherwise the dialog is dismissed.
+ *
* @param eventTime powerPress event time
* @return true if powerPress was consumed, false otherwise
*/
public boolean onSinglePressDetected(long eventTime) {
- if (!mSideFpsEventHandlerReady.get() || !mIsSideFps.get()
- || mFingerprintState != STATE_ENROLLING) {
+ if (!mSideFpsEventHandlerReady.get()) {
return false;
}
- mHandler.post(() -> {
- Dialog confirmScreenOffDialog = new AlertDialog.Builder(mContext)
- .setTitle(R.string.fp_enrollment_powerbutton_intent_title)
- .setMessage(R.string.fp_enrollment_powerbutton_intent_message)
- .setPositiveButton(
- R.string.fp_enrollment_powerbutton_intent_positive_button,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- mPowerManager.goToSleep(
- eventTime,
- PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
- 0 /* flags */
- );
- }
- })
- .setNegativeButton(
- R.string.fp_enrollment_powerbutton_intent_negative_button,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- }
- })
- .setCancelable(false)
- .create();
- confirmScreenOffDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
- confirmScreenOffDialog.show();
- });
- return true;
+
+ switch (mFingerprintState) {
+ case STATE_ENROLLING:
+ case STATE_BP_AUTH:
+ mHandler.post(() -> {
+ if (mDialog != null) {
+ mDialog.dismiss();
+ }
+ mDialog = showConfirmDialog(mDialogSupplier.get(),
+ mPowerManager, eventTime, mFingerprintState, mDialogDismissListener);
+ });
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @NonNull
+ private static Dialog showConfirmDialog(@NonNull AlertDialog.Builder dialogBuilder,
+ @NonNull PowerManager powerManager, long eventTime,
+ @FingerprintStateListener.State int fingerprintState,
+ @NonNull DialogInterface.OnDismissListener dismissListener) {
+ final boolean enrolling = fingerprintState == STATE_ENROLLING;
+ final int title = enrolling ? R.string.fp_power_button_enrollment_title
+ : R.string.fp_power_button_bp_title;
+ final int message = enrolling ? R.string.fp_power_button_enrollment_message
+ : R.string.fp_power_button_bp_message;
+ final int positiveText = enrolling ? R.string.fp_power_button_enrollment_positive_button
+ : R.string.fp_power_button_bp_positive_button;
+ final int negativeText = enrolling ? R.string.fp_power_button_enrollment_negative_button
+ : R.string.fp_power_button_bp_negative_button;
+
+ final Dialog confirmScreenOffDialog = dialogBuilder
+ .setTitle(title)
+ .setMessage(message)
+ .setPositiveButton(positiveText,
+ (dialog, which) -> {
+ dialog.dismiss();
+ powerManager.goToSleep(
+ eventTime,
+ PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
+ 0 /* flags */
+ );
+ })
+ .setNegativeButton(negativeText, (dialog, which) -> dialog.dismiss())
+ .setOnDismissListener(dismissListener)
+ .setCancelable(false)
+ .create();
+ confirmScreenOffDialog.getWindow().setType(
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL);
+ confirmScreenOffDialog.show();
+
+ return confirmScreenOffDialog;
}
/**
@@ -116,26 +170,44 @@
*/
public void onFingerprintSensorReady() {
final PackageManager pm = mContext.getPackageManager();
- if (!pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) return;
- FingerprintManager fingerprintManager =
+ if (!pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ return;
+ }
+
+ final FingerprintManager fingerprintManager =
mContext.getSystemService(FingerprintManager.class);
fingerprintManager.addAuthenticatorsRegisteredCallback(
new IFingerprintAuthenticatorsRegisteredCallback.Stub() {
@Override
public void onAllAuthenticatorsRegistered(
List<FingerprintSensorPropertiesInternal> sensors) {
- mIsSideFps.set(fingerprintManager.isPowerbuttonFps());
- FingerprintStateListener fingerprintStateListener =
- new FingerprintStateListener() {
- @Override
- public void onStateChanged(
- @FingerprintStateListener.State int newState) {
- mFingerprintState = newState;
- }
- };
- fingerprintManager.registerFingerprintStateListener(
- fingerprintStateListener);
- mSideFpsEventHandlerReady.set(true);
+ if (fingerprintManager.isPowerbuttonFps()) {
+ fingerprintManager.registerFingerprintStateListener(
+ new FingerprintStateListener() {
+ @Nullable private Runnable mStateRunnable = null;
+
+ @Override
+ public void onStateChanged(
+ @FingerprintStateListener.State int newState) {
+ if (mStateRunnable != null) {
+ mHandler.removeCallbacks(mStateRunnable);
+ mStateRunnable = null;
+ }
+
+ // When the user hits the power button the events can
+ // arrive in any order (success auth & power). Add a
+ // damper when moving to idle in case auth is first
+ if (newState == STATE_IDLE) {
+ mStateRunnable = () -> mFingerprintState = newState;
+ mHandler.postDelayed(mStateRunnable,
+ DEBOUNCE_DELAY_MILLIS);
+ } else {
+ mFingerprintState = newState;
+ }
+ }
+ });
+ mSideFpsEventHandlerReady.set(true);
+ }
}
});
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 59b6a08..7e36f89 100755
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -44,6 +44,7 @@
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.media.PlaybackParams;
+import android.media.tv.AitInfo;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.DvbDeviceInfo;
@@ -1761,6 +1762,26 @@
}
@Override
+ public void setIAppNotificationEnabled(IBinder sessionToken, boolean enabled, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "setIAppNotificationEnabled");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId)
+ .setIAppNotificationEnabled(enabled);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slog.e(TAG, "error in setIAppNotificationEnabled", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void sendAppPrivateCommand(IBinder sessionToken, String command, Bundle data,
int userId) {
final int callingUid = Binder.getCallingUid();
@@ -3341,7 +3362,23 @@
}
}
- // For the recording session only
+ @Override
+ public void onAitInfoUpdated(AitInfo aitInfo) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "onAitInfoUpdated(" + aitInfo + ")");
+ }
+ if (mSessionState.session == null || mSessionState.client == null) {
+ return;
+ }
+ try {
+ mSessionState.client.onAitInfoUpdated(aitInfo, mSessionState.seq);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in onAitInfoUpdated", e);
+ }
+ }
+ }
+
@Override
public void onTuned(Uri channelUri) {
synchronized (mLock) {
@@ -3352,7 +3389,7 @@
return;
}
try {
- mSessionState.client.onTuned(mSessionState.seq, channelUri);
+ mSessionState.client.onTuned(channelUri, mSessionState.seq);
} catch (RemoteException e) {
Slog.e(TAG, "error in onTuned", e);
}
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 c7b6421..f12139d 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -28,6 +28,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
+import android.graphics.Rect;
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoResponse;
import android.media.tv.interactive.ITvIAppClient;
@@ -39,6 +40,7 @@
import android.media.tv.interactive.ITvIAppSessionCallback;
import android.media.tv.interactive.TvIAppInfo;
import android.media.tv.interactive.TvIAppService;
+import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.Process;
@@ -522,11 +524,6 @@
removeSessionStateLocked(state.mSessionToken, state.mUserId);
}
- private SessionState getSessionState(IBinder sessionToken) {
- // TODO: implement user state and get session from it.
- return null;
- }
-
private int resolveCallingUserId(int callingPid, int callingUid, int requestedUserId,
String methodName) {
return ActivityManager.handleIncomingUser(callingPid, callingUid, requestedUserId, false,
@@ -570,6 +567,11 @@
}
@GuardedBy("mLock")
+ private ITvIAppSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
+ return getSessionLocked(getSessionStateLocked(sessionToken, callingUid, userId));
+ }
+
+ @GuardedBy("mLock")
private ITvIAppSession getSessionLocked(SessionState sessionState) {
ITvIAppSession session = sessionState.mSession;
if (session == null) {
@@ -601,6 +603,32 @@
}
@Override
+ public void prepare(String tiasId, int type, int userId) {
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
+ Binder.getCallingUid(), userId, "prepare");
+ 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 prepare TIAS - unknown TIAS id " + tiasId);
+ return;
+ }
+ ComponentName componentName = iAppState.mInfo.getComponent();
+ ServiceState serviceState = userState.mServiceStateMap.get(componentName);
+ if (serviceState != null) {
+ serviceState.mService.prepare(type);
+ }
+ }
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in prepare", 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();
@@ -683,17 +711,53 @@
}
@Override
+ public void notifyTuned(IBinder sessionToken, Uri channelUri, int userId) {
+ if (DEBUG) {
+ Slogf.d(TAG, "notifyTuned(sessionToken=" + sessionToken
+ + ", Uri=" + channelUri + ")");
+ }
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "notifyTuned");
+ SessionState sessionState = null;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).notifyTuned(channelUri);
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in notifyTuned", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public void startIApp(IBinder sessionToken, int userId) {
if (DEBUG) {
Slogf.d(TAG, "BinderService#start(userId=%d)", userId);
}
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "notifyTuned");
+ SessionState sessionState = null;
+ final long identity = Binder.clearCallingIdentity();
try {
- SessionState sessionState = getSessionState(sessionToken);
- if (sessionState != null && sessionState.mSession != null) {
- sessionState.mSession.startIApp();
+ synchronized (mLock) {
+ try {
+ sessionState = getSessionStateLocked(sessionToken, callingUid,
+ resolvedUserId);
+ getSessionLocked(sessionState).startIApp();
+ } catch (RemoteException | SessionNotFoundException e) {
+ Slogf.e(TAG, "error in start", e);
+ }
}
- } catch (RemoteException e) {
- Slogf.e(TAG, "error in start", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
@@ -802,6 +866,67 @@
Binder.restoreCallingIdentity(identity);
}
}
+
+ @Override
+ public void createMediaView(IBinder sessionToken, IBinder windowToken, Rect frame,
+ int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "createMediaView");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId)
+ .createMediaView(windowToken, frame);
+ } catch (RemoteException | TvIAppManagerService.SessionNotFoundException e) {
+ Slog.e(TAG, "error in createMediaView", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void relayoutMediaView(IBinder sessionToken, Rect frame, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "relayoutMediaView");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId)
+ .relayoutMediaView(frame);
+ } catch (RemoteException | TvIAppManagerService.SessionNotFoundException e) {
+ Slog.e(TAG, "error in relayoutMediaView", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void removeMediaView(IBinder sessionToken, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "removeMediaView");
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ try {
+ getSessionLocked(sessionToken, callingUid, resolvedUserId)
+ .removeMediaView();
+ } catch (RemoteException | TvIAppManagerService.SessionNotFoundException e) {
+ Slog.e(TAG, "error in removeMediaView", e);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
@GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
index 5c1b5ff..1c675c2 100644
--- a/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
+++ b/services/core/java/com/android/server/vcn/util/PersistableBundleUtils.java
@@ -46,6 +46,7 @@
private static final String PARCEL_UUID_KEY = "PARCEL_UUID";
private static final String BYTE_ARRAY_KEY = "BYTE_ARRAY_KEY";
private static final String INTEGER_KEY = "INTEGER_KEY";
+ private static final String STRING_KEY = "STRING_KEY";
/**
* Functional interface to convert an object of the specified type to a PersistableBundle.
@@ -91,6 +92,21 @@
return bundle.getInt(INTEGER_KEY);
};
+ /** Serializer to convert s String to a PersistableBundle. */
+ public static final Serializer<String> STRING_SERIALIZER =
+ (i) -> {
+ final PersistableBundle result = new PersistableBundle();
+ result.putString(STRING_KEY, i);
+ return result;
+ };
+
+ /** Deserializer to convert a PersistableBundle to a String. */
+ public static final Deserializer<String> STRING_DESERIALIZER =
+ (bundle) -> {
+ Objects.requireNonNull(bundle, "PersistableBundle is null");
+ return bundle.getString(STRING_KEY);
+ };
+
/**
* Converts a ParcelUuid to a PersistableBundle.
*
diff --git a/services/core/java/com/android/server/vibrator/Vibration.java b/services/core/java/com/android/server/vibrator/Vibration.java
index ddac9cd..1d6e158 100644
--- a/services/core/java/com/android/server/vibrator/Vibration.java
+++ b/services/core/java/com/android/server/vibrator/Vibration.java
@@ -53,13 +53,13 @@
IGNORED,
IGNORED_APP_OPS,
IGNORED_BACKGROUND,
- IGNORED_RINGTONE,
IGNORED_UNKNOWN_VIBRATION,
IGNORED_UNSUPPORTED,
IGNORED_FOR_ALARM,
IGNORED_FOR_EXTERNAL,
IGNORED_FOR_ONGOING,
IGNORED_FOR_POWER,
+ IGNORED_FOR_RINGER_MODE,
IGNORED_FOR_SETTINGS,
IGNORED_SUPERSEDED,
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index f82f99d..1ee115d 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -53,12 +53,44 @@
import com.android.server.LocalServices;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/** Controls all the system settings related to vibration. */
final class VibrationSettings {
private static final String TAG = "VibrationSettings";
+ /**
+ * Set of usages allowed for vibrations from background processes.
+ *
+ * <p>Some examples are notification, ringtone or alarm vibrations, that are allowed to vibrate
+ * unexpectedly as they are meant to grab the user's attention. Hardware feedback and physical
+ * emulation are also supported, as the trigger process might still be in the background when
+ * the user interaction wakes the device.
+ */
+ private static final Set<Integer> BACKGROUND_PROCESS_USAGE_ALLOWLIST = new HashSet<>(
+ Arrays.asList(
+ USAGE_RINGTONE,
+ USAGE_ALARM,
+ USAGE_NOTIFICATION,
+ USAGE_COMMUNICATION_REQUEST,
+ USAGE_HARDWARE_FEEDBACK,
+ USAGE_PHYSICAL_EMULATION));
+
+ /**
+ * Set of usages allowed for vibrations in battery saver mode (low power).
+ *
+ * <p>Some examples are ringtone or alarm vibrations, that have high priority and should vibrate
+ * even when the device is saving battery.
+ */
+ private static final Set<Integer> BATTERY_SAVER_USAGE_ALLOWLIST = new HashSet<>(
+ Arrays.asList(
+ USAGE_RINGTONE,
+ USAGE_ALARM,
+ USAGE_COMMUNICATION_REQUEST));
+
/** Listener for changes on vibration settings. */
interface OnVibratorSettingsChanged {
/** Callback triggered when any of the vibrator settings change. */
@@ -94,8 +126,6 @@
@GuardedBy("mLock")
private boolean mApplyRampingRinger;
@GuardedBy("mLock")
- private int mZenMode;
- @GuardedBy("mLock")
private int mHapticFeedbackIntensity;
@GuardedBy("mLock")
private int mHardwareFeedbackIntensity;
@@ -104,7 +134,7 @@
@GuardedBy("mLock")
private int mRingIntensity;
@GuardedBy("mLock")
- private boolean mLowPowerMode;
+ private boolean mBatterySaverMode;
VibrationSettings(Context context, Handler handler) {
this(context, handler,
@@ -172,8 +202,8 @@
public void onLowPowerModeChanged(PowerSaveState result) {
boolean shouldNotifyListeners;
synchronized (mLock) {
- shouldNotifyListeners = result.batterySaverEnabled != mLowPowerMode;
- mLowPowerMode = result.batterySaverEnabled;
+ shouldNotifyListeners = result.batterySaverEnabled != mBatterySaverMode;
+ mBatterySaverMode = result.batterySaverEnabled;
}
if (shouldNotifyListeners) {
notifyListeners();
@@ -187,7 +217,6 @@
registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES));
registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING));
registerSettingsObserver(Settings.System.getUriFor(Settings.System.APPLY_RAMPING_RINGER));
- registerSettingsObserver(Settings.Global.getUriFor(Settings.Global.ZEN_MODE));
registerSettingsObserver(
Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY));
registerSettingsObserver(
@@ -299,71 +328,78 @@
return mFallbackEffects.get(effectId);
}
- /**
- * Return {@code true} if the device should vibrate for current ringer mode.
- *
- * <p>This checks the current {@link AudioManager#getRingerModeInternal()} against user settings
- * for ringtone usage only. All other usages are allowed independently of ringer mode.
- */
- public boolean shouldVibrateForRingerMode(int usageHint) {
- if (usageHint != USAGE_RINGTONE) {
- return true;
- }
- synchronized (mLock) {
- if (mAudioManager == null) {
- return false;
- }
- int ringerMode = mAudioManager.getRingerModeInternal();
- if (mVibrateWhenRinging) {
- return ringerMode != AudioManager.RINGER_MODE_SILENT;
- } else if (mApplyRampingRinger) {
- return ringerMode != AudioManager.RINGER_MODE_SILENT;
- } else {
- return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
- }
- }
- }
-
- /**
- * Returns {@code true} if this vibration is allowed for given {@code uid}.
- *
- * <p>This checks if the user is aware of this foreground process, or if the vibration usage is
- * allowed to play in the background (i.e. it's a notification, ringtone or alarm vibration).
- */
- public boolean shouldVibrateForUid(int uid, int usageHint) {
- return mUidObserver.isUidForeground(uid) || isClassAlarm(usageHint);
- }
-
- /**
- * Returns {@code true} if this vibration is allowed for current power mode state.
- *
- * <p>This checks if the device is in battery saver mode, in which case only alarm, ringtone and
- * {@link VibrationAttributes#USAGE_COMMUNICATION_REQUEST} usages are allowed to vibrate.
- */
- public boolean shouldVibrateForPowerMode(int usageHint) {
- synchronized (mLock) {
- return !mLowPowerMode || usageHint == USAGE_RINGTONE || usageHint == USAGE_ALARM
- || usageHint == USAGE_COMMUNICATION_REQUEST;
- }
- }
-
/** Return {@code true} if input devices should vibrate instead of this device. */
public boolean shouldVibrateInputDevices() {
return mVibrateInputDevices;
}
- /** Return {@code true} if setting for {@link Settings.Global#ZEN_MODE} is not OFF. */
- public boolean isInZenMode() {
- return mZenMode != Settings.Global.ZEN_MODE_OFF;
+ /**
+ * Check if given vibration should be ignored by the service.
+ *
+ * @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored,
+ * null otherwise.
+ */
+ @Nullable
+ public Vibration.Status shouldIgnoreVibration(int uid, VibrationAttributes attrs) {
+ final int usage = attrs.getUsage();
+ synchronized (mLock) {
+ if (!mUidObserver.isUidForeground(uid)
+ && !BACKGROUND_PROCESS_USAGE_ALLOWLIST.contains(usage)) {
+ return Vibration.Status.IGNORED_BACKGROUND;
+ }
+
+ if (mBatterySaverMode && !BATTERY_SAVER_USAGE_ALLOWLIST.contains(usage)) {
+ return Vibration.Status.IGNORED_FOR_POWER;
+ }
+
+ int intensity = getCurrentIntensity(usage);
+ if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
+ return Vibration.Status.IGNORED_FOR_SETTINGS;
+ }
+
+ if (!shouldVibrateForRingerModeLocked(usage)) {
+ return Vibration.Status.IGNORED_FOR_RINGER_MODE;
+ }
+ }
+ return null;
}
- private static boolean isClassAlarm(int usageHint) {
- return (usageHint & VibrationAttributes.USAGE_CLASS_MASK)
- == VibrationAttributes.USAGE_CLASS_ALARM;
+ /**
+ * Return {@code true} if the device should vibrate for current ringer mode.
+ *
+ * <p>This checks the current {@link AudioManager#getRingerModeInternal()} against user settings
+ * for touch and ringtone usages only. All other usages are allowed by this method.
+ */
+ @GuardedBy("mLock")
+ private boolean shouldVibrateForRingerModeLocked(int usageHint) {
+ // If audio manager was not loaded yet then assume most restrictive mode.
+ int ringerMode = (mAudioManager == null)
+ ? AudioManager.RINGER_MODE_SILENT
+ : mAudioManager.getRingerModeInternal();
+
+ switch (usageHint) {
+ case USAGE_TOUCH:
+ // Touch feedback disabled when phone is on silent mode.
+ return ringerMode != AudioManager.RINGER_MODE_SILENT;
+ case USAGE_RINGTONE:
+ switch (ringerMode) {
+ case AudioManager.RINGER_MODE_SILENT:
+ return false;
+ case AudioManager.RINGER_MODE_VIBRATE:
+ return true;
+ default:
+ // Ringtone vibrations also depend on 2 other settings:
+ return mVibrateWhenRinging || mApplyRampingRinger;
+ }
+ default:
+ // All other usages ignore ringer mode settings.
+ return true;
+ }
}
/** Updates all vibration settings and triggers registered listeners. */
- public void updateSettings() {
+ @VisibleForTesting
+ void updateSettings() {
synchronized (mLock) {
mVibrateWhenRinging = getSystemSetting(Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
mApplyRampingRinger = getSystemSetting(Settings.System.APPLY_RAMPING_RINGER, 0) != 0;
@@ -378,7 +414,6 @@
mRingIntensity = getSystemSetting(Settings.System.RING_VIBRATION_INTENSITY,
getDefaultIntensity(USAGE_RINGTONE));
mVibrateInputDevices = getSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
- mZenMode = getGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
}
notifyListeners();
}
@@ -399,31 +434,33 @@
@Override
public String toString() {
- return "VibrationSettings{"
- + "mVibrateInputDevices=" + mVibrateInputDevices
- + ", mVibrateWhenRinging=" + mVibrateWhenRinging
- + ", mApplyRampingRinger=" + mApplyRampingRinger
- + ", mLowPowerMode=" + mLowPowerMode
- + ", mZenMode=" + Settings.Global.zenModeToString(mZenMode)
- + ", mProcStatesCache=" + mUidObserver.mProcStatesCache
- + ", mHapticChannelMaxVibrationAmplitude=" + getHapticChannelMaxVibrationAmplitude()
- + ", mRampStepDuration=" + mRampStepDuration
- + ", mRampDownDuration=" + mRampDownDuration
- + ", mHardwareHapticFeedbackIntensity="
- + intensityToString(getCurrentIntensity(USAGE_HARDWARE_FEEDBACK))
- + ", mHapticFeedbackIntensity="
- + intensityToString(getCurrentIntensity(USAGE_TOUCH))
- + ", mHapticFeedbackDefaultIntensity="
- + intensityToString(getDefaultIntensity(USAGE_TOUCH))
- + ", mNotificationIntensity="
- + intensityToString(getCurrentIntensity(USAGE_NOTIFICATION))
- + ", mNotificationDefaultIntensity="
- + intensityToString(getDefaultIntensity(USAGE_NOTIFICATION))
- + ", mRingIntensity="
- + intensityToString(getCurrentIntensity(USAGE_RINGTONE))
- + ", mRingDefaultIntensity="
- + intensityToString(getDefaultIntensity(USAGE_RINGTONE))
- + '}';
+ synchronized (mLock) {
+ return "VibrationSettings{"
+ + "mVibrateInputDevices=" + mVibrateInputDevices
+ + ", mVibrateWhenRinging=" + mVibrateWhenRinging
+ + ", mApplyRampingRinger=" + mApplyRampingRinger
+ + ", mBatterySaverMode=" + mBatterySaverMode
+ + ", mProcStatesCache=" + mUidObserver.mProcStatesCache
+ + ", mHapticChannelMaxVibrationAmplitude="
+ + getHapticChannelMaxVibrationAmplitude()
+ + ", mRampStepDuration=" + mRampStepDuration
+ + ", mRampDownDuration=" + mRampDownDuration
+ + ", mHardwareHapticFeedbackIntensity="
+ + intensityToString(getCurrentIntensity(USAGE_HARDWARE_FEEDBACK))
+ + ", mHapticFeedbackIntensity="
+ + intensityToString(getCurrentIntensity(USAGE_TOUCH))
+ + ", mHapticFeedbackDefaultIntensity="
+ + intensityToString(getDefaultIntensity(USAGE_TOUCH))
+ + ", mNotificationIntensity="
+ + intensityToString(getCurrentIntensity(USAGE_NOTIFICATION))
+ + ", mNotificationDefaultIntensity="
+ + intensityToString(getDefaultIntensity(USAGE_NOTIFICATION))
+ + ", mRingIntensity="
+ + intensityToString(getCurrentIntensity(USAGE_RINGTONE))
+ + ", mRingDefaultIntensity="
+ + intensityToString(getDefaultIntensity(USAGE_RINGTONE))
+ + '}';
+ }
}
/** Write current settings into given {@link ProtoOutputStream}. */
@@ -480,10 +517,6 @@
settingName, defaultValue, UserHandle.USER_CURRENT);
}
- private int getGlobalSetting(String settingName, int defaultValue) {
- return Settings.Global.getInt(mContext.getContentResolver(), settingName, defaultValue);
- }
-
private void registerSettingsObserver(Uri settingUri) {
mContext.getContentResolver().registerContentObserver(
settingUri, /* notifyForDescendants= */ true, mSettingObserver,
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 9717201..478e86e 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -46,7 +46,6 @@
import android.os.Trace;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
-import android.os.Vibrator;
import android.os.VibratorInfo;
import android.os.vibrator.PrebakedSegment;
import android.os.vibrator.VibrationEffectSegment;
@@ -394,13 +393,13 @@
if (DEBUG) {
Slog.d(TAG, "Starting vibrate for vibration " + vib.id);
}
- Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(vib);
- if (ignoreStatus != null) {
- endVibrationLocked(vib, ignoreStatus);
- return vib;
+ Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
+ vib.uid, vib.opPkg, vib.attrs);
+
+ if (ignoreStatus == null) {
+ ignoreStatus = shouldIgnoreVibrationForOngoingLocked(vib);
}
- ignoreStatus = shouldIgnoreVibrationForCurrentLocked(vib);
if (ignoreStatus != null) {
endVibrationLocked(vib, ignoreStatus);
return vib;
@@ -453,8 +452,7 @@
&& shouldCancelVibration(
mCurrentExternalVibration.externalVibration.getVibrationAttributes(),
usageFilter)) {
- mCurrentExternalVibration.end(Vibration.Status.CANCELLED);
- mVibratorManagerRecords.record(mCurrentExternalVibration);
+ endVibrationLocked(mCurrentExternalVibration, Vibration.Status.CANCELLED);
mCurrentExternalVibration.externalVibration.mute();
mCurrentExternalVibration = null;
setExternalControl(false);
@@ -482,15 +480,76 @@
}
try {
if (isDumpProto) {
- mVibratorManagerRecords.dumpProto(fd);
+ dumpProto(fd);
} else {
- mVibratorManagerRecords.dumpText(pw);
+ dumpText(pw);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
+ private void dumpText(PrintWriter pw) {
+ if (DEBUG) {
+ Slog.d(TAG, "Dumping vibrator manager service to text...");
+ }
+ synchronized (mLock) {
+ pw.println("Vibrator Manager Service:");
+ pw.println(" mVibrationSettings:");
+ pw.println(" " + mVibrationSettings);
+ pw.println();
+ pw.println(" mVibratorControllers:");
+ for (int i = 0; i < mVibrators.size(); i++) {
+ pw.println(" " + mVibrators.valueAt(i));
+ }
+ pw.println();
+ pw.println(" mCurrentVibration:");
+ pw.println(" " + (mCurrentVibration == null
+ ? null : mCurrentVibration.getVibration().getDebugInfo()));
+ pw.println();
+ pw.println(" mNextVibration:");
+ pw.println(" " + (mNextVibration == null
+ ? null : mNextVibration.getVibration().getDebugInfo()));
+ pw.println();
+ pw.println(" mCurrentExternalVibration:");
+ pw.println(" " + (mCurrentExternalVibration == null
+ ? null : mCurrentExternalVibration.getDebugInfo()));
+ pw.println();
+ }
+ mVibratorManagerRecords.dumpText(pw);
+ }
+
+ synchronized void dumpProto(FileDescriptor fd) {
+ final ProtoOutputStream proto = new ProtoOutputStream(fd);
+ if (DEBUG) {
+ Slog.d(TAG, "Dumping vibrator manager service to proto...");
+ }
+ synchronized (mLock) {
+ mVibrationSettings.dumpProto(proto);
+ if (mCurrentVibration != null) {
+ mCurrentVibration.getVibration().getDebugInfo().dumpProto(proto,
+ VibratorManagerServiceDumpProto.CURRENT_VIBRATION);
+ }
+ if (mCurrentExternalVibration != null) {
+ mCurrentExternalVibration.getDebugInfo().dumpProto(proto,
+ VibratorManagerServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
+ }
+
+ boolean isVibrating = false;
+ boolean isUnderExternalControl = false;
+ for (int i = 0; i < mVibrators.size(); i++) {
+ proto.write(VibratorManagerServiceDumpProto.VIBRATOR_IDS, mVibrators.keyAt(i));
+ isVibrating |= mVibrators.valueAt(i).isVibrating();
+ isUnderExternalControl |= mVibrators.valueAt(i).isUnderExternalControl();
+ }
+ proto.write(VibratorManagerServiceDumpProto.IS_VIBRATING, isVibrating);
+ proto.write(VibratorManagerServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
+ isUnderExternalControl);
+ }
+ mVibratorManagerRecords.dumpProto(proto);
+ proto.flush();
+ }
+
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback cb, ResultReceiver resultReceiver) {
@@ -515,8 +574,15 @@
return;
}
- if (inputDevicesChanged || !mVibrationSettings.shouldVibrateForPowerMode(
- mCurrentVibration.getVibration().attrs.getUsage())) {
+ Vibration vib = mCurrentVibration.getVibration();
+ Vibration.Status ignoreStatus = shouldIgnoreVibrationLocked(
+ vib.uid, vib.opPkg, vib.attrs);
+
+ if (inputDevicesChanged || (ignoreStatus != null)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Canceling vibration because settings changed: "
+ + (inputDevicesChanged ? "input devices changed" : ignoreStatus));
+ }
mCurrentVibration.cancel();
}
}
@@ -602,15 +668,56 @@
@GuardedBy("mLock")
private void endVibrationLocked(Vibration vib, Vibration.Status status) {
vib.end(status);
+ logVibrationStatus(vib.uid, vib.attrs, status);
mVibratorManagerRecords.record(vib);
}
@GuardedBy("mLock")
private void endVibrationLocked(ExternalVibrationHolder vib, Vibration.Status status) {
vib.end(status);
+ logVibrationStatus(vib.externalVibration.getUid(),
+ vib.externalVibration.getVibrationAttributes(), status);
mVibratorManagerRecords.record(vib);
}
+ private void logVibrationStatus(int uid, VibrationAttributes attrs, Vibration.Status status) {
+ switch (status) {
+ case IGNORED_BACKGROUND:
+ Slog.e(TAG, "Ignoring incoming vibration as process with"
+ + " uid= " + uid + " is background," + " attrs= " + attrs);
+ break;
+ case IGNORED_ERROR_APP_OPS:
+ Slog.w(TAG, "Would be an error: vibrate from uid " + uid);
+ break;
+ case IGNORED_FOR_ALARM:
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
+ }
+ break;
+ case IGNORED_FOR_EXTERNAL:
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
+ }
+ break;
+ case IGNORED_FOR_ONGOING:
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring incoming vibration in favor of repeating vibration");
+ }
+ break;
+ case IGNORED_FOR_RINGER_MODE:
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring incoming vibration because of ringer mode, attrs="
+ + attrs);
+ }
+ break;
+ default:
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration for uid=" + uid + " and with attrs=" + attrs
+ + " ended with status " + status);
+ }
+ }
+ }
+
@GuardedBy("mLock")
private void reportFinishedVibrationLocked(Vibration.Status status) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
@@ -651,32 +758,36 @@
}
/**
- * Check if given vibration should be ignored in favour of one of the vibrations currently
- * running on the same vibrators.
+ * Check if given vibration should be ignored by this service because of the ongoing vibration.
*
- * @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored.
+ * @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored, null
+ * otherwise.
*/
@GuardedBy("mLock")
@Nullable
- private Vibration.Status shouldIgnoreVibrationForCurrentLocked(Vibration vibration) {
- if (vibration.isRepeating()) {
- // Repeating vibrations always take precedence.
+ private Vibration.Status shouldIgnoreVibrationForOngoingLocked(Vibration vib) {
+ if (mCurrentExternalVibration != null) {
+ // If something has external control of the vibrator, assume that it's more important.
+ return Vibration.Status.IGNORED_FOR_EXTERNAL;
+ }
+
+ if (mCurrentVibration == null || vib.isRepeating()) {
+ // Incoming repeating vibrations always take precedence over ongoing vibrations.
return null;
}
- if (mCurrentVibration != null && !mCurrentVibration.getVibration().hasEnded()) {
- if (mCurrentVibration.getVibration().attrs.getUsage()
- == VibrationAttributes.USAGE_ALARM) {
- if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
- }
- return Vibration.Status.IGNORED_FOR_ALARM;
- }
- if (mCurrentVibration.getVibration().isRepeating()) {
- if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration in favor of repeating vibration");
- }
- return Vibration.Status.IGNORED_FOR_ONGOING;
- }
+
+ Vibration currentVibration = mCurrentVibration.getVibration();
+ if (currentVibration.hasEnded()) {
+ // Current vibration is finishing up, it should not block incoming vibrations.
+ return null;
+ }
+
+ if (currentVibration.attrs.getUsage() == VibrationAttributes.USAGE_ALARM) {
+ return Vibration.Status.IGNORED_FOR_ALARM;
+ }
+
+ if (currentVibration.isRepeating()) {
+ return Vibration.Status.IGNORED_FOR_ONGOING;
}
return null;
}
@@ -684,57 +795,16 @@
/**
* Check if given vibration should be ignored by this service.
*
- * @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored.
- * @see #shouldIgnoreVibrationLocked(int, String, VibrationAttributes)
- */
- @GuardedBy("mLock")
- @Nullable
- private Vibration.Status shouldIgnoreVibrationLocked(Vibration vib) {
- // If something has external control of the vibrator, assume that it's more important.
- if (mCurrentExternalVibration != null) {
- if (DEBUG) {
- Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
- }
- return Vibration.Status.IGNORED_FOR_EXTERNAL;
- }
-
- if (!mVibrationSettings.shouldVibrateForUid(vib.uid, vib.attrs.getUsage())) {
- Slog.e(TAG, "Ignoring incoming vibration as process with"
- + " uid= " + vib.uid + " is background,"
- + " attrs= " + vib.attrs);
- return Vibration.Status.IGNORED_BACKGROUND;
- }
-
- return shouldIgnoreVibrationLocked(vib.uid, vib.opPkg, vib.attrs);
- }
-
- /**
- * Check if a vibration with given {@code uid}, {@code opPkg} and {@code attrs} should be
- * ignored by this service.
- *
- * @param uid The user id of this vibration
- * @param opPkg The package name of this vibration
- * @param attrs The attributes of this vibration
- * @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored.
+ * @return One of Vibration.Status.IGNORED_* values if the vibration should be ignored, null
+ * otherwise.
*/
@GuardedBy("mLock")
@Nullable
private Vibration.Status shouldIgnoreVibrationLocked(int uid, String opPkg,
VibrationAttributes attrs) {
- if (!mVibrationSettings.shouldVibrateForPowerMode(attrs.getUsage())) {
- return Vibration.Status.IGNORED_FOR_POWER;
- }
-
- int intensity = mVibrationSettings.getCurrentIntensity(attrs.getUsage());
- if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
- return Vibration.Status.IGNORED_FOR_SETTINGS;
- }
-
- if (!mVibrationSettings.shouldVibrateForRingerMode(attrs.getUsage())) {
- if (DEBUG) {
- Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
- }
- return Vibration.Status.IGNORED_RINGTONE;
+ Vibration.Status statusFromSettings = mVibrationSettings.shouldIgnoreVibration(uid, attrs);
+ if (statusFromSettings != null) {
+ return statusFromSettings;
}
int mode = checkAppOpModeLocked(uid, opPkg, attrs);
@@ -742,7 +812,6 @@
if (mode == AppOpsManager.MODE_ERRORED) {
// We might be getting calls from within system_server, so we don't actually
// want to throw a SecurityException here.
- Slog.w(TAG, "Would be an error: vibrate from uid " + uid);
return Vibration.Status.IGNORED_ERROR_APP_OPS;
} else {
return Vibration.Status.IGNORED_APP_OPS;
@@ -1236,21 +1305,18 @@
}
/** Keep records of vibrations played and provide debug information for this service. */
- private final class VibratorManagerRecords {
- @GuardedBy("mLock")
+ private static final class VibratorManagerRecords {
private final SparseArray<LinkedList<Vibration.DebugInfo>> mPreviousVibrations =
new SparseArray<>();
- @GuardedBy("mLock")
private final LinkedList<Vibration.DebugInfo> mPreviousExternalVibrations =
new LinkedList<>();
private final int mPreviousVibrationsLimit;
- private VibratorManagerRecords(int limit) {
+ VibratorManagerRecords(int limit) {
mPreviousVibrationsLimit = limit;
}
- @GuardedBy("mLock")
- void record(Vibration vib) {
+ synchronized void record(Vibration vib) {
int usage = vib.attrs.getUsage();
if (!mPreviousVibrations.contains(usage)) {
mPreviousVibrations.put(usage, new LinkedList<>());
@@ -1258,122 +1324,67 @@
record(mPreviousVibrations.get(usage), vib.getDebugInfo());
}
- @GuardedBy("mLock")
- void record(ExternalVibrationHolder vib) {
+ synchronized void record(ExternalVibrationHolder vib) {
record(mPreviousExternalVibrations, vib.getDebugInfo());
}
- @GuardedBy("mLock")
- void record(LinkedList<Vibration.DebugInfo> records, Vibration.DebugInfo info) {
+ synchronized void record(LinkedList<Vibration.DebugInfo> records,
+ Vibration.DebugInfo info) {
if (records.size() > mPreviousVibrationsLimit) {
records.removeFirst();
}
records.addLast(info);
}
- void dumpText(PrintWriter pw) {
- pw.println("Vibrator Manager Service:");
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "Dumping vibrator manager service to text...");
- }
- pw.println(" mVibrationSettings:");
- pw.println(" " + mVibrationSettings);
+ synchronized void dumpText(PrintWriter pw) {
+ for (int i = 0; i < mPreviousVibrations.size(); i++) {
pw.println();
- pw.println(" mVibratorControllers:");
- for (int i = 0; i < mVibrators.size(); i++) {
- pw.println(" " + mVibrators.valueAt(i));
- }
- pw.println();
- pw.println(" mCurrentVibration:");
- pw.println(" " + (mCurrentVibration == null
- ? null : mCurrentVibration.getVibration().getDebugInfo()));
- pw.println();
- pw.println(" mNextVibration:");
- pw.println(" " + (mNextVibration == null
- ? null : mNextVibration.getVibration().getDebugInfo()));
- pw.println();
- pw.println(" mCurrentExternalVibration:");
- pw.println(" " + (mCurrentExternalVibration == null
- ? null : mCurrentExternalVibration.getDebugInfo()));
- pw.println();
- for (int i = 0; i < mPreviousVibrations.size(); i++) {
- pw.println();
- pw.print(" Previous vibrations for usage ");
- pw.print(VibrationAttributes.usageToString(mPreviousVibrations.keyAt(i)));
- pw.println(":");
- for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) {
- pw.println(" " + info);
- }
- }
-
- pw.println();
- pw.println(" Previous external vibrations:");
- for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
+ pw.print(" Previous vibrations for usage ");
+ pw.print(VibrationAttributes.usageToString(mPreviousVibrations.keyAt(i)));
+ pw.println(":");
+ for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) {
pw.println(" " + info);
}
}
+
+ pw.println();
+ pw.println(" Previous external vibrations:");
+ for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
+ pw.println(" " + info);
+ }
}
- synchronized void dumpProto(FileDescriptor fd) {
- final ProtoOutputStream proto = new ProtoOutputStream(fd);
-
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "Dumping vibrator manager service to proto...");
+ synchronized void dumpProto(ProtoOutputStream proto) {
+ for (int i = 0; i < mPreviousVibrations.size(); i++) {
+ long fieldId;
+ switch (mPreviousVibrations.keyAt(i)) {
+ case VibrationAttributes.USAGE_RINGTONE:
+ fieldId = VibratorManagerServiceDumpProto.PREVIOUS_RING_VIBRATIONS;
+ break;
+ case VibrationAttributes.USAGE_NOTIFICATION:
+ fieldId = VibratorManagerServiceDumpProto
+ .PREVIOUS_NOTIFICATION_VIBRATIONS;
+ break;
+ case VibrationAttributes.USAGE_ALARM:
+ fieldId = VibratorManagerServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS;
+ break;
+ default:
+ fieldId = VibratorManagerServiceDumpProto.PREVIOUS_VIBRATIONS;
}
- mVibrationSettings.dumpProto(proto);
- if (mCurrentVibration != null) {
- mCurrentVibration.getVibration().getDebugInfo().dumpProto(proto,
- VibratorManagerServiceDumpProto.CURRENT_VIBRATION);
- }
- if (mCurrentExternalVibration != null) {
- mCurrentExternalVibration.getDebugInfo().dumpProto(proto,
- VibratorManagerServiceDumpProto.CURRENT_EXTERNAL_VIBRATION);
- }
-
- boolean isVibrating = false;
- boolean isUnderExternalControl = false;
- for (int i = 0; i < mVibrators.size(); i++) {
- proto.write(VibratorManagerServiceDumpProto.VIBRATOR_IDS, mVibrators.keyAt(i));
- isVibrating |= mVibrators.valueAt(i).isVibrating();
- isUnderExternalControl |= mVibrators.valueAt(i).isUnderExternalControl();
- }
- proto.write(VibratorManagerServiceDumpProto.IS_VIBRATING, isVibrating);
- proto.write(VibratorManagerServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL,
- isUnderExternalControl);
-
- for (int i = 0; i < mPreviousVibrations.size(); i++) {
- long fieldId;
- switch (mPreviousVibrations.keyAt(i)) {
- case VibrationAttributes.USAGE_RINGTONE:
- fieldId = VibratorManagerServiceDumpProto.PREVIOUS_RING_VIBRATIONS;
- break;
- case VibrationAttributes.USAGE_NOTIFICATION:
- fieldId = VibratorManagerServiceDumpProto
- .PREVIOUS_NOTIFICATION_VIBRATIONS;
- break;
- case VibrationAttributes.USAGE_ALARM:
- fieldId = VibratorManagerServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS;
- break;
- default:
- fieldId = VibratorManagerServiceDumpProto.PREVIOUS_VIBRATIONS;
- }
- for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) {
- info.dumpProto(proto, fieldId);
- }
- }
-
- for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
- info.dumpProto(proto,
- VibratorManagerServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
+ for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) {
+ info.dumpProto(proto, fieldId);
}
}
- proto.flush();
+
+ for (Vibration.DebugInfo info : mPreviousExternalVibrations) {
+ info.dumpProto(proto,
+ VibratorManagerServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS);
+ }
}
}
/** Clears mNextVibration if set, ending it cleanly */
+ @GuardedBy("mLock")
private void clearNextVibrationLocked(Vibration.Status endStatus) {
if (mNextVibration != null) {
endVibrationLocked(mNextVibration.getVibration(), endStatus);
@@ -1482,6 +1493,7 @@
}
}
+ @GuardedBy("mLock")
private void stopExternalVibrateLocked(Vibration.Status status) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "stopExternalVibrateLocked");
try {
diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
index 00e1f55..d736ede 100644
--- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
+++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.ActivityOptions;
+import android.app.TaskInfo;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ResolveInfo;
@@ -40,6 +41,12 @@
public abstract @Nullable Intent intercept(ActivityInterceptorInfo info);
/**
+ * Called when an activity is successfully launched.
+ */
+ public void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) {
+ }
+
+ /**
* The unique id of each interceptor which determines the order it will execute in.
*/
@IntDef(suffix = { "_ORDERED_ID" }, value = {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 100c44b..2b28478 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -830,6 +830,8 @@
// SystemUi sets the pinned mode on activity after transition is done.
boolean mWaitForEnteringPinnedMode;
+ private final ActivityRecordInputSink mActivityRecordInputSink;
+
private final Runnable mPauseTimeoutRunnable = new Runnable() {
@Override
public void run() {
@@ -1046,6 +1048,8 @@
pw.print(" forceNewConfig="); pw.println(forceNewConfig);
pw.print(prefix); pw.print("mActivityType=");
pw.println(activityTypeToString(getActivityType()));
+ pw.print(prefix); pw.print("mImeInsetsFrozenUntilStartInput=");
+ pw.println(mImeInsetsFrozenUntilStartInput);
if (requestedVrComponent != null) {
pw.print(prefix);
pw.print("requestedVrComponent=");
@@ -1785,6 +1789,8 @@
createTime = _createTime;
}
mAtmService.mPackageConfigPersister.updateConfigIfNeeded(this, mUserId, packageName);
+
+ mActivityRecordInputSink = new ActivityRecordInputSink(this);
}
/**
@@ -6771,6 +6777,10 @@
} else if (!show && mLastSurfaceShowing) {
getSyncTransaction().hide(mSurfaceControl);
}
+ if (show) {
+ mActivityRecordInputSink.applyChangesToSurfaceIfChanged(
+ getSyncTransaction(), mSurfaceControl);
+ }
}
if (mThumbnail != null) {
mThumbnail.setShowing(getPendingTransaction(), show);
@@ -7996,7 +8006,8 @@
if (mVisibleRequested) {
// It may toggle the UI for user to restart the size compatibility mode activity.
display.handleActivitySizeCompatModeIfNeeded(this);
- } else if (mCompatDisplayInsets != null && !visibleIgnoringKeyguard) {
+ } else if (mCompatDisplayInsets != null && !visibleIgnoringKeyguard
+ && (app == null || !app.hasVisibleActivities())) {
// visibleIgnoringKeyguard is checked to avoid clearing mCompatDisplayInsets during
// displays change. Displays are turned off during the change so mVisibleRequested
// can be false.
diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
new file mode 100644
index 0000000..b183281
--- /dev/null
+++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java
@@ -0,0 +1,171 @@
+/*
+ * 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.wm;
+
+import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
+import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
+
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
+import android.os.IBinder;
+import android.os.InputConstants;
+import android.os.Looper;
+import android.util.Slog;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.InputWindowHandle;
+import android.view.MotionEvent;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.widget.Toast;
+
+/**
+ * Creates a InputWindowHandle that catches all touches that would otherwise pass through an
+ * Activity.
+ */
+class ActivityRecordInputSink {
+
+ /**
+ * Feature flag for making Activities consume all touches within their task bounds.
+ */
+ @ChangeId
+ @Disabled
+ static final long ENABLE_TOUCH_OPAQUE_ACTIVITIES = 194480991L;
+
+ private static final String TAG = "ActivityRecordInputSink";
+ private static final int NUMBER_OF_TOUCHES_TO_DISABLE = 3;
+ private static final long TOAST_COOL_DOWN_MILLIS = 3000L;
+
+ private final ActivityRecord mActivityRecord;
+ private final boolean mIsCompatEnabled;
+
+ // Hold on to InputEventReceiver to prevent it from getting GCd.
+ private InputEventReceiver mInputEventReceiver;
+ private InputWindowHandleWrapper mInputWindowHandleWrapper;
+ private final String mName = Integer.toHexString(System.identityHashCode(this))
+ + " ActivityRecordInputSink";
+ private int mRapidTouchCount = 0;
+ private IBinder mToken;
+ private boolean mDisabled = false;
+
+ ActivityRecordInputSink(ActivityRecord activityRecord) {
+ mActivityRecord = activityRecord;
+ mIsCompatEnabled = CompatChanges.isChangeEnabled(ENABLE_TOUCH_OPAQUE_ACTIVITIES,
+ mActivityRecord.getUid());
+ }
+
+ public void applyChangesToSurfaceIfChanged(
+ SurfaceControl.Transaction transaction, SurfaceControl surfaceControl) {
+ InputWindowHandleWrapper inputWindowHandleWrapper = getInputWindowHandleWrapper();
+ if (inputWindowHandleWrapper.isChanged()) {
+ inputWindowHandleWrapper.applyChangesToSurface(transaction, surfaceControl);
+ }
+ }
+
+ private InputWindowHandleWrapper getInputWindowHandleWrapper() {
+ if (mInputWindowHandleWrapper == null) {
+ mInputWindowHandleWrapper = new InputWindowHandleWrapper(createInputWindowHandle());
+ InputChannel inputChannel =
+ mActivityRecord.mWmService.mInputManager.createInputChannel(mName);
+ mToken = inputChannel.getToken();
+ mInputEventReceiver = createInputEventReceiver(inputChannel);
+ }
+ if (mDisabled || !mIsCompatEnabled || mActivityRecord.isAnimating(TRANSITION | PARENTS,
+ ANIMATION_TYPE_APP_TRANSITION)) {
+ // TODO(b/208662670): Investigate if we can have feature active during animations.
+ mInputWindowHandleWrapper.setToken(null);
+ } else if (mActivityRecord.mStartingData != null) {
+ // TODO(b/208659130): Remove this special case
+ // Don't block touches during splash screen. This is done to not show toasts for
+ // touches passing through splash screens. b/171772640
+ mInputWindowHandleWrapper.setToken(null);
+ } else {
+ mInputWindowHandleWrapper.setToken(mToken);
+ }
+ return mInputWindowHandleWrapper;
+ }
+
+ private InputWindowHandle createInputWindowHandle() {
+ InputWindowHandle inputWindowHandle = new InputWindowHandle(
+ mActivityRecord.getInputApplicationHandle(false),
+ mActivityRecord.getDisplayId());
+ inputWindowHandle.replaceTouchableRegionWithCrop(
+ mActivityRecord.getParentSurfaceControl());
+ inputWindowHandle.name = mName;
+ inputWindowHandle.ownerUid = mActivityRecord.getUid();
+ inputWindowHandle.ownerPid = mActivityRecord.getPid();
+ inputWindowHandle.layoutParamsFlags =
+ WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+ inputWindowHandle.dispatchingTimeoutMillis =
+ InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+ return inputWindowHandle;
+ }
+
+ private InputEventReceiver createInputEventReceiver(InputChannel inputChannel) {
+ return new SinkInputEventReceiver(inputChannel,
+ mActivityRecord.mAtmService.mUiHandler.getLooper());
+ }
+
+ private void showAsToastAndLog(String message) {
+ Toast.makeText(mActivityRecord.mAtmService.mUiContext, message,
+ Toast.LENGTH_LONG).show();
+ Slog.wtf(TAG, message + " " + mActivityRecord.mActivityComponent);
+ }
+
+ private class SinkInputEventReceiver extends InputEventReceiver {
+ private long mLastToast = 0;
+
+ SinkInputEventReceiver(InputChannel inputChannel, Looper looper) {
+ super(inputChannel, looper);
+ }
+
+ public void onInputEvent(InputEvent event) {
+ if (!(event instanceof MotionEvent)) {
+ Slog.wtf(TAG,
+ "Received InputEvent that was not a MotionEvent");
+ finishInputEvent(event, true);
+ return;
+ }
+ MotionEvent motionEvent = (MotionEvent) event;
+ if (motionEvent.getAction() != MotionEvent.ACTION_DOWN) {
+ finishInputEvent(event, true);
+ return;
+ }
+
+ if (event.getEventTime() - mLastToast > TOAST_COOL_DOWN_MILLIS) {
+ String message = "go/activity-touch-opaque - "
+ + mActivityRecord.mActivityComponent.getPackageName()
+ + " blocked the touch!";
+ showAsToastAndLog(message);
+ mLastToast = event.getEventTime();
+ mRapidTouchCount = 1;
+ } else if (++mRapidTouchCount >= NUMBER_OF_TOUCHES_TO_DISABLE && !mDisabled) {
+ // Disable touch blocking until Activity Record is recreated.
+ String message = "Disabled go/activity-touch-opaque - "
+ + mActivityRecord.mActivityComponent.getPackageName();
+ showAsToastAndLog(message);
+ mDisabled = true;
+ }
+ finishInputEvent(event, true);
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 223f0be..352a070 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -37,6 +37,7 @@
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.KeyguardManager;
+import android.app.TaskInfo;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
import android.content.IIntentSender;
@@ -402,4 +403,16 @@
mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
return true;
}
+
+ /**
+ * Called when an activity is successfully launched.
+ */
+ void onActivityLaunched(TaskInfo taskInfo, ActivityInfo activityInfo) {
+ final SparseArray<ActivityInterceptorCallback> callbacks =
+ mService.getActivityInterceptorCallbacks();
+ for (int i = 0; i < callbacks.size(); i++) {
+ final ActivityInterceptorCallback callback = callbacks.valueAt(i);
+ callback.onActivityLaunched(taskInfo, activityInfo);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index bb7434d..471b4ce 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1559,6 +1559,10 @@
mService.getTaskChangeNotificationController().notifyActivityRestartAttempt(
targetTask.getTaskInfo(), homeTaskVisible, clearedTask, visible);
}
+
+ if (ActivityManager.isStartResultSuccessful(result)) {
+ mInterceptor.onActivityLaunched(targetTask.getTaskInfo(), r.info);
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3b4a31b..2e318bc 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1585,8 +1585,9 @@
@Override
boolean isSyncFinished() {
- if (mDisplayRotation.isWaitingForRemoteRotation()) return false;
- return super.isSyncFinished();
+ // Do not consider children because if they are requested to be synced, they should be
+ // added to sync group explicitly.
+ return !mDisplayRotation.isWaitingForRemoteRotation();
}
/**
@@ -1876,16 +1877,16 @@
}
}
- /** Shows the given window which may be hidden for screen frozen. */
- void finishFadeRotationAnimation(WindowState w) {
+ /** Shows the given window which may be hidden for screen rotation. */
+ void finishFadeRotationAnimation(WindowToken windowToken) {
final FadeRotationAnimationController controller = mFadeRotationAnimationController;
- if (controller != null && controller.show(w.mToken)) {
+ if (controller != null && controller.show(windowToken)) {
mFadeRotationAnimationController = null;
}
}
- /** Returns {@code true} if the display should wait for the given window to stop freezing. */
- boolean waitForUnfreeze(WindowState w) {
+ /** Returns {@code true} if the screen rotation animation needs to wait for the window. */
+ boolean shouldSyncRotationChange(WindowState w) {
if (w.mForceSeamlesslyRotate) {
// The window should look no different before and after rotation.
return false;
@@ -3230,6 +3231,7 @@
mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
controller.mTransitionMetricsReporter.associate(t,
startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN));
+ startFadeRotationAnimation(false /* shouldDebounce */);
}
t.setKnownConfigChanges(this, changes);
}
@@ -4136,11 +4138,11 @@
* which controls the visibility and animation of the input method window.
*/
void updateImeInputAndControlTarget(WindowState target) {
+ if (target != null && target.mActivityRecord != null) {
+ target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false;
+ }
if (mImeInputTarget != target) {
ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target);
- if (target != null && target.mActivityRecord != null) {
- target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false;
- }
setImeInputTarget(target);
mInsetsStateController.updateAboveInsetsState(mInputMethodWindow, mInsetsStateController
.getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 427bbeb..3200473 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -595,12 +595,8 @@
// Go through all tasks and collect them before the rotation
// TODO(shell-transitions): move collect() to onConfigurationChange once wallpaper
// handling is synchronized.
- mDisplayContent.forAllTasks(task -> {
- if (task.isVisible()) {
- mDisplayContent.mTransitionController.collect(task);
- }
- });
- mDisplayContent.getInsetsStateController().addProvidersToTransition();
+ mDisplayContent.mTransitionController.collectForDisplayChange(mDisplayContent,
+ null /* use collecting transition */);
}
mService.mAtmService.deferWindowLayout();
try {
diff --git a/services/core/java/com/android/server/wm/FadeAnimationController.java b/services/core/java/com/android/server/wm/FadeAnimationController.java
index 2f3ad40..817b27a 100644
--- a/services/core/java/com/android/server/wm/FadeAnimationController.java
+++ b/services/core/java/com/android/server/wm/FadeAnimationController.java
@@ -36,10 +36,12 @@
* An animation controller to fade-in/out for a window token.
*/
public class FadeAnimationController {
+ protected final DisplayContent mDisplayContent;
protected final Context mContext;
protected final ArrayMap<WindowToken, Runnable> mDeferredFinishCallbacks = new ArrayMap<>();
public FadeAnimationController(DisplayContent displayContent) {
+ mDisplayContent = displayContent;
mContext = displayContent.mWmService.mContext;
}
@@ -69,7 +71,9 @@
return;
}
- final FadeAnimationAdapter animationAdapter = createAdapter(show, windowToken);
+ final Animation animation = show ? getFadeInAnimation() : getFadeOutAnimation();
+ final FadeAnimationAdapter animationAdapter = animation != null
+ ? createAdapter(createAnimationSpec(animation), show, windowToken) : null;
if (animationAdapter == null) {
return;
}
@@ -86,17 +90,10 @@
show /* hidden */, animationType, finishedCallback);
}
- protected FadeAnimationAdapter createAdapter(boolean show, WindowToken windowToken) {
- final Animation animation = show ? getFadeInAnimation() : getFadeOutAnimation();
- if (animation == null) {
- return null;
- }
-
- final LocalAnimationAdapter.AnimationSpec windowAnimationSpec =
- createAnimationSpec(animation);
-
- return new FadeAnimationAdapter(
- windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken);
+ protected FadeAnimationAdapter createAdapter(LocalAnimationAdapter.AnimationSpec animationSpec,
+ boolean show, WindowToken windowToken) {
+ return new FadeAnimationAdapter(animationSpec, windowToken.getSurfaceAnimationRunner(),
+ show, windowToken);
}
protected LocalAnimationAdapter.AnimationSpec createAnimationSpec(
@@ -140,7 +137,7 @@
protected class FadeAnimationAdapter extends LocalAnimationAdapter {
protected final boolean mShow;
- private final WindowToken mToken;
+ protected final WindowToken mToken;
FadeAnimationAdapter(AnimationSpec windowAnimationSpec,
SurfaceAnimationRunner surfaceAnimationRunner, boolean show,
diff --git a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
index 52a7ac7..cf36c85 100644
--- a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
+++ b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
@@ -18,6 +18,10 @@
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
+import android.os.HandlerExecutor;
+import android.util.ArrayMap;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
@@ -33,10 +37,11 @@
*/
public class FadeRotationAnimationController extends FadeAnimationController {
- private final ArrayList<WindowToken> mTargetWindowTokens = new ArrayList<>();
+ /** The map of window token to its animation leash. */
+ private final ArrayMap<WindowToken, SurfaceControl> mTargetWindowTokens = new ArrayMap<>();
private final WindowManagerService mService;
/** If non-null, it usually indicates that there will be a screen rotation animation. */
- private final Runnable mFrozenTimeoutRunnable;
+ private final Runnable mTimeoutRunnable;
private final WindowToken mNavBarToken;
/** A runnable which gets called when the {@link #show()} is called. */
@@ -45,16 +50,30 @@
/** Whether to use constant zero alpha animation. */
private boolean mHideImmediately;
+ /** Whether this controller is triggered from shell transition. */
+ private final boolean mIsChangeTransition;
+
+ /** Whether the start transaction of the transition is committed (by shell). */
+ private boolean mIsStartTransactionCommitted;
+
+ /** The list to store the drawn tokens before the rotation animation starts. */
+ private ArrayList<WindowToken> mPendingShowTokens;
+
public FadeRotationAnimationController(DisplayContent displayContent) {
super(displayContent);
mService = displayContent.mWmService;
- mFrozenTimeoutRunnable = mService.mDisplayFrozen ? () -> {
+ mIsChangeTransition = displayContent.inTransition()
+ && displayContent.mTransitionController.getCollectingTransitionType()
+ == WindowManager.TRANSIT_CHANGE;
+ mIsStartTransactionCommitted = !mIsChangeTransition;
+ mTimeoutRunnable = displayContent.getRotationAnimation() != null
+ || mIsChangeTransition ? () -> {
synchronized (mService.mGlobalLock) {
displayContent.finishFadeRotationAnimationIfPossible();
mService.mWindowPlacerLocked.performSurfacePlacement();
}
} : null;
- if (mFrozenTimeoutRunnable != null) {
+ if (mTimeoutRunnable != null) {
// Hide the windows immediately because screen should have been covered by screenshot.
mHideImmediately = true;
}
@@ -68,7 +87,7 @@
// Do not animate movable navigation bar (e.g. non-gesture mode) or when the navigation
// bar is currently controlled by recents animation.
if (!displayPolicy.navigationBarCanMove() && !navBarControlledByRecents) {
- mTargetWindowTokens.add(mNavBarToken);
+ mTargetWindowTokens.put(mNavBarToken, null);
}
} else {
mNavBarToken = null;
@@ -79,7 +98,7 @@
if (w.mActivityRecord == null && w.mHasSurface && !w.mForceSeamlesslyRotate
&& !w.mIsWallpaper && !w.mIsImWindow && w != navigationBar
&& w != notificationShade) {
- mTargetWindowTokens.add(w.mToken);
+ mTargetWindowTokens.put(w.mToken, null);
}
}, true /* traverseTopToBottom */);
}
@@ -87,12 +106,13 @@
/** Applies show animation on the previously hidden window tokens. */
void show() {
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
- final WindowToken windowToken = mTargetWindowTokens.get(i);
+ final WindowToken windowToken = mTargetWindowTokens.keyAt(i);
fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
}
mTargetWindowTokens.clear();
- if (mFrozenTimeoutRunnable != null) {
- mService.mH.removeCallbacks(mFrozenTimeoutRunnable);
+ mPendingShowTokens = null;
+ if (mTimeoutRunnable != null) {
+ mService.mH.removeCallbacks(mTimeoutRunnable);
}
if (mOnShowRunnable != null) {
mOnShowRunnable.run();
@@ -105,10 +125,22 @@
* controller is created for normal rotation.
*/
boolean show(WindowToken token) {
- if (mFrozenTimeoutRunnable != null && mTargetWindowTokens.remove(token)) {
+ if (!mIsStartTransactionCommitted) {
+ // The fade-in animation should only start after the screenshot layer is shown by shell.
+ // Otherwise the window will be blinking before the rotation animation starts. So store
+ // to a pending list and animate them until the transaction is committed.
+ if (mTargetWindowTokens.containsKey(token)) {
+ if (mPendingShowTokens == null) {
+ mPendingShowTokens = new ArrayList<>();
+ }
+ mPendingShowTokens.add(token);
+ }
+ return false;
+ }
+ if (mTimeoutRunnable != null && mTargetWindowTokens.remove(token) != null) {
fadeWindowToken(true /* show */, token, ANIMATION_TYPE_FIXED_TRANSFORM);
if (mTargetWindowTokens.isEmpty()) {
- mService.mH.removeCallbacks(mFrozenTimeoutRunnable);
+ mService.mH.removeCallbacks(mTimeoutRunnable);
return true;
}
}
@@ -118,11 +150,11 @@
/** Applies hide animation on the window tokens which may be seamlessly rotated later. */
void hide() {
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
- final WindowToken windowToken = mTargetWindowTokens.get(i);
+ final WindowToken windowToken = mTargetWindowTokens.keyAt(i);
fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
}
- if (mFrozenTimeoutRunnable != null) {
- mService.mH.postDelayed(mFrozenTimeoutRunnable,
+ if (mTimeoutRunnable != null) {
+ mService.mH.postDelayed(mTimeoutRunnable,
WindowManagerService.WINDOW_FREEZE_TIMEOUT_DURATION);
}
}
@@ -131,7 +163,6 @@
void hideImmediately(WindowToken windowToken) {
final boolean original = mHideImmediately;
mHideImmediately = true;
- mTargetWindowTokens.add(windowToken);
fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_FIXED_TRANSFORM);
mHideImmediately = original;
}
@@ -143,16 +174,43 @@
/** Returns {@code true} if the controller will run fade animations on the window. */
boolean isTargetToken(WindowToken token) {
- return mTargetWindowTokens.contains(token);
+ return mTargetWindowTokens.containsKey(token);
}
void setOnShowRunnable(Runnable onShowRunnable) {
mOnShowRunnable = onShowRunnable;
}
+ /**
+ * Puts initial operation of leash to the transaction which will be executed when the
+ * transition starts. And associate transaction callback to consume pending animations.
+ */
+ void setupStartTransaction(SurfaceControl.Transaction t) {
+ // Hide the windows immediately because a screenshot layer should cover the screen.
+ for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
+ final SurfaceControl leash = mTargetWindowTokens.valueAt(i);
+ if (leash != null) {
+ t.setAlpha(leash, 0f);
+ }
+ }
+ // If there are windows have redrawn in new rotation but the start transaction has not
+ // been applied yet, the fade-in animation will be deferred. So once the transaction is
+ // committed, the fade-in animation can run with screen rotation animation.
+ t.addTransactionCommittedListener(new HandlerExecutor(mService.mH), () -> {
+ synchronized (mService.mGlobalLock) {
+ mIsStartTransactionCommitted = true;
+ if (mPendingShowTokens == null) return;
+ for (int i = mPendingShowTokens.size() - 1; i >= 0; i--) {
+ mDisplayContent.finishFadeRotationAnimation(mPendingShowTokens.get(i));
+ }
+ mPendingShowTokens = null;
+ }
+ });
+ }
+
@Override
public Animation getFadeInAnimation() {
- if (mFrozenTimeoutRunnable != null) {
+ if (mTimeoutRunnable != null) {
// Use a shorter animation so it is easier to align with screen rotation animation.
return AnimationUtils.loadAnimation(mContext, R.anim.screen_rotate_0_enter);
}
@@ -162,8 +220,28 @@
@Override
public Animation getFadeOutAnimation() {
if (mHideImmediately) {
- return new AlphaAnimation(0 /* fromAlpha */, 0 /* toAlpha */);
+ // For change transition, the hide transaction needs to be applied with sync transaction
+ // (setupStartTransaction). So keep alpha 1 just to get the animation leash.
+ final float alpha = mIsChangeTransition ? 1 : 0;
+ return new AlphaAnimation(alpha /* fromAlpha */, alpha /* toAlpha */);
}
return super.getFadeOutAnimation();
}
+
+ @Override
+ protected FadeAnimationAdapter createAdapter(LocalAnimationAdapter.AnimationSpec animationSpec,
+ boolean show, WindowToken windowToken) {
+ return new FadeAnimationAdapter(animationSpec, windowToken.getSurfaceAnimationRunner(),
+ show, windowToken) {
+ @Override
+ public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t,
+ int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) {
+ // The fade cycle is done when showing, so only need to store the leash when hiding.
+ if (!show) {
+ mTargetWindowTokens.put(mToken, animationLeash);
+ }
+ super.startAnimation(animationLeash, t, type, finishCallback);
+ }
+ };
+ }
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 405a9e5..e33c440 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -249,16 +249,6 @@
return result;
}
- public void addProvidersToTransition() {
- for (int i = mProviders.size() - 1; i >= 0; --i) {
- final InsetsSourceProvider p = mProviders.valueAt(i);
- if (p == null) continue;
- final WindowContainer wc = p.mWin;
- if (wc == null) continue;
- mDisplayContent.mTransitionController.collect(wc);
- }
- }
-
/**
* @return The provider of a specific type.
*/
diff --git a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
index 7abf3b8..af8293a 100644
--- a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
+++ b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
@@ -37,7 +37,6 @@
private static final Interpolator FADE_OUT_INTERPOLATOR =
new PathInterpolator(0.2f, 0f, 1f, 1f);
- private DisplayContent mDisplayContent;
private final WindowState mNavigationBar;
private Animation mFadeInAnimation;
private Animation mFadeOutAnimation;
@@ -47,7 +46,6 @@
public NavBarFadeAnimationController(DisplayContent displayContent) {
super(displayContent);
- mDisplayContent = displayContent;
mNavigationBar = displayContent.getDisplayPolicy().getNavigationBar();
mFadeInAnimation = new AlphaAnimation(0f, 1f);
mFadeInAnimation.setDuration(FADE_IN_DURATION);
@@ -69,16 +67,10 @@
}
@Override
- protected FadeAnimationAdapter createAdapter(boolean show, WindowToken windowToken) {
- final Animation animation = show ? getFadeInAnimation() : getFadeOutAnimation();
- if (animation == null) {
- return null;
- }
-
- final LocalAnimationAdapter.AnimationSpec windowAnimationSpec =
- createAnimationSpec(animation);
+ protected FadeAnimationAdapter createAdapter(LocalAnimationAdapter.AnimationSpec animationSpec,
+ boolean show, WindowToken windowToken) {
return new NavFadeAnimationAdapter(
- windowAnimationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken,
+ animationSpec, windowToken.getSurfaceAnimationRunner(), show, windowToken,
show ? mFadeInParent : mFadeOutParent);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b328e4d..f0b55cb 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4600,23 +4600,14 @@
moveToFront(reason, null);
}
- /**
- * @param reason The reason for moving the root task to the front.
- * @param task If non-null, the task will be moved to the top of the root task.
- */
void moveToFront(String reason, Task task) {
- if (!isAttached()) {
- return;
- }
-
- final TaskDisplayArea taskDisplayArea = getDisplayArea();
-
if (inSplitScreenSecondaryWindowingMode()) {
// If the root task is in split-screen secondary mode, we need to make sure we move the
// primary split-screen root task forward in the case it is currently behind a
// fullscreen root task so both halves of the split-screen appear on-top and the
// fullscreen root task isn't cutting between them.
// TODO(b/70677280): This is a workaround until we can fix as part of b/70677280.
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
final Task topFullScreenRootTask =
taskDisplayArea.getTopRootTaskInWindowingMode(WINDOWING_MODE_FULLSCREEN);
if (topFullScreenRootTask != null) {
@@ -4624,10 +4615,30 @@
taskDisplayArea.getRootSplitScreenPrimaryTask();
if (primarySplitScreenRootTask != null
&& topFullScreenRootTask.compareTo(primarySplitScreenRootTask) > 0) {
- primarySplitScreenRootTask.moveToFront(reason + " splitScreenToTop");
+ primarySplitScreenRootTask.moveToFrontInner(reason + " splitScreenToTop",
+ null /* task */);
}
}
+ } else if (mMoveAdjacentTogether && getAdjacentTaskFragment() != null) {
+ final Task adjacentTask = getAdjacentTaskFragment().asTask();
+ if (adjacentTask != null) {
+ adjacentTask.moveToFrontInner(reason + " adjacentTaskToTop", null /* task */);
+ }
}
+ moveToFrontInner(reason, task);
+ }
+
+ /**
+ * @param reason The reason for moving the root task to the front.
+ * @param task If non-null, the task will be moved to the top of the root task.
+ */
+ @VisibleForTesting
+ void moveToFrontInner(String reason, Task task) {
+ if (!isAttached()) {
+ return;
+ }
+
+ final TaskDisplayArea taskDisplayArea = getDisplayArea();
if (!isActivityTypeHome() && returnsToHomeRootTask()) {
// Make sure the root home task is behind this root task since that is where we
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index e497b53..59a5cdf 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -168,6 +168,14 @@
private TaskFragment mAdjacentTaskFragment;
/**
+ * Whether to move adjacent task fragment together when re-positioning.
+ *
+ * @see #mAdjacentTaskFragment
+ */
+ // TODO(b/207185041): Remove this once having a single-top root for split screen.
+ boolean mMoveAdjacentTogether;
+
+ /**
* Prevents duplicate calls to onTaskAppeared.
*/
boolean mTaskFragmentAppearedSent;
@@ -309,14 +317,15 @@
return service.mWindowOrganizerController.getTaskFragment(token);
}
- void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment) {
+ void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment, boolean moveTogether) {
if (mAdjacentTaskFragment == taskFragment) {
return;
}
resetAdjacentTaskFragment();
if (taskFragment != null) {
mAdjacentTaskFragment = taskFragment;
- taskFragment.setAdjacentTaskFragment(this);
+ mMoveAdjacentTogether = moveTogether;
+ taskFragment.setAdjacentTaskFragment(this, moveTogether);
}
}
@@ -325,9 +334,11 @@
if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) {
mAdjacentTaskFragment.mAdjacentTaskFragment = null;
mAdjacentTaskFragment.mDelayLastActivityRemoval = false;
+ mAdjacentTaskFragment.mMoveAdjacentTogether = false;
}
mAdjacentTaskFragment = null;
mDelayLastActivityRemoval = false;
+ mMoveAdjacentTogether = false;
}
void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid,
@@ -1942,7 +1953,15 @@
if (inOutConfig.smallestScreenWidthDp
== Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
- if (WindowConfiguration.isFloating(windowingMode)) {
+ // When entering to or exiting from Pip, the PipTaskOrganizer will set the
+ // windowing mode of the activity in the task to WINDOWING_MODE_FULLSCREEN and
+ // temporarily set the bounds of the task to fullscreen size for transitioning.
+ // It will get the wrong value if the calculation is based on this temporary
+ // fullscreen bounds.
+ // We should just inherit the value from parent for this temporary state.
+ final boolean inPipTransition = windowingMode == WINDOWING_MODE_PINNED
+ && !mTmpFullBounds.isEmpty() && mTmpFullBounds.equals(parentBounds);
+ if (WindowConfiguration.isFloating(windowingMode) && !inPipTransition) {
// For floating tasks, calculate the smallest width from the bounds of the task
inOutConfig.smallestScreenWidthDp = (int) (
Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 7349594..3974747 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -596,6 +596,12 @@
}
}
+ // This is non-null only if display has changes. It handles the visible windows that don't
+ // need to be participated in the transition.
+ final FadeRotationAnimationController controller = dc.getFadeRotationAnimationController();
+ if (controller != null) {
+ controller.setupStartTransaction(transaction);
+ }
mStartTransaction = transaction;
mFinishTransaction = mController.mAtm.mWindowManager.mTransactionFactory.get();
buildFinishTransaction(mFinishTransaction, info.getRootLeash());
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index e054570..99dfe13 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -337,6 +337,29 @@
mCollectingTransition.collectExistenceChange(wc);
}
+ /**
+ * Collects the window containers which need to be synced with the changing display (e.g.
+ * rotating) to the given transition or the current collecting transition.
+ */
+ void collectForDisplayChange(@NonNull DisplayContent dc, @Nullable Transition incoming) {
+ if (incoming == null) incoming = mCollectingTransition;
+ if (incoming == null) return;
+ final Transition transition = incoming;
+ // Collect all visible tasks.
+ dc.forAllLeafTasks(task -> {
+ if (task.isVisible()) {
+ transition.collect(task);
+ }
+ }, true /* traverseTopToBottom */);
+ // Collect all visible non-app windows which need to be drawn before the animation starts.
+ dc.forAllWindows(w -> {
+ if (w.mActivityRecord == null && w.isVisible() && !inTransition(w.mToken)
+ && dc.shouldSyncRotationChange(w)) {
+ transition.collect(w.mToken);
+ }
+ }, true /* traverseTopToBottom */);
+ }
+
/** @see Transition#setOverrideAnimation */
void setOverrideAnimation(TransitionInfo.AnimationOptions options,
@Nullable IRemoteCallback startCallback, @Nullable IRemoteCallback finishCallback) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 0649b25..525d84be 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -335,10 +335,7 @@
// Go through all tasks and collect them before the rotation
// TODO(shell-transitions): move collect() to onConfigurationChange once
// wallpaper handling is synchronized.
- dc.forAllTasks(task -> {
- if (task.isVisible()) transition.collect(task);
- });
- dc.getInsetsStateController().addProvidersToTransition();
+ dc.mTransitionController.collectForDisplayChange(dc, transition);
dc.sendNewConfiguration();
effects |= TRANSACT_EFFECTS_LIFECYCLE;
}
@@ -664,7 +661,7 @@
sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception);
break;
}
- tf1.setAdjacentTaskFragment(tf2);
+ tf1.setAdjacentTaskFragment(tf2, false /* moveAdjacentTogether */);
effects |= TRANSACT_EFFECTS_LIFECYCLE;
final Bundle bundle = hop.getLaunchOptions();
@@ -977,7 +974,7 @@
throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by"
+ " organizer root1=" + root1 + " root2=" + root2);
}
- root1.setAdjacentTaskFragment(root2);
+ root1.setAdjacentTaskFragment(root2, hop.getMoveAdjacentTogether());
return TRANSACT_EFFECTS_LIFECYCLE;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index bae5465..0b91742 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1473,12 +1473,12 @@
if (changing) {
mLastFreezeDuration = 0;
if (mWmService.mRoot.mOrientationChangeComplete
- && mDisplayContent.waitForUnfreeze(this)) {
+ && mDisplayContent.shouldSyncRotationChange(this)) {
mWmService.mRoot.mOrientationChangeComplete = false;
}
} else {
// The orientation change is completed. If it was hidden by the animation, reshow it.
- mDisplayContent.finishFadeRotationAnimation(this);
+ mDisplayContent.finishFadeRotationAnimation(mToken);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index b147455..316051e 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -566,7 +566,7 @@
if (w.getOrientationChanging()) {
if (!w.isDrawn()) {
- if (w.mDisplayContent.waitForUnfreeze(w)) {
+ if (w.mDisplayContent.shouldSyncRotationChange(w)) {
w.mWmService.mRoot.mOrientationChangeComplete = false;
mAnimator.mLastWindowFreezeSource = w;
}
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 318ad06..e5a3b7a 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -693,11 +693,8 @@
@Override
public String toString() {
if (stringName == null) {
- StringBuilder sb = new StringBuilder();
- sb.append("WindowToken{");
- sb.append(Integer.toHexString(System.identityHashCode(this)));
- sb.append(" "); sb.append(token); sb.append('}');
- stringName = sb.toString();
+ stringName = "WindowToken{" + Integer.toHexString(System.identityHashCode(this))
+ + " type=" + windowType + " " + token + "}";
}
return stringName;
}
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 2ccef9a..4504853 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -55,6 +55,7 @@
#include "gnss/GnssAntennaInfoCallback.h"
#include "gnss/GnssBatching.h"
#include "gnss/GnssConfiguration.h"
+#include "gnss/GnssGeofence.h"
#include "gnss/GnssMeasurement.h"
#include "gnss/GnssNavigationMessage.h"
#include "gnss/Utils.h"
@@ -79,12 +80,6 @@
static jmethodID method_requestRefLocation;
static jmethodID method_requestSetID;
static jmethodID method_requestUtcTime;
-static jmethodID method_reportGeofenceTransition;
-static jmethodID method_reportGeofenceStatus;
-static jmethodID method_reportGeofenceAddStatus;
-static jmethodID method_reportGeofenceRemoveStatus;
-static jmethodID method_reportGeofencePauseStatus;
-static jmethodID method_reportGeofenceResumeStatus;
static jmethodID method_reportGnssServiceDied;
static jmethodID method_reportGnssPowerStats;
static jmethodID method_setSubHalMeasurementCorrectionsCapabilities;
@@ -133,8 +128,6 @@
using android::hardware::gnss::V1_0::GnssLocationFlags;
using android::hardware::gnss::V1_0::IAGnssRilCallback;
-using android::hardware::gnss::V1_0::IGnssGeofenceCallback;
-using android::hardware::gnss::V1_0::IGnssGeofencing;
using android::hardware::gnss::V1_0::IGnssNavigationMessage;
using android::hardware::gnss::V1_0::IGnssNavigationMessageCallback;
using android::hardware::gnss::V1_0::IGnssNi;
@@ -192,8 +185,6 @@
using IGnssAidl = android::hardware::gnss::IGnss;
using IGnssCallbackAidl = android::hardware::gnss::IGnssCallback;
using IGnssBatchingAidl = android::hardware::gnss::IGnssBatching;
-using IGnssGeofenceAidl = android::hardware::gnss::IGnssGeofence;
-using IGnssGeofenceCallbackAidl = android::hardware::gnss::IGnssGeofenceCallback;
using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds;
using IGnssPsdsCallbackAidl = android::hardware::gnss::IGnssPsdsCallback;
using IGnssConfigurationAidl = android::hardware::gnss::IGnssConfiguration;
@@ -220,12 +211,10 @@
sp<IGnss_V2_1> gnssHal_V2_1 = nullptr;
sp<IGnssAidl> gnssHalAidl = nullptr;
sp<IGnssBatchingAidl> gnssBatchingAidlIface = nullptr;
-sp<IGnssGeofenceAidl> gnssGeofenceAidlIface = nullptr;
sp<IGnssPsdsAidl> gnssPsdsAidlIface = nullptr;
sp<IGnssXtra> gnssXtraIface = nullptr;
sp<IAGnssRil_V1_0> agnssRilIface = nullptr;
sp<IAGnssRil_V2_0> agnssRilIface_V2_0 = nullptr;
-sp<IGnssGeofencing> gnssGeofencingIface = nullptr;
sp<IAGnss_V1_0> agnssIface = nullptr;
sp<IAGnss_V2_0> agnssIface_V2_0 = nullptr;
sp<IGnssDebug_V1_0> gnssDebugIface = nullptr;
@@ -241,6 +230,7 @@
std::unique_ptr<android::gnss::GnssMeasurementInterface> gnssMeasurementIface = nullptr;
std::unique_ptr<android::gnss::GnssNavigationMessageInterface> gnssNavigationMessageIface = nullptr;
std::unique_ptr<android::gnss::GnssBatchingInterface> gnssBatchingIface = nullptr;
+std::unique_ptr<android::gnss::GnssGeofenceInterface> gnssGeofencingIface = nullptr;
#define WAKE_LOCK_NAME "GPS"
@@ -714,199 +704,6 @@
return Void();
}
-/** Util class for GnssGeofenceCallback methods. */
-struct GnssGeofenceCallbackUtil {
- template <class T>
- static void gnssGeofenceTransitionCb(int geofenceId, const T& location, int transition,
- int64_t timestampMillis);
- template <class T>
- static void gnssGeofenceStatusCb(int availability, const T& lastLocation);
- static void gnssGeofenceAddCb(int geofenceId, int status);
- static void gnssGeofenceRemoveCb(int geofenceId, int status);
- static void gnssGeofencePauseCb(int geofenceId, int status);
- static void gnssGeofenceResumeCb(int geofenceId, int status);
-
-private:
- GnssGeofenceCallbackUtil() = delete;
-};
-
-template <class T>
-void GnssGeofenceCallbackUtil::gnssGeofenceTransitionCb(int geofenceId, const T& location,
- int transition, int64_t timestamp) {
- JNIEnv* env = getJniEnv();
-
- jobject jLocation = translateGnssLocation(env, location);
-
- env->CallVoidMethod(mCallbacksObj,
- method_reportGeofenceTransition,
- geofenceId,
- jLocation,
- transition,
- timestamp);
-
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- env->DeleteLocalRef(jLocation);
-}
-
-template <class T>
-void GnssGeofenceCallbackUtil::gnssGeofenceStatusCb(int availability, const T& lastLocation) {
- JNIEnv* env = getJniEnv();
-
- jobject jLocation = translateGnssLocation(env, lastLocation);
-
- env->CallVoidMethod(mCallbacksObj, method_reportGeofenceStatus, availability, jLocation);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
- env->DeleteLocalRef(jLocation);
-}
-
-void GnssGeofenceCallbackUtil::gnssGeofenceAddCb(int geofenceId, int status) {
- JNIEnv* env = getJniEnv();
- if (status != IGnssGeofenceCallbackAidl::OPERATION_SUCCESS) {
- ALOGE("%s: Error in adding a Geofence: %d\n", __func__, status);
- }
-
- env->CallVoidMethod(mCallbacksObj,
- method_reportGeofenceAddStatus,
- geofenceId,
- status);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
-}
-
-void GnssGeofenceCallbackUtil::gnssGeofenceRemoveCb(int geofenceId, int status) {
- JNIEnv* env = getJniEnv();
- if (status != IGnssGeofenceCallbackAidl::OPERATION_SUCCESS) {
- ALOGE("%s: Error in removing a Geofence: %d\n", __func__, status);
- }
-
- env->CallVoidMethod(mCallbacksObj,
- method_reportGeofenceRemoveStatus,
- geofenceId, status);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
-}
-
-void GnssGeofenceCallbackUtil::gnssGeofencePauseCb(int geofenceId, int status) {
- JNIEnv* env = getJniEnv();
- if (status != IGnssGeofenceCallbackAidl::OPERATION_SUCCESS) {
- ALOGE("%s: Error in pausing Geofence: %d\n", __func__, status);
- }
-
- env->CallVoidMethod(mCallbacksObj,
- method_reportGeofencePauseStatus,
- geofenceId, status);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
-}
-
-void GnssGeofenceCallbackUtil::gnssGeofenceResumeCb(int geofenceId, int status) {
- JNIEnv* env = getJniEnv();
- if (status != IGnssGeofenceCallbackAidl::OPERATION_SUCCESS) {
- ALOGE("%s: Error in resuming Geofence: %d\n", __func__, status);
- }
-
- env->CallVoidMethod(mCallbacksObj,
- method_reportGeofenceResumeStatus,
- geofenceId, status);
- checkAndClearExceptionFromCallback(env, __FUNCTION__);
-}
-
-/*
- * GnssGeofenceCallbackAidl class implements the callback methods for the IGnssGeofence AIDL
- * interface.
- */
-struct GnssGeofenceCallbackAidl : public android::hardware::gnss::BnGnssGeofenceCallback {
- Status gnssGeofenceTransitionCb(int geofenceId, const GnssLocationAidl& location,
- int transition, int64_t timestampMillis) override;
- Status gnssGeofenceStatusCb(int availability, const GnssLocationAidl& lastLocation) override;
- Status gnssGeofenceAddCb(int geofenceId, int status) override;
- Status gnssGeofenceRemoveCb(int geofenceId, int status) override;
- Status gnssGeofencePauseCb(int geofenceId, int status) override;
- Status gnssGeofenceResumeCb(int geofenceId, int status) override;
-};
-
-Status GnssGeofenceCallbackAidl::gnssGeofenceTransitionCb(int geofenceId,
- const GnssLocationAidl& location,
- int transition, int64_t timestampMillis) {
- GnssGeofenceCallbackUtil::gnssGeofenceTransitionCb(geofenceId, location, transition,
- timestampMillis);
- return Status::ok();
-}
-
-Status GnssGeofenceCallbackAidl::gnssGeofenceStatusCb(int availability,
- const GnssLocationAidl& lastLocation) {
- GnssGeofenceCallbackUtil::gnssGeofenceStatusCb(availability, lastLocation);
- return Status::ok();
-}
-
-Status GnssGeofenceCallbackAidl::gnssGeofenceAddCb(int geofenceId, int status) {
- GnssGeofenceCallbackUtil::gnssGeofenceAddCb(geofenceId, status);
- return Status::ok();
-}
-
-Status GnssGeofenceCallbackAidl::gnssGeofenceRemoveCb(int geofenceId, int status) {
- GnssGeofenceCallbackUtil::gnssGeofenceRemoveCb(geofenceId, status);
- return Status::ok();
-}
-
-Status GnssGeofenceCallbackAidl::gnssGeofencePauseCb(int geofenceId, int status) {
- GnssGeofenceCallbackUtil::gnssGeofencePauseCb(geofenceId, status);
- return Status::ok();
-}
-
-Status GnssGeofenceCallbackAidl::gnssGeofenceResumeCb(int geofenceId, int status) {
- GnssGeofenceCallbackUtil::gnssGeofenceResumeCb(geofenceId, status);
- return Status::ok();
-}
-
-/*
- * GnssGeofenceCallback class implements the callback methods for the
- * IGnssGeofence HIDL interface.
- */
-struct GnssGeofenceCallback : public IGnssGeofenceCallback {
- // Methods from ::android::hardware::gps::V1_0::IGnssGeofenceCallback follow.
- Return<void> gnssGeofenceTransitionCb(int32_t geofenceId, const GnssLocation_V1_0& location,
- GeofenceTransition transition,
- hardware::gnss::V1_0::GnssUtcTime timestamp) override;
- Return<void> gnssGeofenceStatusCb(GeofenceAvailability status,
- const GnssLocation_V1_0& location) override;
- Return<void> gnssGeofenceAddCb(int32_t geofenceId, GeofenceStatus status) override;
- Return<void> gnssGeofenceRemoveCb(int32_t geofenceId, GeofenceStatus status) override;
- Return<void> gnssGeofencePauseCb(int32_t geofenceId, GeofenceStatus status) override;
- Return<void> gnssGeofenceResumeCb(int32_t geofenceId, GeofenceStatus status) override;
-};
-
-Return<void> GnssGeofenceCallback::gnssGeofenceTransitionCb(
- int32_t geofenceId, const GnssLocation_V1_0& location, GeofenceTransition transition,
- hardware::gnss::V1_0::GnssUtcTime timestamp) {
- GnssGeofenceCallbackUtil::gnssGeofenceTransitionCb(geofenceId, location, (int)transition,
- (int64_t)timestamp);
- return Void();
-}
-
-Return<void> GnssGeofenceCallback::gnssGeofenceStatusCb(GeofenceAvailability availability,
- const GnssLocation_V1_0& location) {
- GnssGeofenceCallbackUtil::gnssGeofenceStatusCb((int)availability, location);
- return Void();
-}
-
-Return<void> GnssGeofenceCallback::gnssGeofenceAddCb(int32_t geofenceId, GeofenceStatus status) {
- GnssGeofenceCallbackUtil::gnssGeofenceAddCb(geofenceId, (int)status);
- return Void();
-}
-
-Return<void> GnssGeofenceCallback::gnssGeofenceRemoveCb(int32_t geofenceId, GeofenceStatus status) {
- GnssGeofenceCallbackUtil::gnssGeofenceRemoveCb(geofenceId, (int)status);
- return Void();
-}
-
-Return<void> GnssGeofenceCallback::gnssGeofencePauseCb(int32_t geofenceId, GeofenceStatus status) {
- GnssGeofenceCallbackUtil::gnssGeofencePauseCb(geofenceId, (int)status);
- return Void();
-}
-
-Return<void> GnssGeofenceCallback::gnssGeofenceResumeCb(int32_t geofenceId, GeofenceStatus status) {
- GnssGeofenceCallbackUtil::gnssGeofenceResumeCb(geofenceId, (int)status);
- return Void();
-}
-
/*
* MeasurementCorrectionsCallback implements callback methods of interface
* IMeasurementCorrectionsCallback.hal.
@@ -1206,18 +1003,6 @@
method_requestRefLocation = env->GetMethodID(clazz, "requestRefLocation", "()V");
method_requestSetID = env->GetMethodID(clazz, "requestSetID", "(I)V");
method_requestUtcTime = env->GetMethodID(clazz, "requestUtcTime", "()V");
- method_reportGeofenceTransition = env->GetMethodID(clazz, "reportGeofenceTransition",
- "(ILandroid/location/Location;IJ)V");
- method_reportGeofenceStatus = env->GetMethodID(clazz, "reportGeofenceStatus",
- "(ILandroid/location/Location;)V");
- method_reportGeofenceAddStatus = env->GetMethodID(clazz, "reportGeofenceAddStatus",
- "(II)V");
- method_reportGeofenceRemoveStatus = env->GetMethodID(clazz, "reportGeofenceRemoveStatus",
- "(II)V");
- method_reportGeofenceResumeStatus = env->GetMethodID(clazz, "reportGeofenceResumeStatus",
- "(II)V");
- method_reportGeofencePauseStatus = env->GetMethodID(clazz, "reportGeofencePauseStatus",
- "(II)V");
method_reportGnssServiceDied = env->GetMethodID(clazz, "reportGnssServiceDied", "()V");
method_reportNfwNotification = env->GetMethodID(clazz, "reportNfwNotification",
"(Ljava/lang/String;BLjava/lang/String;BLjava/lang/String;BZZ)V");
@@ -1522,27 +1307,27 @@
if (checkHidlReturn(gnssConfiguration,
"Unable to get a handle to GnssConfiguration_V1_1")) {
gnssConfigurationIface =
- std::make_unique<android::gnss::GnssConfiguration_V1_1>(gnssConfiguration);
+ std::make_unique<gnss::GnssConfiguration_V1_1>(gnssConfiguration);
}
} else {
auto gnssConfiguration = gnssHal->getExtensionGnssConfiguration();
if (checkHidlReturn(gnssConfiguration,
"Unable to get a handle to GnssConfiguration_V1_0")) {
gnssConfigurationIface =
- std::make_unique<android::gnss::GnssConfiguration_V1_0>(gnssConfiguration);
+ std::make_unique<gnss::GnssConfiguration_V1_0>(gnssConfiguration);
}
}
if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
- sp<IGnssGeofenceAidl> gnssGeofenceAidl;
- auto status = gnssHalAidl->getExtensionGnssGeofence(&gnssGeofenceAidl);
- if (checkAidlStatus(status, "Unable to get a handle to GnssGeofence interface.")) {
- gnssGeofenceAidlIface = gnssGeofenceAidl;
+ sp<hardware::gnss::IGnssGeofence> gnssGeofence;
+ auto status = gnssHalAidl->getExtensionGnssGeofence(&gnssGeofence);
+ if (checkAidlStatus(status, "Unable to get a handle to GnssGeofence AIDL interface.")) {
+ gnssGeofencingIface = std::make_unique<gnss::GnssGeofenceAidl>(gnssGeofence);
}
} else if (gnssHal != nullptr) {
auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing();
if (checkHidlReturn(gnssGeofencing, "Unable to get a handle to GnssGeofencing")) {
- gnssGeofencingIface = gnssGeofencing;
+ gnssGeofencingIface = std::make_unique<gnss::GnssGeofenceHidl>(gnssGeofencing);
}
}
@@ -1680,19 +1465,9 @@
ALOGI("Unable to initialize IAGnss interface.");
}
- // Set IGnssGeofencing.hal callback.
- if (gnssGeofenceAidlIface != nullptr) {
- sp<IGnssGeofenceCallbackAidl> gnssGeofenceCallbackAidl = new GnssGeofenceCallbackAidl();
- auto status = gnssGeofenceAidlIface->setCallback(gnssGeofenceCallbackAidl);
- if (!checkAidlStatus(status, "IGnssGeofenceAidl setCallback() failed.")) {
- gnssGeofenceAidlIface = nullptr;
- }
- } else if (gnssGeofencingIface != nullptr) {
- sp<IGnssGeofenceCallback> gnssGeofencingCbIface = new GnssGeofenceCallback();
- auto status = gnssGeofencingIface->setCallback(gnssGeofencingCbIface);
- if (!checkHidlReturn(status, "IGnssGeofencing setCallback() failed.")) {
- gnssGeofencingIface = nullptr;
- }
+ // Set GnssGeofence callback.
+ if (gnssGeofencingIface != nullptr) {
+ gnssGeofencingIface->setCallback(std::make_unique<gnss::GnssGeofenceCallback>());
} else {
ALOGI("Unable to initialize IGnssGeofencing interface.");
}
@@ -2239,7 +2014,7 @@
static jboolean android_location_gnss_hal_GnssNative_is_geofence_supported(JNIEnv* /* env */,
jclass) {
- if (gnssGeofencingIface == nullptr && gnssGeofenceAidlIface == nullptr) {
+ if (gnssGeofencingIface == nullptr) {
return JNI_FALSE;
}
return JNI_TRUE;
@@ -2249,75 +2024,41 @@
JNIEnv* /* env */, jclass, jint geofenceId, jdouble latitude, jdouble longitude,
jdouble radius, jint last_transition, jint monitor_transition,
jint notification_responsiveness, jint unknown_timer) {
- if (gnssGeofenceAidlIface != nullptr) {
- auto status =
- gnssGeofenceAidlIface->addGeofence(geofenceId, latitude, longitude, radius,
- last_transition, monitor_transition,
- notification_responsiveness, unknown_timer);
- return checkAidlStatus(status, "IGnssGeofenceAidl addGeofence() failed.");
+ if (gnssGeofencingIface == nullptr) {
+ ALOGE("%s: IGnssGeofencing interface not available.", __func__);
+ return JNI_FALSE;
}
-
- if (gnssGeofencingIface != nullptr) {
- auto result = gnssGeofencingIface
- ->addGeofence(geofenceId, latitude, longitude, radius,
- static_cast<IGnssGeofenceCallback::GeofenceTransition>(
- last_transition),
- monitor_transition, notification_responsiveness,
- unknown_timer);
- return checkHidlReturn(result, "IGnssGeofencing addGeofence() failed.");
- }
-
- ALOGE("%s: IGnssGeofencing interface not available.", __func__);
- return JNI_FALSE;
+ return gnssGeofencingIface->addGeofence(geofenceId, latitude, longitude, radius,
+ last_transition, monitor_transition,
+ notification_responsiveness, unknown_timer);
}
static jboolean android_location_gnss_hal_GnssNative_remove_geofence(JNIEnv* /* env */, jclass,
jint geofenceId) {
- if (gnssGeofenceAidlIface != nullptr) {
- auto status = gnssGeofenceAidlIface->removeGeofence(geofenceId);
- return checkAidlStatus(status, "IGnssGeofenceAidl removeGeofence() failed.");
+ if (gnssGeofencingIface == nullptr) {
+ ALOGE("%s: IGnssGeofencing interface not available.", __func__);
+ return JNI_FALSE;
}
-
- if (gnssGeofencingIface != nullptr) {
- auto result = gnssGeofencingIface->removeGeofence(geofenceId);
- return checkHidlReturn(result, "IGnssGeofencing removeGeofence() failed.");
- }
-
- ALOGE("%s: IGnssGeofencing interface not available.", __func__);
- return JNI_FALSE;
+ return gnssGeofencingIface->removeGeofence(geofenceId);
}
static jboolean android_location_gnss_hal_GnssNative_pause_geofence(JNIEnv* /* env */, jclass,
jint geofenceId) {
- if (gnssGeofenceAidlIface != nullptr) {
- auto status = gnssGeofenceAidlIface->pauseGeofence(geofenceId);
- return checkAidlStatus(status, "IGnssGeofenceAidl pauseGeofence() failed.");
+ if (gnssGeofencingIface == nullptr) {
+ ALOGE("%s: IGnssGeofencing interface not available.", __func__);
+ return JNI_FALSE;
}
-
- if (gnssGeofencingIface != nullptr) {
- auto result = gnssGeofencingIface->pauseGeofence(geofenceId);
- return checkHidlReturn(result, "IGnssGeofencing pauseGeofence() failed.");
- }
-
- ALOGE("%s: IGnssGeofencing interface not available.", __func__);
- return JNI_FALSE;
+ return gnssGeofencingIface->pauseGeofence(geofenceId);
}
static jboolean android_location_gnss_hal_GnssNative_resume_geofence(JNIEnv* /* env */, jclass,
jint geofenceId,
jint monitor_transition) {
- if (gnssGeofenceAidlIface != nullptr) {
- auto status = gnssGeofenceAidlIface->resumeGeofence(geofenceId, monitor_transition);
- return checkAidlStatus(status, "IGnssGeofenceAidl resumeGeofence() failed.");
+ if (gnssGeofencingIface == nullptr) {
+ ALOGE("%s: IGnssGeofencing interface not available.", __func__);
+ return JNI_FALSE;
}
-
- if (gnssGeofencingIface != nullptr) {
- auto result = gnssGeofencingIface->resumeGeofence(geofenceId, monitor_transition);
- return checkHidlReturn(result, "IGnssGeofencing resumeGeofence() failed.");
- }
-
- ALOGE("%s: IGnssGeofencing interface not available.", __func__);
- return JNI_FALSE;
+ return gnssGeofencingIface->resumeGeofence(geofenceId, monitor_transition);
}
static jboolean android_location_gnss_hal_GnssNative_is_antenna_info_supported(JNIEnv* env,
diff --git a/services/core/jni/gnss/Android.bp b/services/core/jni/gnss/Android.bp
index 6c6b304..ac50bfa 100644
--- a/services/core/jni/gnss/Android.bp
+++ b/services/core/jni/gnss/Android.bp
@@ -27,6 +27,8 @@
"GnssBatching.cpp",
"GnssBatchingCallback.cpp",
"GnssConfiguration.cpp",
+ "GnssGeofence.cpp",
+ "GnssGeofenceCallback.cpp",
"GnssMeasurement.cpp",
"GnssMeasurementCallback.cpp",
"GnssNavigationMessage.cpp",
diff --git a/services/core/jni/gnss/GnssGeofence.cpp b/services/core/jni/gnss/GnssGeofence.cpp
new file mode 100644
index 0000000..01d134d
--- /dev/null
+++ b/services/core/jni/gnss/GnssGeofence.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+// Define LOG_TAG before <log/log.h> to overwrite the default value.
+#define LOG_TAG "GnssGeofenceJni"
+
+#include "GnssGeofence.h"
+
+#include "Utils.h"
+
+using android::hardware::hidl_bitfield;
+using GeofenceTransition = android::hardware::gnss::V1_0::IGnssGeofenceCallback::GeofenceTransition;
+using IGnssGeofenceAidl = android::hardware::gnss::IGnssGeofence;
+using IGnssGeofenceHidl = android::hardware::gnss::V1_0::IGnssGeofencing;
+
+namespace android::gnss {
+
+// Implementation of GnssGeofence (AIDL HAL)
+
+GnssGeofenceAidl::GnssGeofenceAidl(const sp<IGnssGeofenceAidl>& iGnssGeofence)
+ : mIGnssGeofenceAidl(iGnssGeofence) {
+ assert(mIGnssGeofenceAidl != nullptr);
+}
+
+jboolean GnssGeofenceAidl::setCallback(const std::unique_ptr<GnssGeofenceCallback>& callback) {
+ auto status = mIGnssGeofenceAidl->setCallback(callback->getAidl());
+ return checkAidlStatus(status, "IGnssGeofenceAidl init() failed.");
+}
+
+jboolean GnssGeofenceAidl::addGeofence(int geofenceId, double latitudeDegrees,
+ double longitudeDegrees, double radiusMeters,
+ int lastTransition, int monitorTransitions,
+ int notificationResponsivenessMs, int unknownTimerMs) {
+ auto status = mIGnssGeofenceAidl->addGeofence(geofenceId, latitudeDegrees, longitudeDegrees,
+ radiusMeters, lastTransition, monitorTransitions,
+ notificationResponsivenessMs, unknownTimerMs);
+ return checkAidlStatus(status, "IGnssGeofenceAidl addGeofence() failed");
+}
+
+jboolean GnssGeofenceAidl::removeGeofence(int geofenceId) {
+ auto status = mIGnssGeofenceAidl->removeGeofence(geofenceId);
+ return checkAidlStatus(status, "IGnssGeofenceAidl removeGeofence() failed.");
+}
+
+jboolean GnssGeofenceAidl::pauseGeofence(int geofenceId) {
+ auto status = mIGnssGeofenceAidl->pauseGeofence(geofenceId);
+ return checkAidlStatus(status, "IGnssGeofenceAidl pauseGeofence() failed.");
+}
+
+jboolean GnssGeofenceAidl::resumeGeofence(int geofenceId, int monitorTransitions) {
+ auto status = mIGnssGeofenceAidl->resumeGeofence(geofenceId, monitorTransitions);
+ return checkAidlStatus(status, "IGnssGeofenceAidl resumeGeofence() failed.");
+}
+
+// Implementation of GnssGeofenceHidl
+
+GnssGeofenceHidl::GnssGeofenceHidl(const sp<IGnssGeofenceHidl>& iGnssGeofence)
+ : mIGnssGeofenceHidl(iGnssGeofence) {
+ assert(mIGnssGeofenceHidl != nullptr);
+}
+
+jboolean GnssGeofenceHidl::setCallback(const std::unique_ptr<GnssGeofenceCallback>& callback) {
+ auto result = mIGnssGeofenceHidl->setCallback(callback->getHidl());
+ return checkHidlReturn(result, "IGnssGeofenceHidl setCallback() failed.");
+}
+
+jboolean GnssGeofenceHidl::addGeofence(int geofenceId, double latitudeDegrees,
+ double longitudeDegrees, double radiusMeters,
+ int lastTransition, int monitorTransitions,
+ int notificationResponsivenessMs, int unknownTimerMs) {
+ auto result = mIGnssGeofenceHidl->addGeofence(geofenceId, latitudeDegrees, longitudeDegrees,
+ radiusMeters,
+ static_cast<GeofenceTransition>(lastTransition),
+ static_cast<hidl_bitfield<GeofenceTransition>>(
+ monitorTransitions),
+ notificationResponsivenessMs, unknownTimerMs);
+ return checkHidlReturn(result, "IGnssGeofence addGeofence() failed.");
+}
+
+jboolean GnssGeofenceHidl::removeGeofence(int geofenceId) {
+ auto result = mIGnssGeofenceHidl->removeGeofence(geofenceId);
+ return checkHidlReturn(result, "IGnssGeofence removeGeofence() failed.");
+}
+
+jboolean GnssGeofenceHidl::pauseGeofence(int geofenceId) {
+ auto result = mIGnssGeofenceHidl->pauseGeofence(geofenceId);
+ return checkHidlReturn(result, "IGnssGeofence pauseGeofence() failed.");
+}
+
+jboolean GnssGeofenceHidl::resumeGeofence(int geofenceId, int monitorTransitions) {
+ auto result = mIGnssGeofenceHidl->resumeGeofence(geofenceId, monitorTransitions);
+ return checkHidlReturn(result, "IGnssGeofence resumeGeofence() failed.");
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssGeofence.h b/services/core/jni/gnss/GnssGeofence.h
new file mode 100644
index 0000000..31478ea
--- /dev/null
+++ b/services/core/jni/gnss/GnssGeofence.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSGEOFENCE_H
+#define _ANDROID_SERVER_GNSS_GNSSGEOFENCE_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IGnssGeofencing.h>
+#include <android/hardware/gnss/BnGnssGeofence.h>
+#include <log/log.h>
+
+#include "GnssGeofenceCallback.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+class GnssGeofenceInterface {
+public:
+ virtual ~GnssGeofenceInterface() {}
+ virtual jboolean setCallback(const std::unique_ptr<GnssGeofenceCallback>& callback);
+ virtual jboolean addGeofence(int geofenceId, double latitudeDegrees, double longitudeDegrees,
+ double radiusMeters, int lastTransition, int monitorTransitions,
+ int notificationResponsivenessMs, int unknownTimerMs);
+ virtual jboolean pauseGeofence(int geofenceId);
+ virtual jboolean resumeGeofence(int geofenceId, int monitorTransitions);
+ virtual jboolean removeGeofence(int geofenceId);
+};
+
+class GnssGeofenceAidl : public GnssGeofenceInterface {
+public:
+ GnssGeofenceAidl(const sp<android::hardware::gnss::IGnssGeofence>& iGnssGeofence);
+ jboolean setCallback(const std::unique_ptr<GnssGeofenceCallback>& callback) override;
+ jboolean addGeofence(int geofenceId, double latitudeDegrees, double longitudeDegrees,
+ double radiusMeters, int lastTransition, int monitorTransitions,
+ int notificationResponsivenessMs, int unknownTimerMs) override;
+ jboolean pauseGeofence(int geofenceId) override;
+ jboolean resumeGeofence(int geofenceId, int monitorTransitions) override;
+ jboolean removeGeofence(int geofenceId) override;
+
+private:
+ const sp<android::hardware::gnss::IGnssGeofence> mIGnssGeofenceAidl;
+};
+
+class GnssGeofenceHidl : public GnssGeofenceInterface {
+public:
+ GnssGeofenceHidl(const sp<android::hardware::gnss::V1_0::IGnssGeofencing>& iGnssGeofence);
+ jboolean setCallback(const std::unique_ptr<GnssGeofenceCallback>& callback) override;
+ jboolean addGeofence(int geofenceId, double latitudeDegrees, double longitudeDegrees,
+ double radiusMeters, int lastTransition, int monitorTransitions,
+ int notificationResponsivenessMs, int unknownTimerMs) override;
+ jboolean pauseGeofence(int geofenceId) override;
+ jboolean resumeGeofence(int geofenceId, int monitorTransitions) override;
+ jboolean removeGeofence(int geofenceId) override;
+
+private:
+ const sp<android::hardware::gnss::V1_0::IGnssGeofencing> mIGnssGeofenceHidl;
+};
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSGEOFENCE_H
diff --git a/services/core/jni/gnss/GnssGeofenceCallback.cpp b/services/core/jni/gnss/GnssGeofenceCallback.cpp
new file mode 100644
index 0000000..2cdf973
--- /dev/null
+++ b/services/core/jni/gnss/GnssGeofenceCallback.cpp
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "GnssGeofenceCbJni"
+
+#include "GnssGeofenceCallback.h"
+
+namespace android::gnss {
+
+namespace {
+
+jmethodID method_reportGeofenceTransition;
+jmethodID method_reportGeofenceStatus;
+jmethodID method_reportGeofenceAddStatus;
+jmethodID method_reportGeofenceRemoveStatus;
+jmethodID method_reportGeofencePauseStatus;
+jmethodID method_reportGeofenceResumeStatus;
+
+} // anonymous namespace
+
+using binder::Status;
+using hardware::Return;
+using hardware::Void;
+using GeofenceAvailability =
+ android::hardware::gnss::V1_0::IGnssGeofenceCallback::GeofenceAvailability;
+using GeofenceStatus = android::hardware::gnss::V1_0::IGnssGeofenceCallback::GeofenceStatus;
+using GeofenceTransition = android::hardware::gnss::V1_0::IGnssGeofenceCallback::GeofenceTransition;
+
+using GnssLocationAidl = android::hardware::gnss::GnssLocation;
+using GnssLocation_V1_0 = android::hardware::gnss::V1_0::GnssLocation;
+
+void GnssGeofence_class_init_once(JNIEnv* env, jclass clazz) {
+ method_reportGeofenceTransition = env->GetMethodID(clazz, "reportGeofenceTransition",
+ "(ILandroid/location/Location;IJ)V");
+ method_reportGeofenceStatus =
+ env->GetMethodID(clazz, "reportGeofenceStatus", "(ILandroid/location/Location;)V");
+ method_reportGeofenceAddStatus = env->GetMethodID(clazz, "reportGeofenceAddStatus", "(II)V");
+ method_reportGeofenceRemoveStatus =
+ env->GetMethodID(clazz, "reportGeofenceRemoveStatus", "(II)V");
+ method_reportGeofenceResumeStatus =
+ env->GetMethodID(clazz, "reportGeofenceResumeStatus", "(II)V");
+ method_reportGeofencePauseStatus =
+ env->GetMethodID(clazz, "reportGeofencePauseStatus", "(II)V");
+}
+
+Status GnssGeofenceCallbackAidl::gnssGeofenceTransitionCb(int geofenceId,
+ const GnssLocationAidl& location,
+ int transition, int64_t timestampMillis) {
+ GnssGeofenceCallbackUtil::gnssGeofenceTransitionCb(geofenceId, location, transition,
+ timestampMillis);
+ return Status::ok();
+}
+
+Status GnssGeofenceCallbackAidl::gnssGeofenceStatusCb(int availability,
+ const GnssLocationAidl& lastLocation) {
+ GnssGeofenceCallbackUtil::gnssGeofenceStatusCb(availability, lastLocation);
+ return Status::ok();
+}
+
+Status GnssGeofenceCallbackAidl::gnssGeofenceAddCb(int geofenceId, int status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceAddCb(geofenceId, status);
+ return Status::ok();
+}
+
+Status GnssGeofenceCallbackAidl::gnssGeofenceRemoveCb(int geofenceId, int status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceRemoveCb(geofenceId, status);
+ return Status::ok();
+}
+
+Status GnssGeofenceCallbackAidl::gnssGeofencePauseCb(int geofenceId, int status) {
+ GnssGeofenceCallbackUtil::gnssGeofencePauseCb(geofenceId, status);
+ return Status::ok();
+}
+
+Status GnssGeofenceCallbackAidl::gnssGeofenceResumeCb(int geofenceId, int status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceResumeCb(geofenceId, status);
+ return Status::ok();
+}
+
+Return<void> GnssGeofenceCallbackHidl::gnssGeofenceTransitionCb(
+ int32_t geofenceId, const GnssLocation_V1_0& location, GeofenceTransition transition,
+ hardware::gnss::V1_0::GnssUtcTime timestamp) {
+ GnssGeofenceCallbackUtil::gnssGeofenceTransitionCb(geofenceId, location, (int)transition,
+ (int64_t)timestamp);
+ return Void();
+}
+
+Return<void> GnssGeofenceCallbackHidl::gnssGeofenceStatusCb(GeofenceAvailability availability,
+ const GnssLocation_V1_0& location) {
+ GnssGeofenceCallbackUtil::gnssGeofenceStatusCb((int)availability, location);
+ return Void();
+}
+
+Return<void> GnssGeofenceCallbackHidl::gnssGeofenceAddCb(int32_t geofenceId,
+ GeofenceStatus status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceAddCb(geofenceId, (int)status);
+ return Void();
+}
+
+Return<void> GnssGeofenceCallbackHidl::gnssGeofenceRemoveCb(int32_t geofenceId,
+ GeofenceStatus status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceRemoveCb(geofenceId, (int)status);
+ return Void();
+}
+
+Return<void> GnssGeofenceCallbackHidl::gnssGeofencePauseCb(int32_t geofenceId,
+ GeofenceStatus status) {
+ GnssGeofenceCallbackUtil::gnssGeofencePauseCb(geofenceId, (int)status);
+ return Void();
+}
+
+Return<void> GnssGeofenceCallbackHidl::gnssGeofenceResumeCb(int32_t geofenceId,
+ GeofenceStatus status) {
+ GnssGeofenceCallbackUtil::gnssGeofenceResumeCb(geofenceId, (int)status);
+ return Void();
+}
+
+void GnssGeofenceCallbackUtil::gnssGeofenceAddCb(int geofenceId, int status) {
+ JNIEnv* env = getJniEnv();
+ if (status != hardware::gnss::IGnssGeofenceCallback::OPERATION_SUCCESS) {
+ ALOGE("%s: Error in adding a Geofence: %d\n", __func__, status);
+ }
+
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceAddStatus, geofenceId, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void GnssGeofenceCallbackUtil::gnssGeofenceRemoveCb(int geofenceId, int status) {
+ JNIEnv* env = getJniEnv();
+ if (status != hardware::gnss::IGnssGeofenceCallback::OPERATION_SUCCESS) {
+ ALOGE("%s: Error in removing a Geofence: %d\n", __func__, status);
+ }
+
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceRemoveStatus, geofenceId, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void GnssGeofenceCallbackUtil::gnssGeofencePauseCb(int geofenceId, int status) {
+ JNIEnv* env = getJniEnv();
+ if (status != hardware::gnss::IGnssGeofenceCallback::OPERATION_SUCCESS) {
+ ALOGE("%s: Error in pausing Geofence: %d\n", __func__, status);
+ }
+
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofencePauseStatus, geofenceId, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void GnssGeofenceCallbackUtil::gnssGeofenceResumeCb(int geofenceId, int status) {
+ JNIEnv* env = getJniEnv();
+ if (status != hardware::gnss::IGnssGeofenceCallback::OPERATION_SUCCESS) {
+ ALOGE("%s: Error in resuming Geofence: %d\n", __func__, status);
+ }
+
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceResumeStatus, geofenceId, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+} // namespace android::gnss
diff --git a/services/core/jni/gnss/GnssGeofenceCallback.h b/services/core/jni/gnss/GnssGeofenceCallback.h
new file mode 100644
index 0000000..b6a8a36
--- /dev/null
+++ b/services/core/jni/gnss/GnssGeofenceCallback.h
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_SERVER_GNSS_GNSSGEOFENCECALLBACK_H
+#define _ANDROID_SERVER_GNSS_GNSSGEOFENCECALLBACK_H
+
+#pragma once
+
+#ifndef LOG_TAG
+#error LOG_TAG must be defined before including this file.
+#endif
+
+#include <android/hardware/gnss/1.0/IGnssGeofencing.h>
+#include <android/hardware/gnss/BnGnssGeofenceCallback.h>
+#include <log/log.h>
+
+#include <vector>
+
+#include "Utils.h"
+#include "jni.h"
+
+namespace android::gnss {
+
+namespace {
+extern jmethodID method_reportGeofenceTransition;
+extern jmethodID method_reportGeofenceStatus;
+extern jmethodID method_reportGeofenceAddStatus;
+extern jmethodID method_reportGeofenceRemoveStatus;
+extern jmethodID method_reportGeofencePauseStatus;
+extern jmethodID method_reportGeofenceResumeStatus;
+} // anonymous namespace
+
+void GnssGeofence_class_init_once(JNIEnv* env, jclass clazz);
+
+class GnssGeofenceCallbackAidl : public hardware::gnss::BnGnssGeofenceCallback {
+public:
+ GnssGeofenceCallbackAidl() {}
+ binder::Status gnssGeofenceTransitionCb(int geofenceId,
+ const hardware::gnss::GnssLocation& location,
+ int transition, int64_t timestampMillis) override;
+ binder::Status gnssGeofenceStatusCb(int availability,
+ const hardware::gnss::GnssLocation& lastLocation) override;
+ binder::Status gnssGeofenceAddCb(int geofenceId, int status) override;
+ binder::Status gnssGeofenceRemoveCb(int geofenceId, int status) override;
+ binder::Status gnssGeofencePauseCb(int geofenceId, int status) override;
+ binder::Status gnssGeofenceResumeCb(int geofenceId, int status) override;
+};
+
+class GnssGeofenceCallbackHidl : public hardware::gnss::V1_0::IGnssGeofenceCallback {
+public:
+ GnssGeofenceCallbackHidl() {}
+ hardware::Return<void> gnssGeofenceTransitionCb(
+ int32_t geofenceId, const hardware::gnss::V1_0::GnssLocation& location,
+ hardware::gnss::V1_0::IGnssGeofenceCallback::GeofenceTransition transition,
+ hardware::gnss::V1_0::GnssUtcTime timestamp) override;
+ hardware::Return<void> gnssGeofenceStatusCb(
+ hardware::gnss::V1_0::IGnssGeofenceCallback::GeofenceAvailability status,
+ const hardware::gnss::V1_0::GnssLocation& location) override;
+ hardware::Return<void> gnssGeofenceAddCb(
+ int32_t geofenceId,
+ hardware::gnss::V1_0::IGnssGeofenceCallback::GeofenceStatus status) override;
+ hardware::Return<void> gnssGeofenceRemoveCb(
+ int32_t geofenceId,
+ hardware::gnss::V1_0::IGnssGeofenceCallback::GeofenceStatus status) override;
+ hardware::Return<void> gnssGeofencePauseCb(
+ int32_t geofenceId,
+ hardware::gnss::V1_0::IGnssGeofenceCallback::GeofenceStatus status) override;
+ hardware::Return<void> gnssGeofenceResumeCb(
+ int32_t geofenceId,
+ hardware::gnss::V1_0::IGnssGeofenceCallback::GeofenceStatus status) override;
+};
+
+class GnssGeofenceCallback {
+public:
+ GnssGeofenceCallback() {}
+ sp<GnssGeofenceCallbackAidl> getAidl() {
+ if (callbackAidl == nullptr) {
+ callbackAidl = sp<GnssGeofenceCallbackAidl>::make();
+ }
+ return callbackAidl;
+ }
+
+ sp<GnssGeofenceCallbackHidl> getHidl() {
+ if (callbackHidl == nullptr) {
+ callbackHidl = sp<GnssGeofenceCallbackHidl>::make();
+ }
+ return callbackHidl;
+ }
+
+private:
+ sp<GnssGeofenceCallbackAidl> callbackAidl;
+ sp<GnssGeofenceCallbackHidl> callbackHidl;
+};
+
+/** Util class for GnssGeofenceCallback methods. */
+struct GnssGeofenceCallbackUtil {
+ template <class T>
+ static void gnssGeofenceTransitionCb(int geofenceId, const T& location, int transition,
+ int64_t timestampMillis);
+ template <class T>
+ static void gnssGeofenceStatusCb(int availability, const T& lastLocation);
+ static void gnssGeofenceAddCb(int geofenceId, int status);
+ static void gnssGeofenceRemoveCb(int geofenceId, int status);
+ static void gnssGeofencePauseCb(int geofenceId, int status);
+ static void gnssGeofenceResumeCb(int geofenceId, int status);
+
+private:
+ GnssGeofenceCallbackUtil() = delete;
+};
+
+template <class T>
+void GnssGeofenceCallbackUtil::gnssGeofenceTransitionCb(int geofenceId, const T& location,
+ int transition, int64_t timestamp) {
+ JNIEnv* env = getJniEnv();
+
+ jobject jLocation = translateGnssLocation(env, location);
+
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceTransition, geofenceId, jLocation,
+ transition, timestamp);
+
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ env->DeleteLocalRef(jLocation);
+}
+
+template <class T>
+void GnssGeofenceCallbackUtil::gnssGeofenceStatusCb(int availability, const T& lastLocation) {
+ JNIEnv* env = getJniEnv();
+
+ jobject jLocation = translateGnssLocation(env, lastLocation);
+
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceStatus, availability, jLocation);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+ env->DeleteLocalRef(jLocation);
+}
+
+} // namespace android::gnss
+
+#endif // _ANDROID_SERVER_GNSS_GNSSGEOFENCECALLBACK_H
\ No newline at end of file
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index f605fe8..98a7b5e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1833,6 +1833,8 @@
mUserManagerInternal.addUserLifecycleListener(new UserLifecycleListener());
loadOwners();
+
+ performPolicyVersionUpgrade();
}
/**
@@ -3159,7 +3161,6 @@
synchronized (getLockObject()) {
migrateUserRestrictionsIfNecessaryLocked();
fixupAutoTimeRestrictionDuringOrganizationOwnedDeviceMigration();
- performPolicyVersionUpgrade();
}
getUserData(UserHandle.USER_SYSTEM);
cleanUpOldUsers();
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 1ef9d13..50a0a68 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
@@ -1853,6 +1854,36 @@
@SuppressWarnings("GuardedBy")
@Test
+ public void testUpdateOomAdj_DoAll_BoundByPersService_Cycle_Branch_Capability() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+ ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+ MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+ bindService(app, client, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class));
+ ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
+ MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
+ bindService(client, client2, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class));
+ bindService(client2, app, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class));
+ ProcessRecord client3 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
+ MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
+ client3.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+ bindService(app, client3, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class));
+ ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP();
+ lru.clear();
+ lru.add(app);
+ lru.add(client);
+ lru.add(client2);
+ lru.add(client3);
+ sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+
+ assertEquals(PROCESS_CAPABILITY_ALL, client.mState.getSetCapability());
+ assertEquals(PROCESS_CAPABILITY_ALL, client2.mState.getSetCapability());
+ assertEquals(PROCESS_CAPABILITY_ALL, app.mState.getSetCapability());
+ }
+
+ @SuppressWarnings("GuardedBy")
+ @Test
public void testUpdateOomAdj_DoAll_Provider_Cycle_Branch_2() {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
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 205c3da..74dd291 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationProcessorTest.java
@@ -16,8 +16,8 @@
package com.android.server.accessibility;
-import static android.accessibilityservice.MagnificationConfig.FULLSCREEN_MODE;
-import static android.accessibilityservice.MagnificationConfig.WINDOW_MODE;
+import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
+import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_WINDOW;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -82,7 +82,7 @@
@Test
public void getScale_fullscreenMode_expectedValue() {
final MagnificationConfig config = new MagnificationConfig.Builder()
- .setMode(FULLSCREEN_MODE)
+ .setMode(MAGNIFICATION_MODE_FULLSCREEN)
.setScale(TEST_SCALE).build();
setMagnificationActivated(TEST_DISPLAY, config);
@@ -94,7 +94,7 @@
@Test
public void getScale_windowMode_expectedValue() {
final MagnificationConfig config = new MagnificationConfig.Builder()
- .setMode(WINDOW_MODE)
+ .setMode(MAGNIFICATION_MODE_WINDOW)
.setScale(TEST_SCALE).build();
setMagnificationActivated(TEST_DISPLAY, config);
@@ -106,7 +106,7 @@
@Test
public void getCenterX_canControlFullscreenMagnification_returnCenterX() {
final MagnificationConfig config = new MagnificationConfig.Builder()
- .setMode(FULLSCREEN_MODE)
+ .setMode(MAGNIFICATION_MODE_FULLSCREEN)
.setCenterX(TEST_CENTER_X).build();
setMagnificationActivated(TEST_DISPLAY, config);
@@ -119,7 +119,7 @@
@Test
public void getCenterX_canControlWindowMagnification_returnCenterX() {
final MagnificationConfig config = new MagnificationConfig.Builder()
- .setMode(WINDOW_MODE)
+ .setMode(MAGNIFICATION_MODE_WINDOW)
.setCenterX(TEST_CENTER_X).build();
setMagnificationActivated(TEST_DISPLAY, config);
@@ -132,7 +132,7 @@
@Test
public void getCenterY_canControlFullscreenMagnification_returnCenterY() {
final MagnificationConfig config = new MagnificationConfig.Builder()
- .setMode(FULLSCREEN_MODE)
+ .setMode(MAGNIFICATION_MODE_FULLSCREEN)
.setCenterY(TEST_CENTER_Y).build();
setMagnificationActivated(TEST_DISPLAY, config);
@@ -145,7 +145,7 @@
@Test
public void getCenterY_canControlWindowMagnification_returnCenterY() {
final MagnificationConfig config = new MagnificationConfig.Builder()
- .setMode(WINDOW_MODE)
+ .setMode(MAGNIFICATION_MODE_WINDOW)
.setCenterY(TEST_CENTER_Y).build();
setMagnificationActivated(TEST_DISPLAY, config);
@@ -158,7 +158,7 @@
@Test
public void getMagnificationRegion_canControlFullscreenMagnification_returnRegion() {
final Region region = new Region(10, 20, 100, 200);
- setMagnificationActivated(TEST_DISPLAY, FULLSCREEN_MODE);
+ setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
mMagnificationProcessor.getMagnificationRegion(TEST_DISPLAY,
region, /* canControlMagnification= */true);
@@ -169,7 +169,7 @@
@Test
public void getMagnificationRegion_canControlWindowMagnification_returnRegion() {
final Region region = new Region(10, 20, 100, 200);
- setMagnificationActivated(TEST_DISPLAY, WINDOW_MODE);
+ setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_WINDOW);
mMagnificationProcessor.getMagnificationRegion(TEST_DISPLAY,
region, /* canControlMagnification= */true);
@@ -180,7 +180,7 @@
@Test
public void getMagnificationRegion_fullscreenModeNotRegistered_shouldRegisterThenUnregister() {
final Region region = new Region(10, 20, 100, 200);
- setMagnificationActivated(TEST_DISPLAY, FULLSCREEN_MODE);
+ setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
doAnswer((invocation) -> {
((Region) invocation.getArguments()[1]).set(region);
return null;
@@ -198,7 +198,7 @@
@Test
public void getMagnificationCenterX_fullscreenModeNotRegistered_shouldRegisterThenUnregister() {
final MagnificationConfig config = new MagnificationConfig.Builder()
- .setMode(FULLSCREEN_MODE)
+ .setMode(MAGNIFICATION_MODE_FULLSCREEN)
.setCenterX(TEST_CENTER_X).build();
setMagnificationActivated(TEST_DISPLAY, config);
@@ -212,7 +212,7 @@
@Test
public void getMagnificationCenterY_fullscreenModeNotRegistered_shouldRegisterThenUnregister() {
final MagnificationConfig config = new MagnificationConfig.Builder()
- .setMode(FULLSCREEN_MODE)
+ .setMode(MAGNIFICATION_MODE_FULLSCREEN)
.setCenterY(TEST_CENTER_Y).build();
setMagnificationActivated(TEST_DISPLAY, config);
@@ -225,17 +225,17 @@
@Test
public void getCurrentMode_configDefaultMode_returnActivatedMode() {
- final int targetMode = WINDOW_MODE;
+ final int targetMode = MAGNIFICATION_MODE_WINDOW;
setMagnificationActivated(TEST_DISPLAY, targetMode);
int currentMode = mMagnificationProcessor.getControllingMode(TEST_DISPLAY);
- assertEquals(WINDOW_MODE, currentMode);
+ assertEquals(MAGNIFICATION_MODE_WINDOW, currentMode);
}
@Test
public void reset_fullscreenMagnificationActivated() {
- setMagnificationActivated(TEST_DISPLAY, FULLSCREEN_MODE);
+ setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_FULLSCREEN);
mMagnificationProcessor.reset(TEST_DISPLAY, /* animate= */false);
@@ -244,7 +244,7 @@
@Test
public void reset_windowMagnificationActivated() {
- setMagnificationActivated(TEST_DISPLAY, WINDOW_MODE);
+ setMagnificationActivated(TEST_DISPLAY, MAGNIFICATION_MODE_WINDOW);
mMagnificationProcessor.reset(TEST_DISPLAY, /* animate= */false);
@@ -254,7 +254,7 @@
@Test
public void setMagnificationConfig_fullscreenModeNotRegistered_shouldRegister() {
final MagnificationConfig config = new MagnificationConfig.Builder()
- .setMode(FULLSCREEN_MODE)
+ .setMode(MAGNIFICATION_MODE_FULLSCREEN)
.setScale(TEST_SCALE)
.setCenterX(TEST_CENTER_X)
.setCenterY(TEST_CENTER_Y).build();
@@ -269,7 +269,7 @@
@Test
public void setMagnificationConfig_windowMode_enableMagnification() {
final MagnificationConfig config = new MagnificationConfig.Builder()
- .setMode(WINDOW_MODE)
+ .setMode(MAGNIFICATION_MODE_WINDOW)
.setScale(TEST_SCALE)
.setCenterX(TEST_CENTER_X)
.setCenterY(TEST_CENTER_Y).build();
@@ -284,7 +284,7 @@
@Test
public void setMagnificationConfig_fullscreenEnabled_expectedConfigValues() {
final MagnificationConfig config = new MagnificationConfig.Builder()
- .setMode(FULLSCREEN_MODE)
+ .setMode(MAGNIFICATION_MODE_FULLSCREEN)
.setScale(TEST_SCALE)
.setCenterX(TEST_CENTER_X)
.setCenterY(TEST_CENTER_Y).build();
@@ -302,7 +302,7 @@
@Test
public void setMagnificationConfig_windowEnabled_expectedConfigValues() {
final MagnificationConfig config = new MagnificationConfig.Builder()
- .setMode(WINDOW_MODE)
+ .setMode(MAGNIFICATION_MODE_WINDOW)
.setScale(TEST_SCALE)
.setCenterX(TEST_CENTER_X)
.setCenterY(TEST_CENTER_Y).build();
@@ -319,8 +319,8 @@
@Test
public void setMagnificationConfig_controllingModeChangeAndAnimating_transitionConfigMode() {
- final int currentActivatedMode = WINDOW_MODE;
- final int targetMode = FULLSCREEN_MODE;
+ final int currentActivatedMode = MAGNIFICATION_MODE_WINDOW;
+ final int targetMode = MAGNIFICATION_MODE_FULLSCREEN;
final MagnificationConfig oldConfig = new MagnificationConfig.Builder()
.setMode(currentActivatedMode)
.setScale(TEST_SCALE)
@@ -354,15 +354,15 @@
when(mMockMagnificationController.isActivated(displayId, config.getMode())).thenReturn(
true);
mMagnificationProcessor.setMagnificationConfig(displayId, config, false, SERVICE_ID);
- if (config.getMode() == FULLSCREEN_MODE) {
- when(mMockMagnificationController.isActivated(displayId, WINDOW_MODE)).thenReturn(
- false);
+ if (config.getMode() == MAGNIFICATION_MODE_FULLSCREEN) {
+ when(mMockMagnificationController.isActivated(displayId,
+ MAGNIFICATION_MODE_WINDOW)).thenReturn(false);
mFullScreenMagnificationControllerStub.resetAndStubMethods();
mMockFullScreenMagnificationController.setScaleAndCenter(displayId, config.getScale(),
config.getCenterX(), config.getCenterY(), true, SERVICE_ID);
- } else if (config.getMode() == WINDOW_MODE) {
- when(mMockMagnificationController.isActivated(displayId, FULLSCREEN_MODE)).thenReturn(
- false);
+ } else if (config.getMode() == MAGNIFICATION_MODE_WINDOW) {
+ when(mMockMagnificationController.isActivated(displayId,
+ MAGNIFICATION_MODE_FULLSCREEN)).thenReturn(false);
mWindowMagnificationManagerStub.resetAndStubMethods();
mMockWindowMagnificationManager.enableWindowMagnification(displayId, config.getScale(),
config.getCenterX(), config.getCenterY());
diff --git a/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java b/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java
new file mode 100644
index 0000000..41c7e31
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/policy/SideFpsEventHandlerTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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.policy;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AlertDialog;
+import android.content.pm.PackageManager;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.hardware.fingerprint.FingerprintStateListener;
+import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.view.Window;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+import java.util.List;
+
+/**
+ * Unit tests for {@link SideFpsEventHandler}.
+ * <p/>
+ * Run with <code>atest SideFpsEventHandlerTest</code>.
+ */
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class SideFpsEventHandlerTest {
+
+ private static final List<Integer> sAllStates = List.of(
+ FingerprintStateListener.STATE_IDLE,
+ FingerprintStateListener.STATE_ENROLLING,
+ FingerprintStateListener.STATE_KEYGUARD_AUTH,
+ FingerprintStateListener.STATE_BP_AUTH,
+ FingerprintStateListener.STATE_AUTH_OTHER);
+
+ @Rule
+ public TestableContext mContext =
+ new TestableContext(InstrumentationRegistry.getContext(), null);
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private FingerprintManager mFingerprintManager;
+ @Spy
+ private AlertDialog.Builder mDialogBuilder = new AlertDialog.Builder(mContext);
+ @Mock
+ private AlertDialog mAlertDialog;
+ @Mock
+ private Window mWindow;
+
+ private TestLooper mLooper = new TestLooper();
+ private SideFpsEventHandler mEventHandler;
+ private FingerprintStateListener mFingerprintStateListener;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext.addMockSystemService(PackageManager.class, mPackageManager);
+ mContext.addMockSystemService(FingerprintManager.class, mFingerprintManager);
+
+ when(mDialogBuilder.create()).thenReturn(mAlertDialog);
+ when(mAlertDialog.getWindow()).thenReturn(mWindow);
+
+ mEventHandler = new SideFpsEventHandler(
+ mContext, new Handler(mLooper.getLooper()),
+ mContext.getSystemService(PowerManager.class), () -> mDialogBuilder);
+ }
+
+ @Test
+ public void ignoresWithoutFingerprintFeature() {
+ when(mPackageManager.hasSystemFeature(eq(PackageManager.FEATURE_FINGERPRINT)))
+ .thenReturn(false);
+
+ assertThat(mEventHandler.onSinglePressDetected(60L)).isFalse();
+
+ mLooper.dispatchAll();
+ verify(mAlertDialog, never()).show();
+ }
+
+ @Test
+ public void ignoresWithoutSfps() throws Exception {
+ setupWithSensor(false /* hasSfps */, true /* initialized */);
+
+ for (int state : sAllStates) {
+ setFingerprintState(state);
+ assertThat(mEventHandler.onSinglePressDetected(200L)).isFalse();
+
+ mLooper.dispatchAll();
+ verify(mAlertDialog, never()).show();
+ }
+ }
+
+ @Test
+ public void ignoresWhileWaitingForSfps() throws Exception {
+ setupWithSensor(true /* hasSfps */, false /* initialized */);
+
+ for (int state : sAllStates) {
+ setFingerprintState(state);
+ assertThat(mEventHandler.onSinglePressDetected(400L)).isFalse();
+
+ mLooper.dispatchAll();
+ verify(mAlertDialog, never()).show();
+ }
+ }
+
+ @Test
+ public void ignoresWhenIdleOrUnknown() throws Exception {
+ setupWithSensor(true /* hasSfps */, true /* initialized */);
+
+ setFingerprintState(FingerprintStateListener.STATE_IDLE);
+ assertThat(mEventHandler.onSinglePressDetected(80000L)).isFalse();
+
+ setFingerprintState(FingerprintStateListener.STATE_AUTH_OTHER);
+ assertThat(mEventHandler.onSinglePressDetected(90000L)).isFalse();
+
+ mLooper.dispatchAll();
+ verify(mAlertDialog, never()).show();
+ }
+
+ @Test
+ public void ignoresOnKeyguard() throws Exception {
+ setupWithSensor(true /* hasSfps */, true /* initialized */);
+
+ setFingerprintState(FingerprintStateListener.STATE_KEYGUARD_AUTH);
+ assertThat(mEventHandler.onSinglePressDetected(80000L)).isFalse();
+
+ mLooper.dispatchAll();
+ verify(mAlertDialog, never()).show();
+ }
+
+ @Test
+ public void promptsWhenBPisActive() throws Exception {
+ setupWithSensor(true /* hasSfps */, true /* initialized */);
+
+ setFingerprintState(FingerprintStateListener.STATE_BP_AUTH);
+ assertThat(mEventHandler.onSinglePressDetected(80000L)).isTrue();
+
+ mLooper.dispatchAll();
+ verify(mAlertDialog).show();
+ }
+
+ @Test
+ public void promptsWhenEnrolling() throws Exception {
+ setupWithSensor(true /* hasSfps */, true /* initialized */);
+
+ setFingerprintState(FingerprintStateListener.STATE_ENROLLING);
+ assertThat(mEventHandler.onSinglePressDetected(80000L)).isTrue();
+
+ mLooper.dispatchAll();
+ verify(mAlertDialog).show();
+ }
+
+ private void setFingerprintState(@FingerprintStateListener.State int newState) {
+ if (mFingerprintStateListener != null) {
+ mFingerprintStateListener.onStateChanged(newState);
+ mLooper.dispatchAll();
+ }
+ }
+
+ private void setupWithSensor(boolean hasSfps, boolean initialized) throws Exception {
+ when(mPackageManager.hasSystemFeature(eq(PackageManager.FEATURE_FINGERPRINT)))
+ .thenReturn(true);
+ when(mFingerprintManager.isPowerbuttonFps()).thenReturn(hasSfps);
+ mEventHandler.onFingerprintSensorReady();
+
+ ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> fpCallbackCaptor =
+ ArgumentCaptor.forClass(IFingerprintAuthenticatorsRegisteredCallback.class);
+ verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(fpCallbackCaptor.capture());
+ if (initialized) {
+ fpCallbackCaptor.getValue().onAllAuthenticatorsRegistered(
+ List.of(mock(FingerprintSensorPropertiesInternal.class)));
+ if (hasSfps) {
+ ArgumentCaptor<FingerprintStateListener> captor = ArgumentCaptor.forClass(
+ FingerprintStateListener.class);
+ verify(mFingerprintManager).registerFingerprintStateListener(captor.capture());
+ mFingerprintStateListener = captor.getValue();
+ }
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index beee2a7..ab9fbb5 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -32,6 +32,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
@@ -52,6 +53,7 @@
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.UserHandle;
+import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
@@ -124,10 +126,11 @@
new Handler(mTestLooper.getLooper()));
mVibrationSettings.onSystemReady();
+ // Simulate System defaults.
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0);
- setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
+ setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
- setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
+ setRingerMode(AudioManager.RINGER_MODE_NORMAL);
}
@After
@@ -142,13 +145,12 @@
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
- setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_ALARMS);
setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
- verify(mListenerMock, times(8)).onChange();
+ verify(mListenerMock, times(7)).onChange();
}
@Test
@@ -173,126 +175,242 @@
verifyNoMoreInteractions(mListenerMock);
setUserSetting(Settings.System.VIBRATE_INPUT_DEVICES, 1);
- setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_ALARMS);
}
@Test
- public void shouldVibrateForRingerMode_beforeSystemReady_returnsFalseOnlyForRingtone() {
- setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
- setRingerMode(AudioManager.RINGER_MODE_MAX);
- VibrationSettings vibrationSettings = new VibrationSettings(mContextSpy,
- new Handler(mTestLooper.getLooper()));
-
- assertFalse(vibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_ALARM));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_TOUCH));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_NOTIFICATION));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_COMMUNICATION_REQUEST));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_HARDWARE_FEEDBACK));
- }
-
- @Test
- public void shouldVibrateForRingerMode_withoutRingtoneUsage_returnsTrue() {
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_ALARM));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_TOUCH));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_NOTIFICATION));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_COMMUNICATION_REQUEST));
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_HARDWARE_FEEDBACK));
- }
-
- @Test
- public void shouldVibrateForRingerMode_withVibrateWhenRinging_ignoreSettingsForSilentMode() {
- setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
-
- setRingerMode(AudioManager.RINGER_MODE_SILENT);
- assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
-
- setRingerMode(AudioManager.RINGER_MODE_MAX);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
-
- setRingerMode(AudioManager.RINGER_MODE_NORMAL);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
-
- setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
- }
-
- @Test
- public void shouldVibrateForRingerMode_withApplyRampingRinger_ignoreSettingsForSilentMode() {
- setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
- setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
-
- setRingerMode(AudioManager.RINGER_MODE_SILENT);
- assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
-
- setRingerMode(AudioManager.RINGER_MODE_MAX);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
-
- setRingerMode(AudioManager.RINGER_MODE_NORMAL);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
-
- setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
- }
-
- @Test
- public void shouldVibrateForRingerMode_withAllSettingsOff_onlyVibratesForVibrateMode() {
- setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
- setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
-
- setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
- assertTrue(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
-
- setRingerMode(AudioManager.RINGER_MODE_SILENT);
- assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
-
- setRingerMode(AudioManager.RINGER_MODE_MAX);
- assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
-
- setRingerMode(AudioManager.RINGER_MODE_NORMAL);
- assertFalse(mVibrationSettings.shouldVibrateForRingerMode(USAGE_RINGTONE));
- }
-
- @Test
- public void shouldVibrateForUid_withForegroundOnlyUsage_returnsTrueWhInForeground() {
- assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_TOUCH));
+ public void shouldIgnoreVibration_fromBackground_doesNotIgnoreUsagesFromAllowlist() {
+ int[] expectedAllowedVibrations = new int[] {
+ USAGE_RINGTONE,
+ USAGE_ALARM,
+ USAGE_NOTIFICATION,
+ USAGE_COMMUNICATION_REQUEST,
+ USAGE_HARDWARE_FEEDBACK,
+ USAGE_PHYSICAL_EMULATION,
+ };
mVibrationSettings.mUidObserver.onUidStateChanged(
UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
- assertFalse(mVibrationSettings.shouldVibrateForUid(UID, USAGE_TOUCH));
+
+ for (int usage : expectedAllowedVibrations) {
+ assertNull("Error for usage " + VibrationAttributes.usageToString(usage),
+ mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(usage)));
+ }
}
@Test
- public void shouldVibrateForUid_withBackgroundAllowedUsage_returnTrue() {
+ public void shouldIgnoreVibration_fromBackground_ignoresUsagesNotInAllowlist() {
+ int[] expectedIgnoredVibrations = new int[] {
+ USAGE_TOUCH,
+ USAGE_UNKNOWN,
+ };
+
mVibrationSettings.mUidObserver.onUidStateChanged(
UID, ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND, 0, 0);
- assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_ALARM));
- assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_COMMUNICATION_REQUEST));
- assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_NOTIFICATION));
- assertTrue(mVibrationSettings.shouldVibrateForUid(UID, USAGE_RINGTONE));
+ for (int usage : expectedIgnoredVibrations) {
+ assertEquals("Error for usage " + VibrationAttributes.usageToString(usage),
+ Vibration.Status.IGNORED_BACKGROUND,
+ mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(usage)));
+ }
}
@Test
- public void shouldVibrateForPowerMode_withLowPowerAndAllowedUsage_returnTrue() {
+ public void shouldIgnoreVibration_fromForeground_allowsAnyUsage() {
+ mVibrationSettings.mUidObserver.onUidStateChanged(
+ UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, 0);
+
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_TOUCH)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_ALARM)));
+ }
+
+ @Test
+ public void shouldIgnoreVibration_inBatterySaverMode_doesNotIgnoreUsagesFromAllowlist() {
+ int[] expectedAllowedVibrations = new int[] {
+ USAGE_RINGTONE,
+ USAGE_ALARM,
+ USAGE_COMMUNICATION_REQUEST,
+ };
+
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
- assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_ALARM));
- assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_RINGTONE));
- assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_COMMUNICATION_REQUEST));
+ for (int usage : expectedAllowedVibrations) {
+ assertNull("Error for usage " + VibrationAttributes.usageToString(usage),
+ mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(usage)));
+ }
}
@Test
- public void shouldVibrateForPowerMode_withRestrictedUsage_returnsFalseWhileInLowPowerMode() {
+ public void shouldIgnoreVibration_inBatterySaverMode_ignoresUsagesNotInAllowlist() {
+ int[] expectedIgnoredVibrations = new int[] {
+ USAGE_NOTIFICATION,
+ USAGE_HARDWARE_FEEDBACK,
+ USAGE_PHYSICAL_EMULATION,
+ USAGE_TOUCH,
+ USAGE_UNKNOWN,
+ };
+
+ mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+
+ for (int usage : expectedIgnoredVibrations) {
+ assertEquals("Error for usage " + VibrationAttributes.usageToString(usage),
+ Vibration.Status.IGNORED_FOR_POWER,
+ mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(usage)));
+ }
+ }
+
+ @Test
+ public void shouldIgnoreVibration_notInBatterySaverMode_allowsAnyUsage() {
mRegisteredPowerModeListener.onLowPowerModeChanged(NORMAL_POWER_STATE);
- assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_TOUCH));
- assertTrue(mVibrationSettings.shouldVibrateForPowerMode(USAGE_NOTIFICATION));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_TOUCH)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_COMMUNICATION_REQUEST)));
+ }
- mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
+ @Test
+ public void shouldIgnoreVibration_withRingerModeSilent_ignoresRingtoneAndTouch() {
+ // Vibrating settings on are overruled by ringer mode.
+ setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
+ setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
+ setRingerMode(AudioManager.RINGER_MODE_SILENT);
- assertFalse(mVibrationSettings.shouldVibrateForPowerMode(USAGE_TOUCH));
- assertFalse(mVibrationSettings.shouldVibrateForPowerMode(USAGE_NOTIFICATION));
+ assertEquals(Vibration.Status.IGNORED_FOR_RINGER_MODE,
+ mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_RINGTONE)));
+ assertEquals(Vibration.Status.IGNORED_FOR_RINGER_MODE,
+ mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_TOUCH)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_COMMUNICATION_REQUEST)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_HARDWARE_FEEDBACK)));
+ }
+
+ @Test
+ public void shouldIgnoreVibration_withRingerModeVibrate_allowsAllVibrations() {
+ // Vibrating settings off are overruled by ringer mode.
+ setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
+ setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
+ setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
+
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_TOUCH)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_PHYSICAL_EMULATION)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_RINGTONE)));
+ }
+
+ @Test
+ public void shouldIgnoreVibration_withRingerModeNormalAndRingSettingsOff_ignoresRingtoneOnly() {
+ // Vibrating settings off are respected for normal ringer mode.
+ setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
+ setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
+ setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+
+ assertEquals(Vibration.Status.IGNORED_FOR_RINGER_MODE,
+ mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_RINGTONE)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_TOUCH)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_NOTIFICATION)));
+ }
+
+ @Test
+ public void shouldIgnoreVibration_withRingerModeNormalAndRingSettingsOn_allowsAllVibrations() {
+ setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
+ setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0);
+ setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_TOUCH)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_RINGTONE)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_ALARM)));
+ }
+
+ @Test
+ public void shouldIgnoreVibration_withRingerModeNormalAndRampingRingerOn_allowsAllVibrations() {
+ setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0);
+ setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
+ setRingerMode(AudioManager.RINGER_MODE_NORMAL);
+
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_TOUCH)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_RINGTONE)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_COMMUNICATION_REQUEST)));
+ }
+
+ @Test
+ public void shouldIgnoreVibration_withHapticFeedbackSettingsOff_ignoresTouchVibration() {
+ setUserSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+
+ assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
+ mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_TOUCH)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_RINGTONE)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_HARDWARE_FEEDBACK)));
+ }
+
+ @Test
+ public void shouldIgnoreVibration_withHardwareFeedbackSettingsOff_ignoresHardwareVibrations() {
+ setUserSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_OFF);
+
+ assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
+ mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_HARDWARE_FEEDBACK)));
+ assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
+ mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_PHYSICAL_EMULATION)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_TOUCH)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_NOTIFICATION)));
+ }
+
+ @Test
+ public void shouldIgnoreVibration_withNotificationSettingsOff_ignoresNotificationVibrations() {
+ setUserSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+
+ assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
+ mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_NOTIFICATION)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_ALARM)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_RINGTONE)));
+ }
+
+ @Test
+ public void shouldIgnoreVibration_withRingSettingsOff_ignoresRingtoneVibrations() {
+ // Vibrating settings on are overruled by ring intensity setting.
+ setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
+ setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1);
+ setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
+ setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF);
+
+ assertEquals(Vibration.Status.IGNORED_FOR_SETTINGS,
+ mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_RINGTONE)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_NOTIFICATION)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_ALARM)));
+ assertNull(mVibrationSettings.shouldIgnoreVibration(UID,
+ VibrationAttributes.createForUsage(USAGE_TOUCH)));
}
@Test
@@ -305,24 +423,6 @@
}
@Test
- public void isInZenMode_returnsSettingsValue() {
- setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
- assertFalse(mVibrationSettings.isInZenMode());
-
- setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_NO_INTERRUPTIONS);
- assertTrue(mVibrationSettings.isInZenMode());
- setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_ALARMS);
- assertTrue(mVibrationSettings.isInZenMode());
-
- setGlobalSetting(Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
- assertFalse(mVibrationSettings.isInZenMode());
-
- setGlobalSetting(Settings.Global.ZEN_MODE,
- Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
- assertTrue(mVibrationSettings.isInZenMode());
- }
-
- @Test
public void getDefaultIntensity_beforeSystemReady_returnsMediumToAllExceptAlarm() {
mFakeVibrator.setDefaultHapticFeedbackIntensity(VIBRATION_INTENSITY_HIGH);
mFakeVibrator.setDefaultNotificationVibrationIntensity(VIBRATION_INTENSITY_HIGH);
@@ -464,12 +564,6 @@
mVibrationSettings.updateSettings();
}
- private void setGlobalSetting(String settingName, int value) {
- Settings.Global.putInt(mContextSpy.getContentResolver(), settingName, value);
- // FakeSettingsProvider don't support testing triggering ContentObserver yet.
- mVibrationSettings.updateSettings();
- }
-
private void setRingerMode(int ringerMode) {
mAudioManager.setRingerModeInternal(ringerMode);
assertEquals(ringerMode, mAudioManager.getRingerModeInternal());
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index 2e5cf3c..a9fd3c9 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -840,21 +840,25 @@
when(mUm.getUsers()).thenReturn(userInfos);
// construct the permissions for each of them
- ArrayMap<Pair<Integer, String>, Boolean> permissions0 = new ArrayMap<>(),
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> permissions0 = new ArrayMap<>(),
permissions1 = new ArrayMap<>();
- permissions0.put(new Pair<>(10, "package1"), true);
- permissions0.put(new Pair<>(20, "package2"), false);
- permissions1.put(new Pair<>(11, "package1"), false);
- permissions1.put(new Pair<>(21, "package2"), true);
+ permissions0.put(new Pair<>(10, "package1"), new Pair<>(true, false));
+ permissions0.put(new Pair<>(20, "package2"), new Pair<>(false, true));
+ permissions1.put(new Pair<>(11, "package1"), new Pair<>(false, false));
+ permissions1.put(new Pair<>(21, "package2"), new Pair<>(true, true));
when(mPermissionHelper.getNotificationPermissionValues(0)).thenReturn(permissions0);
when(mPermissionHelper.getNotificationPermissionValues(1)).thenReturn(permissions1);
when(mPermissionHelper.getNotificationPermissionValues(2)).thenReturn(new ArrayMap<>());
- ArrayMap<Pair<Integer, String>, Boolean> combinedPermissions =
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> combinedPermissions =
mService.getAllUsersNotificationPermissions();
- assertTrue(combinedPermissions.get(new Pair<>(10, "package1")));
- assertFalse(combinedPermissions.get(new Pair<>(20, "package2")));
- assertFalse(combinedPermissions.get(new Pair<>(11, "package1")));
- assertTrue(combinedPermissions.get(new Pair<>(21, "package2")));
+ assertTrue(combinedPermissions.get(new Pair<>(10, "package1")).first);
+ assertFalse(combinedPermissions.get(new Pair<>(10, "package1")).second);
+ assertFalse(combinedPermissions.get(new Pair<>(20, "package2")).first);
+ assertTrue(combinedPermissions.get(new Pair<>(20, "package2")).second);
+ assertFalse(combinedPermissions.get(new Pair<>(11, "package1")).first);
+ assertFalse(combinedPermissions.get(new Pair<>(11, "package1")).second);
+ assertTrue(combinedPermissions.get(new Pair<>(21, "package2")).first);
+ assertTrue(combinedPermissions.get(new Pair<>(21, "package2")).second);
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
index 5800400..bd3ba04 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java
@@ -313,11 +313,22 @@
when(mPackageManager.getInstalledPackages(eq((long) GET_PERMISSIONS), anyInt()))
.thenReturn(requesting);
- Map<Pair<Integer, String>, Boolean> expected = ImmutableMap.of(new Pair(1, "first"), true,
- new Pair(2, "second"), true,
- new Pair(3, "third"), false);
+ // 2 and 3 are user-set permissions
+ when(mPermManager.getPermissionFlags(
+ "first", Manifest.permission.POST_NOTIFICATIONS, userId)).thenReturn(0);
+ when(mPermManager.getPermissionFlags(
+ "second", Manifest.permission.POST_NOTIFICATIONS, userId))
+ .thenReturn(FLAG_PERMISSION_USER_SET);
+ when(mPermManager.getPermissionFlags(
+ "third", Manifest.permission.POST_NOTIFICATIONS, userId))
+ .thenReturn(FLAG_PERMISSION_USER_SET);
- Map<Pair<Integer, String>, Boolean> actual =
+ Map<Pair<Integer, String>, Pair<Boolean, Boolean>> expected =
+ ImmutableMap.of(new Pair(1, "first"), new Pair(true, false),
+ new Pair(2, "second"), new Pair(true, true),
+ new Pair(3, "third"), new Pair(false, true));
+
+ Map<Pair<Integer, String>, Pair<Boolean, Boolean>> actual =
mPermissionHelper.getNotificationPermissionValues(userId);
assertThat(actual).containsExactlyEntriesIn(expected);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index dd6d469..c85e876 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -707,12 +707,12 @@
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
- ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
- appPermissions.put(new Pair(1, "first"), true);
- appPermissions.put(new Pair(3, "third"), false);
- appPermissions.put(new Pair(UID_P, PKG_P), true);
- appPermissions.put(new Pair(UID_O, PKG_O), false);
- appPermissions.put(new Pair(UID_N_MR1, PKG_N_MR1), true);
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), new Pair(true, false));
+ appPermissions.put(new Pair(3, "third"), new Pair(false, false));
+ appPermissions.put(new Pair(UID_P, PKG_P), new Pair(true, false));
+ appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false));
+ appPermissions.put(new Pair(UID_N_MR1, PKG_N_MR1), new Pair(true, false));
when(mPermissionHelper.getNotificationPermissionValues(UserHandle.USER_SYSTEM))
.thenReturn(appPermissions);
@@ -788,12 +788,12 @@
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
- ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
- appPermissions.put(new Pair(1, "first"), true);
- appPermissions.put(new Pair(3, "third"), false);
- appPermissions.put(new Pair(UID_P, PKG_P), true);
- appPermissions.put(new Pair(UID_O, PKG_O), false);
- appPermissions.put(new Pair(UID_N_MR1, PKG_N_MR1), true);
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), new Pair(true, false));
+ appPermissions.put(new Pair(3, "third"), new Pair(false, false));
+ appPermissions.put(new Pair(UID_P, PKG_P), new Pair(true, false));
+ appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false));
+ appPermissions.put(new Pair(UID_N_MR1, PKG_N_MR1), new Pair(true, false));
when(mPermissionHelper.getNotificationPermissionValues(UserHandle.USER_SYSTEM))
.thenReturn(appPermissions);
@@ -875,9 +875,9 @@
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
- ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
- appPermissions.put(new Pair(UID_P, PKG_P), true);
- appPermissions.put(new Pair(UID_O, PKG_O), false);
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(UID_P, PKG_P), new Pair(true, false));
+ appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false));
when(mPermissionHelper.getNotificationPermissionValues(UserHandle.USER_SYSTEM))
.thenReturn(appPermissions);
@@ -955,12 +955,12 @@
mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
mPermissionHelper, mLogger, mAppOpsManager, mStatsEventBuilderFactory);
- ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
- appPermissions.put(new Pair(1, "first"), true);
- appPermissions.put(new Pair(3, "third"), false);
- appPermissions.put(new Pair(UID_P, PKG_P), true);
- appPermissions.put(new Pair(UID_O, PKG_O), false);
- appPermissions.put(new Pair(UID_N_MR1, PKG_N_MR1), true);
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), new Pair(true, false));
+ appPermissions.put(new Pair(3, "third"), new Pair(false, false));
+ appPermissions.put(new Pair(UID_P, PKG_P), new Pair(true, false));
+ appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false));
+ appPermissions.put(new Pair(UID_N_MR1, PKG_N_MR1), new Pair(true, false));
when(mPermissionHelper.getNotificationPermissionValues(UserHandle.USER_SYSTEM))
.thenReturn(appPermissions);
@@ -2556,11 +2556,11 @@
// know about, those are ignored if migration is not enabled
// package permissions map to be passed in
- ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
- appPermissions.put(new Pair(1, "first"), true); // not in local prefs
- appPermissions.put(new Pair(3, "third"), false); // not in local prefs
- appPermissions.put(new Pair(UID_P, PKG_P), true); // in local prefs
- appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), new Pair(false, false)); // not in local prefs
+ appPermissions.put(new Pair(UID_P, PKG_P), new Pair(true, false)); // in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
@@ -2618,11 +2618,11 @@
// know about, those should still be included
// package permissions map to be passed in
- ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
- appPermissions.put(new Pair(1, "first"), true); // not in local prefs
- appPermissions.put(new Pair(3, "third"), false); // not in local prefs
- appPermissions.put(new Pair(UID_P, PKG_P), true); // in local prefs
- appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), new Pair(false, false)); // not in local prefs
+ appPermissions.put(new Pair(UID_P, PKG_P), new Pair(true, false)); // in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
NotificationChannel channel1 =
new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
@@ -2701,10 +2701,10 @@
// not from the passed-in permissions map
when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
- ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
- appPermissions.put(new Pair(1, "first"), true); // not in local prefs
- appPermissions.put(new Pair(3, "third"), false); // not in local prefs
- appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), new Pair(false, false)); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
// package preferences: only PKG_P is banned
mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
@@ -2726,10 +2726,10 @@
// have their permission set to false, and not based on PackagePreferences importance
when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
- ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
- appPermissions.put(new Pair(1, "first"), true); // not in local prefs
- appPermissions.put(new Pair(3, "third"), false); // not in local prefs
- appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), new Pair(false, false)); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
// package preferences: PKG_O not banned based on local importance, and PKG_P is
mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
@@ -2770,10 +2770,10 @@
// confirm that the string resulting from dumpImpl contains only info from package prefs
when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
- ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
- appPermissions.put(new Pair(1, "first"), true); // not in local prefs
- appPermissions.put(new Pair(3, "third"), false); // not in local prefs
- appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), new Pair(false, true)); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
// local package preferences: PKG_O is not banned even though the permissions would
// indicate so
@@ -2796,6 +2796,7 @@
ArrayList<String> notExpected = new ArrayList<>();
notExpected.add("first (1) importance=DEFAULT");
notExpected.add("third (3) importance=NONE");
+ notExpected.add("userSet="); // no user-set information pre migration
for (String exp : expected) {
assertTrue(actual.contains(exp));
@@ -2819,10 +2820,10 @@
// confirm that the string resulting from dumpImpl contains only importances from permission
when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
- ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
- appPermissions.put(new Pair(1, "first"), true); // not in local prefs
- appPermissions.put(new Pair(3, "third"), false); // not in local prefs
- appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), new Pair(false, true)); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
// local package preferences
mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
@@ -2837,9 +2838,9 @@
// expected (substring) output for each preference via permissions
ArrayList<String> expected = new ArrayList<>();
- expected.add("first (1) importance=DEFAULT");
- expected.add("third (3) importance=NONE");
- expected.add(PKG_O + " (" + UID_O + ") importance=NONE");
+ expected.add("first (1) importance=DEFAULT userSet=false");
+ expected.add("third (3) importance=NONE userSet=true");
+ expected.add(PKG_O + " (" + UID_O + ") importance=NONE userSet=false");
expected.add(PKG_P + " (" + UID_P + ")");
// make sure we don't have package preference info
@@ -2881,10 +2882,10 @@
// test that dumping to proto gets the importances from the right place
when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
- ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
- appPermissions.put(new Pair(1, "first"), true); // not in local prefs
- appPermissions.put(new Pair(3, "third"), false); // not in local prefs
- appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), new Pair(false, false)); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
// local package preferences
mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
@@ -2921,10 +2922,10 @@
when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
// permissions -- these should take precedence
- ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
- appPermissions.put(new Pair(1, "first"), true); // not in local prefs
- appPermissions.put(new Pair(3, "third"), false); // not in local prefs
- appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), new Pair(false, false)); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
// local package preferences
mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
@@ -4993,10 +4994,10 @@
when(mPermissionHelper.isMigrationEnabled()).thenReturn(false);
// build a collection of app permissions that should be passed in but ignored
- ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
- appPermissions.put(new Pair(1, "first"), true); // not in local prefs
- appPermissions.put(new Pair(3, "third"), false); // not in local prefs
- appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), new Pair(false, false)); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
// package preferences: PKG_O not banned based on local importance, and PKG_P is
mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
@@ -5036,10 +5037,10 @@
when(mPermissionHelper.isMigrationEnabled()).thenReturn(true);
// build a collection of app permissions that should be passed in but ignored
- ArrayMap<Pair<Integer, String>, Boolean> appPermissions = new ArrayMap<>();
- appPermissions.put(new Pair(1, "first"), true); // not in local prefs
- appPermissions.put(new Pair(3, "third"), false); // not in local prefs
- appPermissions.put(new Pair(UID_O, PKG_O), false); // in local prefs
+ ArrayMap<Pair<Integer, String>, Pair<Boolean, Boolean>> appPermissions = new ArrayMap<>();
+ appPermissions.put(new Pair(1, "first"), new Pair(true, false)); // not in local prefs
+ appPermissions.put(new Pair(3, "third"), new Pair(false, false)); // not in local prefs
+ appPermissions.put(new Pair(UID_O, PKG_O), new Pair(false, false)); // in local prefs
// package preferences: PKG_O not banned based on local importance, and PKG_P is
mHelper.setImportance(PKG_O, UID_O, IMPORTANCE_HIGH);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 7a133ea..4a8e121 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -39,6 +39,7 @@
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.os.Process.NOBODY_UID;
import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.InsetsState.ITYPE_IME;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
@@ -128,6 +129,8 @@
import android.view.IRemoteAnimationRunner.Stub;
import android.view.IWindowManager;
import android.view.IWindowSession;
+import android.view.InsetsSource;
+import android.view.InsetsState;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.Surface;
@@ -3024,6 +3027,41 @@
assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
}
+ @UseTestDisplay(addWindows = W_INPUT_METHOD)
+ @Test
+ public void testImeInsetsFrozenFlag_resetWhenReportedToBeImeInputTarget() {
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+
+ InsetsSource imeSource = new InsetsSource(ITYPE_IME);
+ app.getInsetsState().addSource(imeSource);
+ mDisplayContent.setImeLayeringTarget(app);
+ mDisplayContent.updateImeInputAndControlTarget(app);
+
+ InsetsState state = mDisplayContent.getInsetsPolicy().getInsetsForWindow(app);
+ assertFalse(state.getSource(ITYPE_IME).isVisible());
+ assertTrue(state.getSource(ITYPE_IME).getFrame().isEmpty());
+
+ // Simulate app is closing and expect IME insets is frozen.
+ mDisplayContent.mOpeningApps.clear();
+ app.mActivityRecord.commitVisibility(false, false);
+ app.mActivityRecord.onWindowsGone();
+ assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+
+ // Simulate app re-start input or turning screen off/on then unlocked by un-secure
+ // keyguard to back to the app, expect IME insets is not frozen
+ imeSource.setFrame(new Rect(100, 400, 500, 500));
+ app.getInsetsState().addSource(imeSource);
+ app.getInsetsState().setSourceVisible(ITYPE_IME, true);
+ mDisplayContent.updateImeInputAndControlTarget(app);
+ assertFalse(app.mActivityRecord.mImeInsetsFrozenUntilStartInput);
+
+ // Verify when IME is visible and the app can receive the right IME insets from policy.
+ makeWindowVisibleAndDrawn(app, mImeWindow);
+ state = mDisplayContent.getInsetsPolicy().getInsetsForWindow(app);
+ assertTrue(state.getSource(ITYPE_IME).isVisible());
+ assertEquals(state.getSource(ITYPE_IME).getFrame(), imeSource.getFrame());
+ }
+
@Test
public void testInClosingAnimation_doNotHideSurface() {
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index c103bc6..3e617d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -19,6 +19,10 @@
import static android.content.pm.ActivityInfo.LOCK_TASK_LAUNCH_MODE_DEFAULT;
import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -327,4 +331,16 @@
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
assertEquals("android.test.second", mInterceptor.mIntent.getAction());
}
+
+ @Test
+ public void testActivityLaunchedCallback_singleCallback() {
+ addMockInterceptorCallback(null);
+
+ assertEquals(1, mActivityInterceptorCallbacks.size());
+ final ActivityInterceptorCallback callback = mActivityInterceptorCallbacks.valueAt(0);
+ spyOn(callback);
+ mInterceptor.onActivityLaunched(null, null);
+
+ verify(callback, times(1)).onActivityLaunched(any(), any());
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index d7a0ab3..dc0e028 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1336,7 +1336,6 @@
spyOn(rotationAnim);
// Assume that the display rotation is changed so it is frozen in preparation for animation.
doReturn(true).when(rotationAnim).hasScreenshot();
- mWm.mDisplayFrozen = true;
displayContent.getDisplayRotation().setRotation((displayContent.getRotation() + 1) % 4);
displayContent.setRotationAnimation(rotationAnim);
// The fade rotation animation also starts to hide some non-app windows.
@@ -1347,9 +1346,9 @@
w.setOrientationChanging(true);
}
// The display only waits for the app window to unfreeze.
- assertFalse(displayContent.waitForUnfreeze(statusBar));
- assertFalse(displayContent.waitForUnfreeze(navBar));
- assertTrue(displayContent.waitForUnfreeze(app));
+ assertFalse(displayContent.shouldSyncRotationChange(statusBar));
+ assertFalse(displayContent.shouldSyncRotationChange(navBar));
+ assertTrue(displayContent.shouldSyncRotationChange(app));
// If all windows animated by fade rotation animation have done the orientation change,
// the animation controller should be cleared.
statusBar.setOrientationChanging(false);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 2954d78..645d804 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -535,6 +535,7 @@
mActivity.mVisibleRequested = false;
mActivity.visibleIgnoringKeyguard = false;
mActivity.app.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
+ mActivity.app.computeProcessActivityState();
// Simulate the display changes orientation.
final Configuration rotatedConfig = rotateDisplay(display, ROTATION_90);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index d68edba..cdf6b59 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -84,7 +84,7 @@
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- adjacentRootTask.setAdjacentTaskFragment(rootTask);
+ adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */);
taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
Task actualRootTask = taskDisplayArea.getLaunchRootTask(
@@ -110,7 +110,7 @@
final Task adjacentRootTask = createTask(
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
- adjacentRootTask.setAdjacentTaskFragment(rootTask);
+ adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */);
taskDisplayArea.setLaunchRootTask(rootTask,
new int[]{WINDOWING_MODE_MULTI_WINDOW}, new int[]{ACTIVITY_TYPE_STANDARD});
@@ -131,7 +131,7 @@
mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
adjacentRootTask.mCreatedByOrganizer = true;
final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea();
- adjacentRootTask.setAdjacentTaskFragment(rootTask);
+ adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */);
taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask);
final Task actualRootTask = taskDisplayArea.getLaunchRootTask(
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index a5c6dc0..9ad8c5b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -330,7 +330,7 @@
// Throw exception if the transaction is trying to change a window that is not organized by
// the organizer.
- mTransaction.setAdjacentRoots(mFragmentWindowToken, token2);
+ mTransaction.setAdjacentRoots(mFragmentWindowToken, token2, false /* moveTogether */);
assertThrows(SecurityException.class, () -> {
try {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 5fde7eb..a7a374b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -59,6 +59,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.clearInvocations;
@@ -1365,6 +1366,25 @@
assertNotNull(activity.getTask().getDimmer());
}
+ @Test
+ public void testMoveToFront_moveAdjacentTask() {
+ final Task task1 =
+ createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+ final Task task2 =
+ createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
+ spyOn(task2);
+
+ task1.setAdjacentTaskFragment(task2, false /* moveTogether */);
+ task1.moveToFront("" /* reason */);
+ verify(task2, never()).moveToFrontInner(anyString(), isNull());
+
+ // Reset adjacent tasks to move together.
+ task1.setAdjacentTaskFragment(null, false /* moveTogether */);
+ task1.setAdjacentTaskFragment(task2, true /* moveTogether */);
+ task1.moveToFront("" /* reason */);
+ verify(task2).moveToFrontInner(anyString(), isNull());
+ }
+
private Task getTestTask() {
final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).build();
return task.getBottomMostTask();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index d9a166a..b7417c4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -19,6 +19,9 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -32,7 +35,9 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -44,6 +49,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.view.SurfaceControl;
+import android.view.TransactionCommittedListener;
import android.window.ITaskOrganizer;
import android.window.ITransitionPlayer;
import android.window.TransitionInfo;
@@ -52,6 +58,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -469,6 +476,54 @@
}
@Test
+ public void testDisplayRotationChange() {
+ final Task task = createActivityRecord(mDisplayContent).getTask();
+ final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
+ final WindowState navBar = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");
+ final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime");
+ final WindowState[] windows = { statusBar, navBar, ime };
+ makeWindowVisible(windows);
+ mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
+ mDisplayContent.getDisplayPolicy().addWindowLw(navBar, navBar.mAttrs);
+ final TestTransitionPlayer player = registerTestTransitionPlayer();
+
+ mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
+ mDisplayContent.requestChangeTransitionIfNeeded(1 /* any changes */);
+ final FadeRotationAnimationController fadeController =
+ mDisplayContent.getFadeRotationAnimationController();
+ assertNotNull(fadeController);
+ for (WindowState w : windows) {
+ w.setOrientationChanging(true);
+ }
+ player.startTransition();
+
+ assertFalse(statusBar.mToken.inTransition());
+ assertTrue(ime.mToken.inTransition());
+ assertTrue(task.inTransition());
+
+ // Status bar finishes drawing before the start transaction. Its fade-in animation will be
+ // executed until the transaction is committed, so it is still in target tokens.
+ statusBar.setOrientationChanging(false);
+ assertTrue(fadeController.isTargetToken(statusBar.mToken));
+
+ final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
+ final ArgumentCaptor<TransactionCommittedListener> listenerCaptor =
+ ArgumentCaptor.forClass(TransactionCommittedListener.class);
+ player.onTransactionReady(startTransaction);
+
+ verify(startTransaction).addTransactionCommittedListener(any(), listenerCaptor.capture());
+ // The transaction is committed, so fade-in animation for status bar is consumed.
+ listenerCaptor.getValue().onTransactionCommitted();
+ assertFalse(fadeController.isTargetToken(statusBar.mToken));
+
+ // Status bar finishes drawing after the start transaction, so its fade-in animation can
+ // execute directly.
+ navBar.setOrientationChanging(false);
+ assertFalse(fadeController.isTargetToken(navBar.mToken));
+ assertNull(mDisplayContent.getFadeRotationAnimationController());
+ }
+
+ @Test
public void testIntermediateVisibility() {
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
final TransitionController controller = new TransitionController(mAtm, snapshotController);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 1eed79f1..75a87ba 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -554,7 +554,7 @@
final RunningTaskInfo info2 = task2.getTaskInfo();
WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.setAdjacentRoots(info1.token, info2.token);
+ wct.setAdjacentRoots(info1.token, info2.token, false /* moveTogether */);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertEquals(task1.getAdjacentTaskFragment(), task2);
assertEquals(task2.getAdjacentTaskFragment(), task1);
@@ -564,8 +564,8 @@
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, task1);
- task1.setAdjacentTaskFragment(null);
- task2.setAdjacentTaskFragment(null);
+ task1.setAdjacentTaskFragment(null, false /* moveTogether */);
+ task2.setAdjacentTaskFragment(null, false /* moveTogether */);
wct = new WindowContainerTransaction();
wct.clearLaunchAdjacentFlagRoot(info1.token);
mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 92fd682..a985de5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1660,10 +1660,17 @@
mLastRequest = request;
}
- public void start() {
+ void startTransition() {
mOrganizer.startTransition(mLastRequest.getType(), mLastTransit, null);
- mLastTransit.onTransactionReady(mLastTransit.getSyncId(),
- mock(SurfaceControl.Transaction.class));
+ }
+
+ void onTransactionReady(SurfaceControl.Transaction t) {
+ mLastTransit.onTransactionReady(mLastTransit.getSyncId(), t);
+ }
+
+ void start() {
+ startTransition();
+ onTransactionReady(mock(SurfaceControl.Transaction.class));
}
public void finish() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index e5dc557..049966c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -489,8 +489,8 @@
createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
final WindowState splitWindow2 =
createAppWindow(splitScreenTask2, ACTIVITY_TYPE_STANDARD, "splitWindow2");
- splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2);
- splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1);
+ splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2, true /* moveTogether */);
+ splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1, true /* moveTogether */);
final Task aboveTask =
createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
@@ -528,8 +528,8 @@
createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD);
final WindowState splitWindow2 =
createAppWindow(splitScreenTask2, ACTIVITY_TYPE_STANDARD, "splitWindow2");
- splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2);
- splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1);
+ splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2, true /* moveTogether */);
+ splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1, true /* moveTogether */);
mDisplayContent.assignChildLayers(mTransaction);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index a32bd2d..997c883 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -41,6 +41,7 @@
import android.app.IUidObserver;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManagerInternal;
+import android.app.usage.AppLaunchEstimateInfo;
import android.app.usage.AppStandbyInfo;
import android.app.usage.ConfigurationStats;
import android.app.usage.EventStats;
@@ -1554,6 +1555,52 @@
}
}
+ private void setEstimatedLaunchTime(int userId, String packageName,
+ @CurrentTimeMillisLong long estimatedLaunchTime) {
+ final long now = System.currentTimeMillis();
+ if (estimatedLaunchTime <= now) {
+ if (DEBUG) {
+ Slog.w(TAG, "Ignoring new estimate for "
+ + userId + ":" + packageName + " because it's old");
+ }
+ return;
+ }
+ final long oldEstimatedLaunchTime = mAppStandby.getEstimatedLaunchTime(packageName, userId);
+ if (estimatedLaunchTime != oldEstimatedLaunchTime) {
+ mAppStandby.setEstimatedLaunchTime(packageName, userId, estimatedLaunchTime);
+ mHandler.obtainMessage(
+ MSG_NOTIFY_ESTIMATED_LAUNCH_TIME_CHANGED, userId, 0, packageName)
+ .sendToTarget();
+ }
+ }
+
+ private void setEstimatedLaunchTimes(int userId, List<AppLaunchEstimateInfo> launchEstimates) {
+ final ArraySet<String> changedTimes = new ArraySet<>();
+ final long now = System.currentTimeMillis();
+ for (int i = launchEstimates.size() - 1; i >= 0; --i) {
+ AppLaunchEstimateInfo estimate = launchEstimates.get(i);
+ if (estimate.estimatedLaunchTime <= now) {
+ if (DEBUG) {
+ Slog.w(TAG, "Ignoring new estimate for "
+ + userId + ":" + estimate.packageName + " because it's old");
+ }
+ continue;
+ }
+ final long oldEstimatedLaunchTime =
+ mAppStandby.getEstimatedLaunchTime(estimate.packageName, userId);
+ if (estimate.estimatedLaunchTime != oldEstimatedLaunchTime) {
+ mAppStandby.setEstimatedLaunchTime(
+ estimate.packageName, userId, estimate.estimatedLaunchTime);
+ changedTimes.add(estimate.packageName);
+ }
+ }
+ if (changedTimes.size() > 0) {
+ mHandler.obtainMessage(
+ MSG_NOTIFY_ESTIMATED_LAUNCH_TIMES_CHANGED, userId, 0, changedTimes)
+ .sendToTarget();
+ }
+ }
+
/**
* Called via the local interface.
*/
@@ -2293,6 +2340,37 @@
}
@Override
+ public void setEstimatedLaunchTime(String packageName, long estimatedLaunchTime,
+ int userId) {
+ getContext().enforceCallingPermission(
+ Manifest.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE,
+ "No permission to change app launch estimates");
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ UsageStatsService.this
+ .setEstimatedLaunchTime(userId, packageName, estimatedLaunchTime);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void setEstimatedLaunchTimes(ParceledListSlice estimatedLaunchTimes, int userId) {
+ getContext().enforceCallingPermission(
+ Manifest.permission.CHANGE_APP_LAUNCH_TIME_ESTIMATE,
+ "No permission to change app launch estimates");
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ UsageStatsService.this
+ .setEstimatedLaunchTimes(userId, estimatedLaunchTimes.getList());
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void onCarrierPrivilegedAppsChanged() {
if (DEBUG) {
Slog.i(TAG, "Carrier privileged apps changed");
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ae2facd..d120f5a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -12620,16 +12620,19 @@
* <p>If this object has been created with {@link #createForSubscriptionId}, applies
* to the given subId. Otherwise, applies to
* {@link SubscriptionManager#getDefaultDataSubscriptionId()}
- *
* @param reason the reason the data enable change is taking place
* @return whether data is enabled for a reason.
* <p>Requires Permission:
+ * The calling app has carrier privileges (see {@link #hasCarrierPrivileges}) or
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} or
- * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}
+ * {@link android.Manifest.permission#ACCESS_NETWORK_STATE} or
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE}
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
- android.Manifest.permission.READ_PHONE_STATE})
+ android.Manifest.permission.READ_PHONE_STATE,
+ android.Manifest.permission.MODIFY_PHONE_STATE
+ })
public boolean isDataEnabledForReason(@DataEnabledReason int reason) {
return isDataEnabledForReason(getSubId(), reason);
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 097d363..8c02ffe 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -328,8 +328,6 @@
@SystemApi
public static final String TYPE_XCAP_STRING = "xcap";
-
-
/**
* APN type for Virtual SIM service.
*
@@ -506,27 +504,21 @@
private final int mRoamingProtocol;
private final int mMtuV4;
private final int mMtuV6;
-
private final boolean mCarrierEnabled;
-
- private final int mNetworkTypeBitmask;
-
+ private final @TelephonyManager.NetworkTypeBitMask int mNetworkTypeBitmask;
+ private final @TelephonyManager.NetworkTypeBitMask long mLingeringNetworkTypeBitmask;
private final int mProfileId;
-
private final boolean mPersistent;
private final int mMaxConns;
private final int mWaitTime;
private final int mMaxConnsTime;
-
private final int mMvnoType;
private final String mMvnoMatchData;
-
private final int mApnSetId;
-
private boolean mPermanentFailed = false;
private final int mCarrierId;
-
private final int mSkip464Xlat;
+ private final boolean mAlwaysOn;
/**
* Returns the MTU size of the IPv4 mobile interface to which the APN connected. Note this value
@@ -843,20 +835,37 @@
}
/**
- * Returns a bitmask describing the Radio Technologies(Network Types) which this APN may use.
+ * Returns a bitmask describing the Radio Technologies (Network Types) which this APN may use.
*
* NetworkType bitmask is calculated from NETWORK_TYPE defined in {@link TelephonyManager}.
*
* Examples of Network Types include {@link TelephonyManager#NETWORK_TYPE_UNKNOWN},
* {@link TelephonyManager#NETWORK_TYPE_GPRS}, {@link TelephonyManager#NETWORK_TYPE_EDGE}.
*
- * @return a bitmask describing the Radio Technologies(Network Types)
+ * @return a bitmask describing the Radio Technologies (Network Types) or 0 if it is undefined.
*/
public int getNetworkTypeBitmask() {
return mNetworkTypeBitmask;
}
/**
+ * Returns a bitmask describing the Radio Technologies (Network Types) that should not be torn
+ * down if it exists or brought up if it already exists for this APN.
+ *
+ * NetworkType bitmask is calculated from NETWORK_TYPE defined in {@link TelephonyManager}.
+ *
+ * Examples of Network Types include {@link TelephonyManager#NETWORK_TYPE_UNKNOWN},
+ * {@link TelephonyManager#NETWORK_TYPE_GPRS}, {@link TelephonyManager#NETWORK_TYPE_EDGE}.
+ *
+ * @return a bitmask describing the Radio Technologies (Network Types) that should linger
+ * or 0 if it is undefined.
+ * @hide
+ */
+ public @TelephonyManager.NetworkTypeBitMask long getLingeringNetworkTypeBitmask() {
+ return mLingeringNetworkTypeBitmask;
+ }
+
+ /**
* Returns the MVNO match type for this APN.
*
* @see Builder#setMvnoType(int)
@@ -888,6 +897,18 @@
return mSkip464Xlat;
}
+ /**
+ * Returns whether User Plane resources have to be activated during every transition from
+ * CM-IDLE mode to CM-CONNECTED state for this APN
+ * See 3GPP TS 23.501 section 5.6.13
+ *
+ * @return True if the PDU session for this APN should always be on and false otherwise
+ * @hide
+ */
+ public boolean isAlwaysOn() {
+ return mAlwaysOn;
+ }
+
private ApnSetting(Builder builder) {
this.mEntryName = builder.mEntryName;
this.mApnName = builder.mApnName;
@@ -912,6 +933,7 @@
this.mMtuV6 = builder.mMtuV6;
this.mCarrierEnabled = builder.mCarrierEnabled;
this.mNetworkTypeBitmask = builder.mNetworkTypeBitmask;
+ this.mLingeringNetworkTypeBitmask = builder.mLingeringNetworkTypeBitmask;
this.mProfileId = builder.mProfileId;
this.mPersistent = builder.mModemCognitive;
this.mMaxConns = builder.mMaxConns;
@@ -922,6 +944,7 @@
this.mApnSetId = builder.mApnSetId;
this.mCarrierId = builder.mCarrierId;
this.mSkip464Xlat = builder.mSkip464Xlat;
+ this.mAlwaysOn = builder.mAlwaysOn;
}
/**
@@ -938,6 +961,10 @@
networkTypeBitmask =
ServiceState.convertBearerBitmaskToNetworkTypeBitmask(bearerBitmask);
}
+ int mtuV4 = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU_V4));
+ if (mtuV4 == -1) {
+ mtuV4 = cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU));
+ }
return new Builder()
.setId(cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers._ID)))
@@ -972,6 +999,8 @@
.setCarrierEnabled(cursor.getInt(cursor.getColumnIndexOrThrow(
Telephony.Carriers.CARRIER_ENABLED)) == 1)
.setNetworkTypeBitmask(networkTypeBitmask)
+ .setLingeringNetworkTypeBitmask(cursor.getInt(cursor.getColumnIndexOrThrow(
+ Carriers.LINGERING_NETWORK_TYPE_BITMASK)))
.setProfileId(cursor.getInt(
cursor.getColumnIndexOrThrow(Telephony.Carriers.PROFILE_ID)))
.setModemCognitive(cursor.getInt(cursor.getColumnIndexOrThrow(
@@ -982,8 +1011,8 @@
cursor.getColumnIndexOrThrow(Telephony.Carriers.WAIT_TIME_RETRY)))
.setMaxConnsTime(cursor.getInt(cursor.getColumnIndexOrThrow(
Telephony.Carriers.TIME_LIMIT_FOR_MAX_CONNECTIONS)))
- .setMtuV4(cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU)))
- .setMtuV6(UNSET_MTU) // TODO: Add corresponding support in telephony provider
+ .setMtuV4(mtuV4)
+ .setMtuV6(cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Carriers.MTU_V6)))
.setMvnoType(getMvnoTypeIntFromString(
cursor.getString(cursor.getColumnIndexOrThrow(
Telephony.Carriers.MVNO_TYPE))))
@@ -994,6 +1023,7 @@
.setCarrierId(cursor.getInt(
cursor.getColumnIndexOrThrow(Telephony.Carriers.CARRIER_ID)))
.setSkip464Xlat(cursor.getInt(cursor.getColumnIndexOrThrow(Carriers.SKIP_464XLAT)))
+ .setAlwaysOn(cursor.getInt(cursor.getColumnIndexOrThrow(Carriers.ALWAYS_ON)) == 1)
.buildWithoutCheck();
}
@@ -1019,6 +1049,7 @@
.setRoamingProtocol(apn.mRoamingProtocol)
.setCarrierEnabled(apn.mCarrierEnabled)
.setNetworkTypeBitmask(apn.mNetworkTypeBitmask)
+ .setLingeringNetworkTypeBitmask(apn.mLingeringNetworkTypeBitmask)
.setProfileId(apn.mProfileId)
.setModemCognitive(apn.mPersistent)
.setMaxConns(apn.mMaxConns)
@@ -1031,6 +1062,7 @@
.setApnSetId(apn.mApnSetId)
.setCarrierId(apn.mCarrierId)
.setSkip464Xlat(apn.mSkip464Xlat)
+ .setAlwaysOn(apn.mAlwaysOn)
.buildWithoutCheck();
}
@@ -1069,9 +1101,11 @@
sb.append(", ").append(mMvnoMatchData);
sb.append(", ").append(mPermanentFailed);
sb.append(", ").append(mNetworkTypeBitmask);
+ sb.append(", ").append(mLingeringNetworkTypeBitmask);
sb.append(", ").append(mApnSetId);
sb.append(", ").append(mCarrierId);
sb.append(", ").append(mSkip464Xlat);
+ sb.append(", ").append(mAlwaysOn);
return sb.toString();
}
@@ -1136,8 +1170,9 @@
return Objects.hash(mApnName, mProxyAddress, mProxyPort, mMmsc, mMmsProxyAddress,
mMmsProxyPort, mUser, mPassword, mAuthType, mApnTypeBitmask, mId, mOperatorNumeric,
mProtocol, mRoamingProtocol, mMtuV4, mMtuV6, mCarrierEnabled, mNetworkTypeBitmask,
- mProfileId, mPersistent, mMaxConns, mWaitTime, mMaxConnsTime, mMvnoType,
- mMvnoMatchData, mApnSetId, mCarrierId, mSkip464Xlat);
+ mLingeringNetworkTypeBitmask, mProfileId, mPersistent, mMaxConns, mWaitTime,
+ mMaxConnsTime, mMvnoType, mMvnoMatchData, mApnSetId, mCarrierId, mSkip464Xlat,
+ mAlwaysOn);
}
@Override
@@ -1174,9 +1209,11 @@
&& Objects.equals(mMvnoType, other.mMvnoType)
&& Objects.equals(mMvnoMatchData, other.mMvnoMatchData)
&& Objects.equals(mNetworkTypeBitmask, other.mNetworkTypeBitmask)
+ && Objects.equals(mLingeringNetworkTypeBitmask, other.mLingeringNetworkTypeBitmask)
&& Objects.equals(mApnSetId, other.mApnSetId)
&& Objects.equals(mCarrierId, other.mCarrierId)
- && Objects.equals(mSkip464Xlat, other.mSkip464Xlat);
+ && Objects.equals(mSkip464Xlat, other.mSkip464Xlat)
+ && Objects.equals(mAlwaysOn, other.mAlwaysOn);
}
/**
@@ -1210,6 +1247,7 @@
&& Objects.equals(mPassword, other.mPassword)
&& Objects.equals(mAuthType, other.mAuthType)
&& Objects.equals(mApnTypeBitmask, other.mApnTypeBitmask)
+ && Objects.equals(mLingeringNetworkTypeBitmask, other.mLingeringNetworkTypeBitmask)
&& (isDataRoaming || Objects.equals(mProtocol, other.mProtocol))
&& (!isDataRoaming || Objects.equals(mRoamingProtocol, other.mRoamingProtocol))
&& Objects.equals(mCarrierEnabled, other.mCarrierEnabled)
@@ -1224,7 +1262,8 @@
&& Objects.equals(mMvnoMatchData, other.mMvnoMatchData)
&& Objects.equals(mApnSetId, other.mApnSetId)
&& Objects.equals(mCarrierId, other.mCarrierId)
- && Objects.equals(mSkip464Xlat, other.mSkip464Xlat);
+ && Objects.equals(mSkip464Xlat, other.mSkip464Xlat)
+ && Objects.equals(mAlwaysOn, other.mAlwaysOn);
}
/**
@@ -1304,9 +1343,13 @@
apnValue.put(Telephony.Carriers.CARRIER_ENABLED, mCarrierEnabled);
apnValue.put(Telephony.Carriers.MVNO_TYPE, getMvnoTypeStringFromInt(mMvnoType));
apnValue.put(Telephony.Carriers.NETWORK_TYPE_BITMASK, mNetworkTypeBitmask);
+ apnValue.put(Telephony.Carriers.LINGERING_NETWORK_TYPE_BITMASK,
+ mLingeringNetworkTypeBitmask);
+ apnValue.put(Telephony.Carriers.MTU_V4, mMtuV4);
+ apnValue.put(Telephony.Carriers.MTU_V6, mMtuV6);
apnValue.put(Telephony.Carriers.CARRIER_ID, mCarrierId);
apnValue.put(Telephony.Carriers.SKIP_464XLAT, mSkip464Xlat);
-
+ apnValue.put(Telephony.Carriers.ALWAYS_ON, mAlwaysOn);
return apnValue;
}
@@ -1510,6 +1553,31 @@
return ServiceState.bitmaskHasTech(mNetworkTypeBitmask, networkType);
}
+ /**
+ * Check if this APN setting can support the given lingering network
+ *
+ * @param networkType The lingering network type
+ * @return {@code true} if this APN setting can support the given lingering network.
+ *
+ * @hide
+ */
+ public boolean canSupportLingeringNetworkType(@NetworkType int networkType) {
+ if (networkType == 0) {
+ return canSupportNetworkType(networkType);
+ }
+ // Do a special checking for GSM. In reality, GSM is a voice only network type and can never
+ // be used for data. We allow it here because in some DSDS corner cases, on the non-DDS
+ // sub, modem reports data rat unknown. In that case if voice is GSM and this APN supports
+ // GPRS or EDGE, this APN setting should be selected.
+ if (networkType == TelephonyManager.NETWORK_TYPE_GSM
+ && (mLingeringNetworkTypeBitmask & (TelephonyManager.NETWORK_TYPE_BITMASK_GPRS
+ | TelephonyManager.NETWORK_TYPE_BITMASK_EDGE)) != 0) {
+ return true;
+ }
+
+ return ServiceState.bitmaskHasTech((int) mLingeringNetworkTypeBitmask, networkType);
+ }
+
// Implement Parcelable.
@Override
/** @hide */
@@ -1537,6 +1605,7 @@
dest.writeInt(mRoamingProtocol);
dest.writeBoolean(mCarrierEnabled);
dest.writeInt(mNetworkTypeBitmask);
+ dest.writeLong(mLingeringNetworkTypeBitmask);
dest.writeInt(mProfileId);
dest.writeBoolean(mPersistent);
dest.writeInt(mMaxConns);
@@ -1549,6 +1618,7 @@
dest.writeInt(mApnSetId);
dest.writeInt(mCarrierId);
dest.writeInt(mSkip464Xlat);
+ dest.writeBoolean(mAlwaysOn);
}
private static ApnSetting readFromParcel(Parcel in) {
@@ -1570,6 +1640,7 @@
.setRoamingProtocol(in.readInt())
.setCarrierEnabled(in.readBoolean())
.setNetworkTypeBitmask(in.readInt())
+ .setLingeringNetworkTypeBitmask(in.readLong())
.setProfileId(in.readInt())
.setModemCognitive(in.readBoolean())
.setMaxConns(in.readInt())
@@ -1582,6 +1653,7 @@
.setApnSetId(in.readInt())
.setCarrierId(in.readInt())
.setSkip464Xlat(in.readInt())
+ .setAlwaysOn(in.readBoolean())
.buildWithoutCheck();
}
@@ -1649,7 +1721,8 @@
private int mRoamingProtocol = UNSPECIFIED_INT;
private int mMtuV4;
private int mMtuV6;
- private int mNetworkTypeBitmask;
+ private @TelephonyManager.NetworkTypeBitMask int mNetworkTypeBitmask;
+ private @TelephonyManager.NetworkTypeBitMask long mLingeringNetworkTypeBitmask;
private boolean mCarrierEnabled;
private int mProfileId;
private boolean mModemCognitive;
@@ -1661,6 +1734,7 @@
private int mApnSetId;
private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
private int mSkip464Xlat = Carriers.SKIP_464XLAT_DEFAULT;
+ private boolean mAlwaysOn;
/**
* Default constructor for Builder.
@@ -2012,6 +2086,19 @@
}
/**
+ * Sets lingering Radio Technology (Network Type) for this APN.
+ *
+ * @param lingeringNetworkTypeBitmask the Radio Technology (Network Type) that should linger
+ * @hide
+ */
+ @NonNull
+ public Builder setLingeringNetworkTypeBitmask(@TelephonyManager.NetworkTypeBitMask
+ long lingeringNetworkTypeBitmask) {
+ this.mLingeringNetworkTypeBitmask = lingeringNetworkTypeBitmask;
+ return this;
+ }
+
+ /**
* Sets the MVNO match type for this APN.
*
* @param mvnoType the MVNO match type to set for this APN
@@ -2048,6 +2135,18 @@
}
/**
+ * Sets whether the PDU session brought up by this APN should always be on.
+ * See 3GPP TS 23.501 section 5.6.13
+ *
+ * @param alwaysOn the always on status to set for this APN
+ * @hide
+ */
+ public Builder setAlwaysOn(boolean alwaysOn) {
+ this.mAlwaysOn = alwaysOn;
+ return this;
+ }
+
+ /**
* Builds {@link ApnSetting} from this builder.
*
* @return {@code null} if {@link #setApnName(String)} or {@link #setEntryName(String)}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
index 3e89cab..5d0d718 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt
@@ -91,16 +91,14 @@
}
/**
- * Checks that the nav bar layer starts visible, becomes invisible during unlocking animation
- * and becomes visible at the end
+ * Checks that the nav bar layer starts invisible, becomes visible during unlocking animation
+ * and remains visible at the end
*/
- @FlakyTest
+ @Postsubmit
@Test
fun navBarLayerVisibilityChanges() {
testSpec.assertLayers {
- this.isVisible(FlickerComponentName.NAV_BAR)
- .then()
- .isInvisible(FlickerComponentName.NAV_BAR)
+ this.isInvisible(FlickerComponentName.NAV_BAR)
.then()
.isVisible(FlickerComponentName.NAV_BAR)
}
@@ -153,16 +151,14 @@
}
/**
- * Checks that the nav bar starts the transition visible, then becomes invisible during
- * then unlocking animation and becomes visible at the end of the transition
+ * Checks that the nav bar starts the transition invisible, then becomes visible during
+ * the unlocking animation and remains visible at the end of the transition
*/
- @FlakyTest
+ @Postsubmit
@Test
fun navBarWindowsVisibilityChanges() {
testSpec.assertWm {
- this.isAboveAppWindowVisible(FlickerComponentName.NAV_BAR)
- .then()
- .isNonAppWindowInvisible(FlickerComponentName.NAV_BAR)
+ this.isNonAppWindowInvisible(FlickerComponentName.NAV_BAR)
.then()
.isAboveAppWindowVisible(FlickerComponentName.NAV_BAR)
}
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index 6e65350..de9bbb6 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -19,6 +19,7 @@
"androidx.test.ext.junit",
"androidx.test.rules",
"services.core.unboosted",
+ "testables",
"truth-prebuilt",
"ub-uiautomator",
],
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
index 3eeba7d..1d65cc3 100644
--- a/tests/Input/src/com/android/test/input/AnrTest.kt
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -19,7 +19,11 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.filters.MediumTest
+import android.app.ActivityManager
+import android.app.ApplicationExitInfo
import android.graphics.Rect
+import android.os.Build
+import android.os.IInputConstants.UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS
import android.os.SystemClock
import android.provider.Settings
import android.provider.Settings.Global.HIDE_ERROR_DIALOGS
@@ -27,10 +31,13 @@
import android.support.test.uiautomator.UiDevice
import android.support.test.uiautomator.UiObject2
import android.support.test.uiautomator.Until
+import android.testing.PollingCheck
import android.view.InputDevice
import android.view.MotionEvent
import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
import org.junit.Assert.fail
import org.junit.Before
import org.junit.Test
@@ -51,22 +58,28 @@
class AnrTest {
companion object {
private const val TAG = "AnrTest"
+ private const val ALL_PIDS = 0
+ private const val NO_MAX = 0
}
- val mInstrumentation = InstrumentationRegistry.getInstrumentation()
- var mHideErrorDialogs = 0
+ private val instrumentation = InstrumentationRegistry.getInstrumentation()
+ private var hideErrorDialogs = 0
+ private lateinit var PACKAGE_NAME: String
+ private val DISPATCHING_TIMEOUT = (UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
+ Build.HW_TIMEOUT_MULTIPLIER)
@Before
fun setUp() {
- val contentResolver = mInstrumentation.targetContext.contentResolver
- mHideErrorDialogs = Settings.Global.getInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
+ val contentResolver = instrumentation.targetContext.contentResolver
+ hideErrorDialogs = Settings.Global.getInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, 0)
+ PACKAGE_NAME = UnresponsiveGestureMonitorActivity::class.java.getPackage().getName()
}
@After
fun tearDown() {
- val contentResolver = mInstrumentation.targetContext.contentResolver
- Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, mHideErrorDialogs)
+ val contentResolver = instrumentation.targetContext.contentResolver
+ Settings.Global.putInt(contentResolver, HIDE_ERROR_DIALOGS, hideErrorDialogs)
}
@Test
@@ -86,19 +99,28 @@
private fun clickCloseAppOnAnrDialog() {
// Find anr dialog and kill app
- val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
+ val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
val closeAppButton: UiObject2? =
uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000)
if (closeAppButton == null) {
fail("Could not find anr dialog")
return
}
+ val initialReasons = getExitReasons()
closeAppButton.click()
+ /**
+ * We must wait for the app to be fully closed before exiting this test. This is because
+ * another test may again invoke 'am start' for the same activity.
+ * If the 1st process that got ANRd isn't killed by the time second 'am start' runs,
+ * the killing logic will apply to the newly launched 'am start' instance, and the second
+ * test will fail because the unresponsive activity will never be launched.
+ */
+ waitForNewExitReason(initialReasons[0].timestamp)
}
private fun clickWaitOnAnrDialog() {
// Find anr dialog and tap on wait
- val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
+ val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
val waitButton: UiObject2? =
uiDevice.wait(Until.findObject(By.res("android:id/aerr_wait")), 20000)
if (waitButton == null) {
@@ -108,9 +130,27 @@
waitButton.click()
}
+ private fun getExitReasons(): List<ApplicationExitInfo> {
+ lateinit var infos: List<ApplicationExitInfo>
+ instrumentation.runOnMainSync {
+ val am = instrumentation.getContext().getSystemService(ActivityManager::class.java)
+ infos = am.getHistoricalProcessExitReasons(PACKAGE_NAME, ALL_PIDS, NO_MAX)
+ }
+ return infos
+ }
+
+ private fun waitForNewExitReason(previousExitTimestamp: Long) {
+ PollingCheck.waitFor {
+ getExitReasons()[0].timestamp > previousExitTimestamp
+ }
+ val reasons = getExitReasons()
+ assertTrue(reasons[0].timestamp > previousExitTimestamp)
+ assertEquals(ApplicationExitInfo.REASON_ANR, reasons[0].reason)
+ }
+
private fun triggerAnr() {
startUnresponsiveActivity()
- val uiDevice: UiDevice = UiDevice.getInstance(mInstrumentation)
+ val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
val obj: UiObject2? = uiDevice.wait(Until.findObject(
By.text("Unresponsive gesture monitor")), 10000)
@@ -125,15 +165,14 @@
MotionEvent.ACTION_DOWN, rect.left.toFloat(), rect.top.toFloat(), 0 /* metaState */)
downEvent.source = InputDevice.SOURCE_TOUCHSCREEN
- mInstrumentation.uiAutomation.injectInputEvent(downEvent, false /* sync*/)
+ instrumentation.uiAutomation.injectInputEvent(downEvent, false /* sync*/)
- // Todo: replace using timeout from android.hardware.input.IInputManager
- SystemClock.sleep(5000) // default ANR timeout for gesture monitors
+ SystemClock.sleep(DISPATCHING_TIMEOUT.toLong()) // default ANR timeout for gesture monitors
}
private fun startUnresponsiveActivity() {
val flags = " -W -n "
- val startCmd = "am start $flags com.android.test.input/.UnresponsiveGestureMonitorActivity"
- mInstrumentation.uiAutomation.executeShellCommand(startCmd)
+ val startCmd = "am start $flags $PACKAGE_NAME/.UnresponsiveGestureMonitorActivity"
+ instrumentation.uiAutomation.executeShellCommand(startCmd)
}
-}
\ No newline at end of file
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java
new file mode 100644
index 0000000..f7d36970
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkPriorityTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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 android.net.vcn;
+
+import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class VcnCellUnderlyingNetworkPriorityTest {
+ private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>();
+ private static final Set<Integer> ALLOWED_CARRIER_IDS = new HashSet<>();
+
+ // Package private for use in VcnGatewayConnectionConfigTest
+ static VcnCellUnderlyingNetworkPriority getTestNetworkPriority() {
+ return new VcnCellUnderlyingNetworkPriority.Builder()
+ .setNetworkQuality(NETWORK_QUALITY_OK)
+ .setAllowMetered(true /* allowMetered */)
+ .setAllowedPlmnIds(ALLOWED_PLMN_IDS)
+ .setAllowedSpecificCarrierIds(ALLOWED_CARRIER_IDS)
+ .setAllowRoaming(true /* allowRoaming */)
+ .setRequireOpportunistic(true /* requireOpportunistic */)
+ .build();
+ }
+
+ @Test
+ public void testBuilderAndGetters() {
+ final VcnCellUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
+ assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
+ assertTrue(networkPriority.allowMetered());
+ assertEquals(ALLOWED_PLMN_IDS, networkPriority.getAllowedPlmnIds());
+ assertEquals(ALLOWED_CARRIER_IDS, networkPriority.getAllowedSpecificCarrierIds());
+ assertTrue(networkPriority.allowRoaming());
+ assertTrue(networkPriority.requireOpportunistic());
+ }
+
+ @Test
+ public void testBuilderAndGettersForDefaultValues() {
+ final VcnCellUnderlyingNetworkPriority networkPriority =
+ new VcnCellUnderlyingNetworkPriority.Builder().build();
+ assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
+ assertFalse(networkPriority.allowMetered());
+ assertEquals(new HashSet<String>(), networkPriority.getAllowedPlmnIds());
+ assertEquals(new HashSet<Integer>(), networkPriority.getAllowedSpecificCarrierIds());
+ assertFalse(networkPriority.allowRoaming());
+ assertFalse(networkPriority.requireOpportunistic());
+ }
+
+ @Test
+ public void testPersistableBundle() {
+ final VcnCellUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
+ assertEquals(
+ networkPriority,
+ VcnUnderlyingNetworkPriority.fromPersistableBundle(
+ networkPriority.toPersistableBundle()));
+ }
+}
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index dc338ae..724c33f 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -38,6 +38,7 @@
import org.junit.runner.RunWith;
import java.util.Arrays;
+import java.util.LinkedHashSet;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@@ -50,9 +51,17 @@
};
public static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN};
+ private static final LinkedHashSet<VcnUnderlyingNetworkPriority> UNDERLYING_NETWORK_PRIORITIES =
+ new LinkedHashSet();
+
static {
Arrays.sort(EXPOSED_CAPS);
Arrays.sort(UNDERLYING_CAPS);
+
+ UNDERLYING_NETWORK_PRIORITIES.add(
+ VcnCellUnderlyingNetworkPriorityTest.getTestNetworkPriority());
+ UNDERLYING_NETWORK_PRIORITIES.add(
+ VcnWifiUnderlyingNetworkPriorityTest.getTestNetworkPriority());
}
public static final long[] RETRY_INTERVALS_MS =
@@ -82,7 +91,10 @@
// Public for use in VcnGatewayConnectionTest
public static VcnGatewayConnectionConfig buildTestConfig() {
- return buildTestConfigWithExposedCaps(EXPOSED_CAPS);
+ final VcnGatewayConnectionConfig.Builder builder =
+ newBuilder().setVcnUnderlyingNetworkPriorities(UNDERLYING_NETWORK_PRIORITIES);
+
+ return buildTestConfigWithExposedCaps(builder, EXPOSED_CAPS);
}
private static VcnGatewayConnectionConfig.Builder newBuilder() {
@@ -159,6 +171,15 @@
}
@Test
+ public void testBuilderRequiresNonNullNetworkPriorities() {
+ try {
+ newBuilder().setVcnUnderlyingNetworkPriorities(null);
+ fail("Expected exception due to invalid underlyingNetworkPriorities");
+ } catch (NullPointerException e) {
+ }
+ }
+
+ @Test
public void testBuilderRequiresNonNullRetryInterval() {
try {
newBuilder().setRetryIntervalsMillis(null);
@@ -195,6 +216,7 @@
Arrays.sort(exposedCaps);
assertArrayEquals(EXPOSED_CAPS, exposedCaps);
+ assertEquals(UNDERLYING_NETWORK_PRIORITIES, config.getVcnUnderlyingNetworkPriorities());
assertEquals(TUNNEL_CONNECTION_PARAMS, config.getTunnelConnectionParams());
assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMillis());
diff --git a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkPriorityTest.java b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkPriorityTest.java
new file mode 100644
index 0000000..dd272cb
--- /dev/null
+++ b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkPriorityTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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 android.net.vcn;
+
+import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_ANY;
+import static android.net.vcn.VcnUnderlyingNetworkPriority.NETWORK_QUALITY_OK;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+
+public class VcnWifiUnderlyingNetworkPriorityTest {
+ private static final String SSID = "TestWifi";
+ private static final int INVALID_NETWORK_QUALITY = -1;
+
+ // Package private for use in VcnGatewayConnectionConfigTest
+ static VcnWifiUnderlyingNetworkPriority getTestNetworkPriority() {
+ return new VcnWifiUnderlyingNetworkPriority.Builder()
+ .setNetworkQuality(NETWORK_QUALITY_OK)
+ .setAllowMetered(true /* allowMetered */)
+ .setSsid(SSID)
+ .build();
+ }
+
+ @Test
+ public void testBuilderAndGetters() {
+ final VcnWifiUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
+ assertEquals(NETWORK_QUALITY_OK, networkPriority.getNetworkQuality());
+ assertTrue(networkPriority.allowMetered());
+ assertEquals(SSID, networkPriority.getSsid());
+ }
+
+ @Test
+ public void testBuilderAndGettersForDefaultValues() {
+ final VcnWifiUnderlyingNetworkPriority networkPriority =
+ new VcnWifiUnderlyingNetworkPriority.Builder().build();
+ assertEquals(NETWORK_QUALITY_ANY, networkPriority.getNetworkQuality());
+ assertFalse(networkPriority.allowMetered());
+ assertNull(SSID, networkPriority.getSsid());
+ }
+
+ @Test
+ public void testBuildWithInvalidNetworkQuality() {
+ try {
+ new VcnWifiUnderlyingNetworkPriority.Builder()
+ .setNetworkQuality(INVALID_NETWORK_QUALITY);
+ fail("Expected to fail due to the invalid network quality");
+ } catch (Exception expected) {
+ }
+ }
+
+ @Test
+ public void testPersistableBundle() {
+ final VcnWifiUnderlyingNetworkPriority networkPriority = getTestNetworkPriority();
+ assertEquals(
+ networkPriority,
+ VcnUnderlyingNetworkPriority.fromPersistableBundle(
+ networkPriority.toPersistableBundle()));
+ }
+}