Snap for 7964896 from 0f642cfedfe5d4f9873974cf7d838e4c8d9b0f58 to tm-release
Change-Id: I2a421603bc92099c47aef68f6bd3b53a6a0b4262
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 56b6800..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";
@@ -13223,6 +13240,7 @@
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR;
field public static final int TYPE_BUILTIN = 0; // 0x0
field public static final int TYPE_DYNAMIC = 1; // 0x1
+ field public static final int TYPE_SDK = 3; // 0x3
field public static final int TYPE_STATIC = 2; // 0x2
field public static final int VERSION_UNDEFINED = -1; // 0xffffffff
}
@@ -16442,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();
@@ -17659,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();
@@ -21597,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();
@@ -21604,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);
}
@@ -31789,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);
@@ -43119,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..96a7434 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);
@@ -9762,7 +9765,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/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c777bf5..a7f3801 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -697,7 +697,7 @@
MATCH_DISABLED_COMPONENTS,
MATCH_DISABLED_UNTIL_USED_COMPONENTS,
MATCH_INSTANT,
- MATCH_STATIC_SHARED_LIBRARIES,
+ MATCH_STATIC_SHARED_AND_SDK_LIBRARIES,
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
@@ -721,7 +721,7 @@
MATCH_SYSTEM_ONLY,
MATCH_UNINSTALLED_PACKAGES,
MATCH_INSTANT,
- MATCH_STATIC_SHARED_LIBRARIES,
+ MATCH_STATIC_SHARED_AND_SDK_LIBRARIES,
GET_DISABLED_COMPONENTS,
GET_DISABLED_UNTIL_USED_COMPONENTS,
GET_UNINSTALLED_PACKAGES,
@@ -1038,14 +1038,14 @@
public static final int MATCH_EXPLICITLY_VISIBLE_ONLY = 0x02000000;
/**
- * Internal {@link PackageInfo} flag: include static shared libraries.
- * Apps that depend on static shared libs can always access the version
+ * Internal {@link PackageInfo} flag: include static shared and SDK libraries.
+ * Apps that depend on static shared/SDK libs can always access the version
* of the lib they depend on. System/shell/root can access all shared
* libs regardless of dependency but need to explicitly ask for them
* via this flag.
* @hide
*/
- public static final int MATCH_STATIC_SHARED_LIBRARIES = 0x04000000;
+ public static final int MATCH_STATIC_SHARED_AND_SDK_LIBRARIES = 0x04000000;
/**
* {@link PackageInfo} flag: return the signing certificates associated with
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index 7abb694..4ba2ee6 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -70,6 +70,13 @@
public static final int TYPE_STATIC = 2;
/**
+ * SDK library type: this library is <strong>not</strong> backwards
+ * -compatible, can be updated and updates can be uninstalled. Clients
+ * depend on a specific version of the library.
+ */
+ public static final int TYPE_SDK = 3;
+
+ /**
* Constant for referring to an undefined version.
*/
public static final int VERSION_UNDEFINED = -1;
@@ -289,6 +296,13 @@
}
/**
+ * @hide
+ */
+ public boolean isSdk() {
+ return mType == TYPE_SDK;
+ }
+
+ /**
* Gets the package that declares the library.
*
* @return The package declaring the library.
@@ -351,6 +365,9 @@
case TYPE_STATIC: {
return "static";
}
+ case TYPE_SDK: {
+ return "sdk";
+ }
default: {
return "unknown";
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 056f99f..63332e7 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -103,11 +103,11 @@
ParsingPackage addUsesOptionalNativeLibrary(String libraryName);
- ParsingPackage addUsesStaticLibrary(String libraryName);
+ ParsingPackage addUsesSdkLibrary(String libraryName, long versionMajor,
+ String[] certSha256Digests);
- ParsingPackage addUsesStaticLibraryCertDigests(String[] certSha256Digests);
-
- ParsingPackage addUsesStaticLibraryVersion(long version);
+ ParsingPackage addUsesStaticLibrary(String libraryName, long version,
+ String[] certSha256Digests);
ParsingPackage addQueriesIntent(Intent intent);
@@ -212,6 +212,12 @@
ParsingPackage setRestoreAnyVersion(boolean restoreAnyVersion);
+ ParsingPackage setSdkLibName(String sdkLibName);
+
+ ParsingPackage setSdkLibVersionMajor(int sdkLibVersionMajor);
+
+ ParsingPackage setSdkLibrary(boolean sdkLibrary);
+
ParsingPackage setSplitHasCode(int splitIndex, boolean splitHasCode);
ParsingPackage setStaticSharedLibrary(boolean staticSharedLibrary);
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index d5957a2..19a8ce9 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -179,6 +179,10 @@
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
+ private String sdkLibName;
+ private int sdkLibVersionMajor;
+ @Nullable
+ @DataClass.ParcelWith(ForInternedString.class)
private String staticSharedLibName;
private long staticSharedLibVersion;
@NonNull
@@ -203,10 +207,17 @@
private List<String> usesStaticLibraries = emptyList();
@Nullable
private long[] usesStaticLibrariesVersions;
-
@Nullable
private String[][] usesStaticLibrariesCertDigests;
+ @NonNull
+ @DataClass.ParcelWith(ForInternedStringList.class)
+ private List<String> usesSdkLibraries = emptyList();
+ @Nullable
+ private long[] usesSdkLibrariesVersionsMajor;
+ @Nullable
+ private String[][] usesSdkLibrariesCertDigests;
+
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
private String sharedUserId;
@@ -518,6 +529,7 @@
private static final long REQUEST_FOREGROUND_SERVICE_EXEMPTION = 1L << 46;
private static final long ATTRIBUTIONS_ARE_USER_VISIBLE = 1L << 47;
private static final long RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED = 1L << 48;
+ private static final long SDK_LIBRARY = 1L << 49;
}
private ParsingPackageImpl setBoolean(@Booleans.Values long flag, boolean value) {
@@ -828,21 +840,24 @@
}
@Override
- public ParsingPackageImpl addUsesStaticLibrary(String libraryName) {
+ public ParsingPackageImpl addUsesSdkLibrary(String libraryName, long versionMajor,
+ String[] certSha256Digests) {
+ this.usesSdkLibraries = CollectionUtils.add(this.usesSdkLibraries,
+ TextUtils.safeIntern(libraryName));
+ this.usesSdkLibrariesVersionsMajor = ArrayUtils.appendLong(
+ this.usesSdkLibrariesVersionsMajor, versionMajor, true);
+ this.usesSdkLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
+ this.usesSdkLibrariesCertDigests, certSha256Digests, true);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl addUsesStaticLibrary(String libraryName, long version,
+ String[] certSha256Digests) {
this.usesStaticLibraries = CollectionUtils.add(this.usesStaticLibraries,
TextUtils.safeIntern(libraryName));
- return this;
- }
-
- @Override
- public ParsingPackageImpl addUsesStaticLibraryVersion(long version) {
this.usesStaticLibrariesVersions = ArrayUtils.appendLong(this.usesStaticLibrariesVersions,
version, true);
- return this;
- }
-
- @Override
- public ParsingPackageImpl addUsesStaticLibraryCertDigests(String[] certSha256Digests) {
this.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
this.usesStaticLibrariesCertDigests, certSha256Digests, true);
return this;
@@ -1136,6 +1151,8 @@
dest.writeString(this.overlayCategory);
dest.writeInt(this.overlayPriority);
sForInternedStringValueMap.parcel(this.overlayables, dest, flags);
+ sForInternedString.parcel(this.sdkLibName, dest, flags);
+ dest.writeInt(this.sdkLibVersionMajor);
sForInternedString.parcel(this.staticSharedLibName, dest, flags);
dest.writeLong(this.staticSharedLibVersion);
sForInternedStringList.parcel(this.libraryNames, dest, flags);
@@ -1143,9 +1160,9 @@
sForInternedStringList.parcel(this.usesOptionalLibraries, dest, flags);
sForInternedStringList.parcel(this.usesNativeLibraries, dest, flags);
sForInternedStringList.parcel(this.usesOptionalNativeLibraries, dest, flags);
+
sForInternedStringList.parcel(this.usesStaticLibraries, dest, flags);
dest.writeLongArray(this.usesStaticLibrariesVersions);
-
if (this.usesStaticLibrariesCertDigests == null) {
dest.writeInt(-1);
} else {
@@ -1155,6 +1172,17 @@
}
}
+ sForInternedStringList.parcel(this.usesSdkLibraries, dest, flags);
+ dest.writeLongArray(this.usesSdkLibrariesVersionsMajor);
+ if (this.usesSdkLibrariesCertDigests == null) {
+ dest.writeInt(-1);
+ } else {
+ dest.writeInt(this.usesSdkLibrariesCertDigests.length);
+ for (int index = 0; index < this.usesSdkLibrariesCertDigests.length; index++) {
+ dest.writeStringArray(this.usesSdkLibrariesCertDigests[index]);
+ }
+ }
+
sForInternedString.parcel(this.sharedUserId, dest, flags);
dest.writeInt(this.sharedUserLabel);
dest.writeTypedList(this.configPreferences);
@@ -1259,6 +1287,8 @@
this.overlayCategory = in.readString();
this.overlayPriority = in.readInt();
this.overlayables = sForInternedStringValueMap.unparcel(in);
+ this.sdkLibName = sForInternedString.unparcel(in);
+ this.sdkLibVersionMajor = in.readInt();
this.staticSharedLibName = sForInternedString.unparcel(in);
this.staticSharedLibVersion = in.readLong();
this.libraryNames = sForInternedStringList.unparcel(in);
@@ -1266,14 +1296,29 @@
this.usesOptionalLibraries = sForInternedStringList.unparcel(in);
this.usesNativeLibraries = sForInternedStringList.unparcel(in);
this.usesOptionalNativeLibraries = sForInternedStringList.unparcel(in);
+
this.usesStaticLibraries = sForInternedStringList.unparcel(in);
this.usesStaticLibrariesVersions = in.createLongArray();
+ {
+ int digestsSize = in.readInt();
+ if (digestsSize >= 0) {
+ this.usesStaticLibrariesCertDigests = new String[digestsSize][];
+ for (int index = 0; index < digestsSize; index++) {
+ this.usesStaticLibrariesCertDigests[index] = sForInternedStringArray.unparcel(
+ in);
+ }
+ }
+ }
- int digestsSize = in.readInt();
- if (digestsSize >= 0) {
- this.usesStaticLibrariesCertDigests = new String[digestsSize][];
- for (int index = 0; index < digestsSize; index++) {
- this.usesStaticLibrariesCertDigests[index] = sForInternedStringArray.unparcel(in);
+ this.usesSdkLibraries = sForInternedStringList.unparcel(in);
+ this.usesSdkLibrariesVersionsMajor = in.createLongArray();
+ {
+ int digestsSize = in.readInt();
+ if (digestsSize >= 0) {
+ this.usesSdkLibrariesCertDigests = new String[digestsSize][];
+ for (int index = 0; index < digestsSize; index++) {
+ this.usesSdkLibrariesCertDigests[index] = sForInternedStringArray.unparcel(in);
+ }
}
}
@@ -1479,6 +1524,17 @@
@Nullable
@Override
+ public String getSdkLibName() {
+ return sdkLibName;
+ }
+
+ @Override
+ public int getSdkLibVersionMajor() {
+ return sdkLibVersionMajor;
+ }
+
+ @Nullable
+ @Override
public String getStaticSharedLibName() {
return staticSharedLibName;
}
@@ -1536,6 +1592,18 @@
return usesStaticLibrariesCertDigests;
}
+ @NonNull
+ @Override
+ public List<String> getUsesSdkLibraries() { return usesSdkLibraries; }
+
+ @Nullable
+ @Override
+ public long[] getUsesSdkLibrariesVersionsMajor() { return usesSdkLibrariesVersionsMajor; }
+
+ @Nullable
+ @Override
+ public String[][] getUsesSdkLibrariesCertDigests() { return usesSdkLibrariesCertDigests; }
+
@Nullable
@Override
public String getSharedUserId() {
@@ -2083,6 +2151,11 @@
}
@Override
+ public boolean isSdkLibrary() {
+ return getBoolean(Booleans.SDK_LIBRARY);
+ }
+
+ @Override
public boolean isOverlay() {
return getBoolean(Booleans.OVERLAY);
}
@@ -2558,6 +2631,23 @@
}
@Override
+ public ParsingPackageImpl setSdkLibName(String sdkLibName) {
+ this.sdkLibName = TextUtils.safeIntern(sdkLibName);
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setSdkLibVersionMajor(int sdkLibVersionMajor) {
+ this.sdkLibVersionMajor = sdkLibVersionMajor;
+ return this;
+ }
+
+ @Override
+ public ParsingPackageImpl setSdkLibrary(boolean value) {
+ return setBoolean(Booleans.SDK_LIBRARY, value);
+ }
+
+ @Override
public ParsingPackageImpl setStaticSharedLibrary(boolean value) {
return setBoolean(Booleans.STATIC_SHARED_LIBRARY, value);
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 2933f95..49b3b08 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -196,6 +196,17 @@
int[] getSplitFlags();
/**
+ * @see R.styleable#AndroidManifestSdkLibrary_name
+ */
+ @Nullable
+ String getSdkLibName();
+
+ /**
+ * @see R.styleable#AndroidManifestSdkLibrary_versionMajor
+ */
+ int getSdkLibVersionMajor();
+
+ /**
* @see R.styleable#AndroidManifestStaticLibrary_name
*/
@Nullable
@@ -267,6 +278,26 @@
@Nullable
long[] getUsesStaticLibrariesVersions();
+ /**
+ * TODO(b/135203078): Move SDK library stuff to an inner data class
+ *
+ * @see R.styleable#AndroidManifestUsesSdkLibrary
+ */
+ @NonNull
+ List<String> getUsesSdkLibraries();
+
+ /**
+ * @see R.styleable#AndroidManifestUsesSdkLibrary_certDigest
+ */
+ @Nullable
+ String[][] getUsesSdkLibrariesCertDigests();
+
+ /**
+ * @see R.styleable#AndroidManifestUsesSdkLibrary_versionMajor
+ */
+ @Nullable
+ long[] getUsesSdkLibrariesVersionsMajor();
+
boolean hasPreserveLegacyExternalStorage();
/**
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index d2ac8739..3e537c8 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -117,6 +117,7 @@
import libcore.io.IoUtils;
import libcore.util.EmptyArray;
+import libcore.util.HexEncoding;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -848,6 +849,8 @@
pkg.addProperty(propertyResult.getResult());
}
return propertyResult;
+ case "uses-sdk-library":
+ return parseUsesSdkLibrary(input, pkg, res, parser);
case "uses-static-library":
return parseUsesStaticLibrary(input, pkg, res, parser);
case "uses-library":
@@ -2212,7 +2215,8 @@
}
}
- if (TextUtils.isEmpty(pkg.getStaticSharedLibName())) {
+ if (TextUtils.isEmpty(pkg.getStaticSharedLibName()) && TextUtils.isEmpty(
+ pkg.getSdkLibName())) {
// Add a hidden app detail activity to normal apps which forwards user to App Details
// page.
ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg);
@@ -2351,10 +2355,14 @@
pkg.addProperty(propertyResult.getResult());
}
return propertyResult;
+ case "sdk-library":
+ return parseSdkLibrary(pkg, res, parser, input);
case "static-library":
return parseStaticLibrary(pkg, res, parser, input);
case "library":
return parseLibrary(pkg, res, parser, input);
+ case "uses-sdk-library":
+ return parseUsesSdkLibrary(input, pkg, res, parser);
case "uses-static-library":
return parseUsesStaticLibrary(input, pkg, res, parser);
case "uses-library":
@@ -2375,6 +2383,41 @@
}
@NonNull
+ private static ParseResult<ParsingPackage> parseSdkLibrary(
+ ParsingPackage pkg, Resources res,
+ XmlResourceParser parser, ParseInput input) {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestSdkLibrary);
+ try {
+ // Note: don't allow this value to be a reference to a resource that may change.
+ String lname = sa.getNonResourceString(
+ R.styleable.AndroidManifestSdkLibrary_name);
+ final int versionMajor = sa.getInt(
+ R.styleable.AndroidManifestSdkLibrary_versionMajor,
+ -1);
+
+ // Fail if malformed.
+ if (lname == null || versionMajor < 0) {
+ return input.error("Bad sdk-library declaration name: " + lname
+ + " version: " + versionMajor);
+ } else if (pkg.getSharedUserId() != null) {
+ return input.error(
+ PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
+ "sharedUserId not allowed in SDK library"
+ );
+ } else if (pkg.getSdkLibName() != null) {
+ return input.error("Multiple SDKs for package "
+ + pkg.getPackageName());
+ }
+
+ return input.success(pkg.setSdkLibName(lname.intern())
+ .setSdkLibVersionMajor(versionMajor)
+ .setSdkLibrary(true));
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ @NonNull
private static ParseResult<ParsingPackage> parseStaticLibrary(
ParsingPackage pkg, Resources res,
XmlResourceParser parser, ParseInput input) {
@@ -2436,6 +2479,68 @@
}
@NonNull
+ private static ParseResult<ParsingPackage> parseUsesSdkLibrary(ParseInput input,
+ ParsingPackage pkg, Resources res, XmlResourceParser parser)
+ throws XmlPullParserException, IOException {
+ TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdkLibrary);
+ try {
+ // Note: don't allow this value to be a reference to a resource that may change.
+ String lname = sa.getNonResourceString(
+ R.styleable.AndroidManifestUsesSdkLibrary_name);
+ final int versionMajor = sa.getInt(
+ R.styleable.AndroidManifestUsesSdkLibrary_versionMajor, -1);
+ String certSha256Digest = sa.getNonResourceString(R.styleable
+ .AndroidManifestUsesSdkLibrary_certDigest);
+
+ // Since an APK providing a static shared lib can only provide the lib - fail if
+ // malformed
+ if (lname == null || versionMajor < 0 || certSha256Digest == null) {
+ return input.error("Bad uses-sdk-library declaration name: " + lname
+ + " version: " + versionMajor + " certDigest" + certSha256Digest);
+ }
+
+ // Can depend only on one version of the same library
+ List<String> usesSdkLibraries = pkg.getUsesSdkLibraries();
+ if (usesSdkLibraries.contains(lname)) {
+ return input.error(
+ "Depending on multiple versions of SDK library " + lname);
+ }
+
+ lname = lname.intern();
+ // We allow ":" delimiters in the SHA declaration as this is the format
+ // emitted by the certtool making it easy for developers to copy/paste.
+ certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
+
+ if ("".equals(certSha256Digest)) {
+ // Test-only uses-sdk-library empty certificate digest override.
+ certSha256Digest = SystemProperties.get(
+ "debug.pm.uses_sdk_library_default_cert_digest", "");
+ // Validate the overridden digest.
+ try {
+ HexEncoding.decode(certSha256Digest, false);
+ } catch (IllegalArgumentException e) {
+ certSha256Digest = "";
+ }
+ }
+
+ ParseResult<String[]> certResult = parseAdditionalCertificates(input, res, parser);
+ if (certResult.isError()) {
+ return input.error(certResult);
+ }
+ String[] additionalCertSha256Digests = certResult.getResult();
+
+ final String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1];
+ certSha256Digests[0] = certSha256Digest;
+ System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
+ 1, additionalCertSha256Digests.length);
+
+ return input.success(pkg.addUsesSdkLibrary(lname, versionMajor, certSha256Digests));
+ } finally {
+ sa.recycle();
+ }
+ }
+
+ @NonNull
private static ParseResult<ParsingPackage> parseUsesStaticLibrary(ParseInput input,
ParsingPackage pkg, Resources res, XmlResourceParser parser)
throws XmlPullParserException, IOException {
@@ -2483,9 +2588,7 @@
System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests,
1, additionalCertSha256Digests.length);
- return input.success(pkg.addUsesStaticLibrary(lname)
- .addUsesStaticLibraryVersion(version)
- .addUsesStaticLibraryCertDigests(certSha256Digests));
+ return input.success(pkg.addUsesStaticLibrary(lname, version, certSha256Digests));
} finally {
sa.recycle();
}
diff --git a/core/java/android/content/pm/parsing/PkgWithoutStateAppInfo.java b/core/java/android/content/pm/parsing/PkgWithoutStateAppInfo.java
index fcad10c..625b9d1 100644
--- a/core/java/android/content/pm/parsing/PkgWithoutStateAppInfo.java
+++ b/core/java/android/content/pm/parsing/PkgWithoutStateAppInfo.java
@@ -21,8 +21,6 @@
import android.content.pm.ApplicationInfo;
import android.util.SparseArray;
-import com.android.internal.R;
-
/**
* Container for fields that are eventually exposed through {@link ApplicationInfo}.
* <p>
@@ -576,6 +574,11 @@
boolean isStaticSharedLibrary();
/**
+ * True means that this package/app contains an SDK library.
+ */
+ boolean isSdkLibrary();
+
+ /**
* If omitted from manifest, returns true if {@link #getTargetSdkVersion()} >= {@link
* android.os.Build.VERSION_CODES#GINGERBREAD}.
*
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/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/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 3d57db9..b8e50fc 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1663,6 +1663,9 @@
@CriticalNative
private static native void nativeScale(long nativePtr, float scale);
+ @CriticalNative
+ private static native int nativeGetSurfaceRotation(long nativePtr);
+
private MotionEvent() {
}
@@ -3805,17 +3808,39 @@
}
/**
- * Gets a rotation matrix that (when applied to a motionevent) will rotate that motion event
- * such that the result coordinates end up in the same physical location on a display whose
- * coordinates are rotated by `rotation`.
+ * Gets the rotation value of the transform for this MotionEvent.
*
- * For example, rotating 0,0 by 90 degrees will move a point from the physical top-left to
- * the bottom-left of the 90-degree-rotated display.
+ * This MotionEvent's rotation can be changed by passing a rotation matrix to
+ * {@link #transform(Matrix)} to change the coordinate space of this event.
+ *
+ * @return the rotation value, or -1 if unknown or invalid.
+ * @see Surface.Rotation
+ * @see #createRotateMatrix(int, int, int)
*
* @hide
*/
+ public @Surface.Rotation int getSurfaceRotation() {
+ return nativeGetSurfaceRotation(mNativePtr);
+ }
+
+ /**
+ * Gets a rotation matrix that (when applied to a MotionEvent) will rotate that motion event
+ * such that the result coordinates end up in the same physical location on a frame whose
+ * coordinates are rotated by `rotation`.
+ *
+ * For example, rotating (0,0) by 90 degrees will move a point from the physical top-left to
+ * the bottom-left of the 90-degree-rotated frame.
+ *
+ * @param rotation the surface rotation of the output matrix
+ * @param rotatedFrameWidth the width of the rotated frame
+ * @param rotatedFrameHeight the height of the rotated frame
+ *
+ * @see #transform(Matrix)
+ * @see #getSurfaceRotation()
+ * @hide
+ */
public static Matrix createRotateMatrix(
- @Surface.Rotation int rotation, int displayW, int displayH) {
+ @Surface.Rotation int rotation, int rotatedFrameWidth, int rotatedFrameHeight) {
if (rotation == Surface.ROTATION_0) {
return new Matrix(Matrix.IDENTITY_MATRIX);
}
@@ -3823,14 +3848,14 @@
float[] values = null;
if (rotation == Surface.ROTATION_90) {
values = new float[]{0, 1, 0,
- -1, 0, displayH,
+ -1, 0, rotatedFrameHeight,
0, 0, 1};
} else if (rotation == Surface.ROTATION_180) {
- values = new float[]{-1, 0, displayW,
- 0, -1, displayH,
+ values = new float[]{-1, 0, rotatedFrameWidth,
+ 0, -1, rotatedFrameHeight,
0, 0, 1};
} else if (rotation == Surface.ROTATION_270) {
- values = new float[]{0, -1, displayW,
+ values = new float[]{0, -1, rotatedFrameWidth,
1, 0, 0,
0, 0, 1};
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 62585c1a..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/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 292f305..07e1a6c 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -769,6 +769,11 @@
event->scale(scale);
}
+static jint android_view_MotionEvent_nativeGetSurfaceRotation(jlong nativePtr) {
+ MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+ return jint(event->getSurfaceRotation());
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gMotionEventMethods[] = {
@@ -845,6 +850,8 @@
{"nativeFindPointerIndex", "(JI)I", (void*)android_view_MotionEvent_nativeFindPointerIndex},
{"nativeGetHistorySize", "(J)I", (void*)android_view_MotionEvent_nativeGetHistorySize},
{"nativeScale", "(JF)V", (void*)android_view_MotionEvent_nativeScale},
+ {"nativeGetSurfaceRotation", "(J)I",
+ (void*)android_view_MotionEvent_nativeGetSurfaceRotation},
};
int register_android_view_MotionEvent(JNIEnv* env) {
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/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 94717b1..fe58114 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2303,6 +2303,36 @@
<attr name="authorities" />
</declare-styleable>
+ <!-- The <code>sdk-library</code> tag declares that this apk is providing itself
+ as an SDK library for other applications to use. Any app can declare an SDK library and there
+ can be only one SDK library per package. These SDK libraries are updatable, multiple major
+ versions can be installed at the same time, and an app depends on a specific version.
+ Other apks can link to it with the {@link #AndroidManifestUsesSdkLibrary uses-sdk-library} tag.
+
+ <p>This appears as a child tag of the {@link #AndroidManifestApplication application} tag. -->
+ <declare-styleable name="AndroidManifestSdkLibrary" parent="AndroidManifestApplication">
+ <!-- Required public name of the SDK library, which other components and packages will use
+ when referring to this SDK library. This is a string using Java-style scoping to ensure
+ it is unique.
+ Both name and version should typically form the apk's package name: name_versionMajor. -->
+ <attr name="name" />
+ <!-- Required major version of the SDK library. -->
+ <attr name="versionMajor" format="integer" />
+ </declare-styleable>
+
+
+ <!-- The <code>uses-sdk-library</code> specifies a shared <strong>SDK</strong> library that this
+ package requires to be present on the device.
+
+ <p>This appears as a child tag of the {@link #AndroidManifestApplication application} tag. -->
+ <declare-styleable name="AndroidManifestUsesSdkLibrary" parent="AndroidManifestApplication">
+ <!-- Required name of the SDK library you use. -->
+ <attr name="name" />
+ <!-- Specify which major version of the SDK library you use. -->
+ <attr name="versionMajor" format="integer" />
+ <!-- The SHA-256 digest of the SDK library signing certificate. -->
+ <attr name="certDigest" format="string" />
+ </declare-styleable>
<!-- The <code>static-library</code> tag declares that this apk is providing itself
as a static shared library for other applications to use. Any app can declare such
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/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index 7f7a7aa..78a8f7b 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -26,6 +26,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
+import android.graphics.Matrix;
import android.platform.test.annotations.Presubmit;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
@@ -174,22 +175,43 @@
@Test
public void testEventRotation() {
+ // The un-rotated frame size.
+ final int width = 600;
+ final int height = 1000;
final MotionEvent event = MotionEvent.obtain(0 /* downTime */, 0 /* eventTime */,
- ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */);
+ ACTION_DOWN, 30 /* x */, 50 /* y */, 0 /* metaState */);
event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ assertEquals(Surface.ROTATION_0, event.getSurfaceRotation());
+
MotionEvent rot90 = MotionEvent.obtain(event);
- rot90.transform(MotionEvent.createRotateMatrix(/* 90 deg */1, 1000, 600));
+ rot90.transform(MotionEvent.createRotateMatrix(Surface.ROTATION_90, height, width));
assertEquals(50, (int) rot90.getX());
assertEquals(570, (int) rot90.getY());
+ assertEquals(Surface.ROTATION_90, rot90.getSurfaceRotation());
MotionEvent rot180 = MotionEvent.obtain(event);
- rot180.transform(MotionEvent.createRotateMatrix(/* 180 deg */2, 1000, 600));
- assertEquals(970, (int) rot180.getX());
- assertEquals(550, (int) rot180.getY());
+ rot180.transform(MotionEvent.createRotateMatrix(Surface.ROTATION_180, width, height));
+ assertEquals(570, (int) rot180.getX());
+ assertEquals(950, (int) rot180.getY());
+ assertEquals(Surface.ROTATION_180, rot180.getSurfaceRotation());
MotionEvent rot270 = MotionEvent.obtain(event);
- rot270.transform(MotionEvent.createRotateMatrix(/* 270 deg */3, 1000, 600));
+ rot270.transform(MotionEvent.createRotateMatrix(Surface.ROTATION_270, height, width));
assertEquals(950, (int) rot270.getX());
assertEquals(30, (int) rot270.getY());
+ assertEquals(Surface.ROTATION_270, rot270.getSurfaceRotation());
+
+ MotionEvent compoundRot = MotionEvent.obtain(event);
+ compoundRot.transform(MotionEvent.createRotateMatrix(Surface.ROTATION_90, height, width));
+ compoundRot.transform(MotionEvent.createRotateMatrix(Surface.ROTATION_180, height, width));
+ assertEquals(950, (int) compoundRot.getX());
+ assertEquals(30, (int) compoundRot.getY());
+ assertEquals(Surface.ROTATION_270, compoundRot.getSurfaceRotation());
+
+ MotionEvent rotInvalid = MotionEvent.obtain(event);
+ Matrix mat = new Matrix();
+ mat.setValues(new float[]{1, 2, 3, -4, -5, -6, 0, 0, 1});
+ rotInvalid.transform(mat);
+ assertEquals(-1, rotInvalid.getSurfaceRotation());
}
}
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/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/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/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/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/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/communal/CommunalManagerUpdater.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalManagerUpdater.java
new file mode 100644
index 0000000..ebe804a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalManagerUpdater.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal;
+
+import android.app.communal.CommunalManager;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.dagger.SysUISingleton;
+
+import java.lang.ref.WeakReference;
+
+import javax.inject.Inject;
+
+/**
+ * The {@link CommunalManagerUpdater} is responsible for forwarding state from SystemUI to
+ * the {@link CommunalManager} system service.
+ */
+@SysUISingleton
+public class CommunalManagerUpdater extends CoreStartable {
+ private static final String TAG = "CommunalManagerUpdater";
+
+ private final CommunalManager mCommunalManager;
+ private final CommunalSourceMonitor mMonitor;
+
+ private final CommunalSourceMonitor.Callback mSourceCallback =
+ new CommunalSourceMonitor.Callback() {
+ @Override
+ public void onSourceAvailable(WeakReference<CommunalSource> source) {
+ try {
+ mCommunalManager.setCommunalViewShowing(
+ source != null && source.get() != null);
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Error updating communal manager service state", e);
+ }
+ }
+ };
+
+ @Inject
+ public CommunalManagerUpdater(Context context, CommunalSourceMonitor monitor) {
+ super(context);
+ mCommunalManager = context.getSystemService(CommunalManager.class);
+ mMonitor = monitor;
+ }
+
+ @Override
+ public void start() {
+ if (mCommunalManager != null) {
+ mMonitor.addCallback(mSourceCallback);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
index 2244532..d3018e3 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSourceMonitor.java
@@ -16,17 +16,11 @@
package com.android.systemui.communal;
-import android.database.ContentObserver;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Log;
-import androidx.annotation.MainThread;
-
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.util.settings.SecureSettings;
import com.google.android.collect.Lists;
@@ -46,10 +40,15 @@
// A list of {@link Callback} that have registered to receive updates.
private final ArrayList<WeakReference<Callback>> mCallbacks = Lists.newArrayList();
- private final SecureSettings mSecureSettings;
+ private final CommunalConditionsMonitor mConditionsMonitor;
private CommunalSource mCurrentSource;
- private boolean mCommunalEnabled;
+
+ // Whether all conditions for communal mode to show have been met.
+ private boolean mAllCommunalConditionsMet = false;
+
+ // Whether the class is currently listening for condition changes.
+ private boolean mListeningForConditions = false;
private CommunalSource.Callback mSourceCallback = new CommunalSource.Callback() {
@Override
@@ -59,24 +58,20 @@
}
};
+ private final CommunalConditionsMonitor.Callback mConditionsCallback =
+ allConditionsMet -> {
+ if (mAllCommunalConditionsMet != allConditionsMet) {
+ if (DEBUG) Log.d(TAG, "communal conditions changed: " + allConditionsMet);
+
+ mAllCommunalConditionsMet = allConditionsMet;
+ executeOnSourceAvailableCallbacks();
+ }
+ };
+
@VisibleForTesting
@Inject
- public CommunalSourceMonitor(
- @MainThread Handler mainThreadHandler,
- SecureSettings secureSettings) {
- mSecureSettings = secureSettings;
-
- ContentObserver settingsObserver = new ContentObserver(mainThreadHandler) {
- @Override
- public void onChange(boolean selfChange) {
- reloadSettings();
- }
- };
- mSecureSettings.registerContentObserverForUser(
- Settings.Secure.COMMUNAL_MODE_ENABLED,
- /* notifyForDescendants= */false,
- settingsObserver, UserHandle.USER_SYSTEM);
- reloadSettings();
+ public CommunalSourceMonitor(CommunalConditionsMonitor communalConditionsMonitor) {
+ mConditionsMonitor = communalConditionsMonitor;
}
/**
@@ -92,7 +87,7 @@
mCurrentSource = source;
- if (mCommunalEnabled) {
+ if (mAllCommunalConditionsMet) {
executeOnSourceAvailableCallbacks();
}
@@ -111,7 +106,7 @@
itr.remove();
} else {
cb.onSourceAvailable(
- (mCommunalEnabled && mCurrentSource != null) ? new WeakReference<>(
+ (mAllCommunalConditionsMet && mCurrentSource != null) ? new WeakReference<>(
mCurrentSource) : null);
}
}
@@ -126,9 +121,14 @@
mCallbacks.add(new WeakReference<>(callback));
// Inform the callback of any already present CommunalSource.
- if (mCommunalEnabled && mCurrentSource != null) {
+ if (mAllCommunalConditionsMet && mCurrentSource != null) {
callback.onSourceAvailable(new WeakReference<>(mCurrentSource));
}
+
+ if (!mListeningForConditions) {
+ mConditionsMonitor.addCallback(mConditionsCallback);
+ mListeningForConditions = true;
+ }
}
/**
@@ -138,21 +138,10 @@
*/
public void removeCallback(Callback callback) {
mCallbacks.removeIf(el -> el.get() == callback);
- }
- private void reloadSettings() {
- boolean newCommunalEnabled = mSecureSettings.getIntForUser(
- Settings.Secure.COMMUNAL_MODE_ENABLED,
- 1,
- UserHandle.USER_SYSTEM) == 1;
-
- if (DEBUG) {
- Log.d(TAG, "communal mode settings reloaded with value:" + newCommunalEnabled);
- }
-
- if (mCommunalEnabled != newCommunalEnabled) {
- mCommunalEnabled = newCommunalEnabled;
- executeOnSourceAvailableCallbacks();
+ if (mCallbacks.isEmpty() && mListeningForConditions) {
+ mConditionsMonitor.removeCallback(mConditionsCallback);
+ mListeningForConditions = false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalStateController.java b/packages/SystemUI/src/com/android/systemui/communal/CommunalStateController.java
index 7be8ecc..c72f542 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalStateController.java
@@ -17,9 +17,6 @@
package com.android.systemui.communal;
import android.annotation.NonNull;
-import android.app.communal.CommunalManager;
-import android.content.Context;
-import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;
@@ -36,9 +33,7 @@
@SysUISingleton
public class CommunalStateController implements
CallbackController<CommunalStateController.Callback> {
- private static final String TAG = CommunalStateController.class.getSimpleName();
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
- private final CommunalManager mCommunalManager;
private boolean mCommunalViewOccluded;
private boolean mCommunalViewShowing;
@@ -61,8 +56,7 @@
@VisibleForTesting
@Inject
- public CommunalStateController(Context context) {
- mCommunalManager = context.getSystemService(CommunalManager.class);
+ public CommunalStateController() {
}
/**
@@ -76,12 +70,6 @@
mCommunalViewShowing = communalViewShowing;
- try {
- mCommunalManager.setCommunalViewShowing(communalViewShowing);
- } catch (RuntimeException e) {
- Log.e(TAG, "Error updating communal manager service state", e);
- }
-
final ArrayList<Callback> callbacks = new ArrayList<>(mCallbacks);
for (Callback callback : callbacks) {
callback.onCommunalViewShowingChanged();
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalCondition.java b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalCondition.java
new file mode 100644
index 0000000..734ab63
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalCondition.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.conditions;
+
+import android.util.Log;
+
+import com.android.systemui.statusbar.policy.CallbackController;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Base class for a condition that needs to be fulfilled in order for Communal Mode to display.
+ */
+public abstract class CommunalCondition implements CallbackController<CommunalCondition.Callback> {
+ private final String mTag = getClass().getSimpleName();
+
+ private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
+ private boolean mIsConditionMet = false;
+ private boolean mStarted = false;
+
+ /**
+ * Starts monitoring the condition.
+ */
+ protected abstract void start();
+
+ /**
+ * Stops monitoring the condition.
+ */
+ protected abstract void stop();
+
+ /**
+ * Registers a callback to receive updates once started. This should be called before
+ * {@link #start()}. Also triggers the callback immediately if already started.
+ */
+ @Override
+ public void addCallback(@NotNull Callback callback) {
+ if (shouldLog()) Log.d(mTag, "adding callback");
+ mCallbacks.add(new WeakReference<>(callback));
+
+ if (mStarted) {
+ callback.onConditionChanged(this, mIsConditionMet);
+ return;
+ }
+
+ start();
+ mStarted = true;
+ }
+
+ /**
+ * Removes the provided callback from further receiving updates.
+ */
+ @Override
+ public void removeCallback(@NotNull Callback callback) {
+ if (shouldLog()) Log.d(mTag, "removing callback");
+ final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ while (iterator.hasNext()) {
+ final Callback cb = iterator.next().get();
+ if (cb == null || cb == callback) {
+ iterator.remove();
+ }
+ }
+
+ if (!mCallbacks.isEmpty() || !mStarted) {
+ return;
+ }
+
+ stop();
+ mStarted = false;
+ }
+
+ /**
+ * Updates the value for whether the condition has been fulfilled, and sends an update if the
+ * value changes and any callback is registered.
+ *
+ * @param isConditionMet True if the condition has been fulfilled. False otherwise.
+ */
+ protected void updateCondition(boolean isConditionMet) {
+ if (mIsConditionMet == isConditionMet) {
+ return;
+ }
+
+ if (shouldLog()) Log.d(mTag, "updating condition to " + isConditionMet);
+ mIsConditionMet = isConditionMet;
+
+ final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ while (iterator.hasNext()) {
+ final Callback cb = iterator.next().get();
+ if (cb == null) {
+ iterator.remove();
+ } else {
+ cb.onConditionChanged(this, mIsConditionMet);
+ }
+ }
+ }
+
+ private boolean shouldLog() {
+ return Log.isLoggable(mTag, Log.DEBUG);
+ }
+
+ /**
+ * Callback that receives updates about whether the condition has been fulfilled.
+ */
+ public interface Callback {
+ /**
+ * Called when the fulfillment of the condition changes.
+ *
+ * @param condition The condition in question.
+ * @param isConditionMet True if the condition has been fulfilled. False otherwise.
+ */
+ void onConditionChanged(CommunalCondition condition, boolean isConditionMet);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java
new file mode 100644
index 0000000..4f772da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalConditionsMonitor.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.conditions;
+
+import static com.android.systemui.communal.dagger.CommunalModule.COMMUNAL_CONDITIONS;
+
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.policy.CallbackController;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * {@link CommunalConditionsMonitor} takes in a set of conditions, monitors whether all of them have
+ * been fulfilled, and informs any registered listeners.
+ */
+@SysUISingleton
+public class CommunalConditionsMonitor implements
+ CallbackController<CommunalConditionsMonitor.Callback> {
+ private final String mTag = getClass().getSimpleName();
+
+ private final ArrayList<WeakReference<Callback>> mCallbacks = new ArrayList<>();
+
+ // Set of all conditions that need to be monitored.
+ private final Set<CommunalCondition> mConditions;
+
+ // Map of values of each condition.
+ private final HashMap<CommunalCondition, Boolean> mConditionsMap = new HashMap<>();
+
+ // Whether all conditions have been met.
+ private boolean mAllConditionsMet = false;
+
+ // Whether the monitor has started listening for all the conditions.
+ private boolean mHaveConditionsStarted = false;
+
+ // Callback for when each condition has been updated.
+ private final CommunalCondition.Callback mConditionCallback = (condition, isConditionMet) -> {
+ mConditionsMap.put(condition, isConditionMet);
+
+ final boolean newAllConditionsMet = !mConditionsMap.containsValue(false);
+
+ if (newAllConditionsMet == mAllConditionsMet) {
+ return;
+ }
+
+ if (shouldLog()) Log.d(mTag, "all conditions met: " + newAllConditionsMet);
+ mAllConditionsMet = newAllConditionsMet;
+
+ // Updates all callbacks.
+ final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ while (iterator.hasNext()) {
+ final Callback callback = iterator.next().get();
+ if (callback == null) {
+ iterator.remove();
+ } else {
+ callback.onConditionsChanged(mAllConditionsMet);
+ }
+ }
+ };
+
+ @Inject
+ public CommunalConditionsMonitor(
+ @Named(COMMUNAL_CONDITIONS) Set<CommunalCondition> communalConditions) {
+ mConditions = communalConditions;
+
+ // Initializes the conditions map and registers a callback for each condition.
+ mConditions.forEach((condition -> mConditionsMap.put(condition, false)));
+ }
+
+ @Override
+ public void addCallback(@NotNull Callback callback) {
+ if (shouldLog()) Log.d(mTag, "adding callback");
+ mCallbacks.add(new WeakReference<>(callback));
+
+ // Updates the callback immediately.
+ callback.onConditionsChanged(mAllConditionsMet);
+
+ if (!mHaveConditionsStarted) {
+ if (shouldLog()) Log.d(mTag, "starting all conditions");
+ mConditions.forEach(condition -> condition.addCallback(mConditionCallback));
+ mHaveConditionsStarted = true;
+ }
+ }
+
+ @Override
+ public void removeCallback(@NotNull Callback callback) {
+ if (shouldLog()) Log.d(mTag, "removing callback");
+ final Iterator<WeakReference<Callback>> iterator = mCallbacks.iterator();
+ while (iterator.hasNext()) {
+ final Callback cb = iterator.next().get();
+ if (cb == null || cb == callback) {
+ iterator.remove();
+ }
+ }
+
+ if (mCallbacks.isEmpty() && mHaveConditionsStarted) {
+ if (shouldLog()) Log.d(mTag, "stopping all conditions");
+ mConditions.forEach(condition -> condition.removeCallback(mConditionCallback));
+
+ mAllConditionsMet = false;
+ mHaveConditionsStarted = false;
+ }
+ }
+
+ /**
+ * Force updates each condition to the value provided.
+ */
+ @VisibleForTesting
+ public void overrideAllConditionsMet(boolean value) {
+ mConditions.forEach(condition -> condition.updateCondition(value));
+ }
+
+ private boolean shouldLog() {
+ return Log.isLoggable(mTag, Log.DEBUG);
+ }
+
+ /**
+ * Callback that receives updates of whether all conditions have been fulfilled.
+ */
+ public interface Callback {
+ /**
+ * Triggered when the fulfillment of all conditions have been met.
+ *
+ * @param allConditionsMet True if all conditions have been fulfilled. False if none or
+ * only partial conditions have been fulfilled.
+ */
+ void onConditionsChanged(boolean allConditionsMet);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalSettingCondition.java b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalSettingCondition.java
new file mode 100644
index 0000000..1616b18
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalSettingCondition.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.conditions;
+
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import androidx.annotation.MainThread;
+
+import com.android.systemui.util.settings.SecureSettings;
+
+import javax.inject.Inject;
+
+/**
+ * Monitors the communal setting, and informs any listeners with updates.
+ */
+public class CommunalSettingCondition extends CommunalCondition {
+ private final SecureSettings mSecureSettings;
+ private final ContentObserver mCommunalSettingContentObserver;
+
+ @Inject
+ public CommunalSettingCondition(@MainThread Handler mainHandler,
+ SecureSettings secureSettings) {
+ mSecureSettings = secureSettings;
+
+ mCommunalSettingContentObserver = new ContentObserver(mainHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ final boolean communalSettingEnabled = mSecureSettings.getIntForUser(
+ Settings.Secure.COMMUNAL_MODE_ENABLED, 0, UserHandle.USER_SYSTEM) == 1;
+ updateCondition(communalSettingEnabled);
+ }
+ };
+ }
+
+ @Override
+ protected void start() {
+ mSecureSettings.registerContentObserverForUser(Settings.Secure.COMMUNAL_MODE_ENABLED,
+ false /*notifyForDescendants*/, mCommunalSettingContentObserver,
+ UserHandle.USER_SYSTEM);
+
+ // Fetches setting immediately.
+ mCommunalSettingContentObserver.onChange(false);
+ }
+
+ @Override
+ protected void stop() {
+ mSecureSettings.unregisterContentObserver(mCommunalSettingContentObserver);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalTrustedNetworkCondition.java b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalTrustedNetworkCondition.java
new file mode 100644
index 0000000..e4692db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/conditions/CommunalTrustedNetworkCondition.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.conditions;
+
+import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.wifi.WifiInfo;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.settings.SecureSettings;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+import javax.inject.Inject;
+
+/**
+ * Monitors Wi-Fi connections and triggers callback, if any, when the device is connected to and
+ * disconnected from a trusted network.
+ */
+public class CommunalTrustedNetworkCondition extends CommunalCondition {
+ private final String mTag = getClass().getSimpleName();
+ private final ConnectivityManager mConnectivityManager;
+ private final ContentObserver mTrustedNetworksObserver;
+ private final SecureSettings mSecureSettings;
+
+ // The SSID of the connected Wi-Fi network. Null if not connected to Wi-Fi.
+ private String mWifiSSID;
+
+ // Set of SSIDs of trusted networks.
+ private final HashSet<String> mTrustedNetworks = new HashSet<>();
+
+ /**
+ * The deliminator used to separate trusted network keys saved as a string in secure settings.
+ */
+ public static final String SETTINGS_STRING_DELIMINATOR = ",/";
+
+ @Inject
+ public CommunalTrustedNetworkCondition(@Main Handler handler,
+ ConnectivityManager connectivityManager, SecureSettings secureSettings) {
+ mConnectivityManager = connectivityManager;
+ mSecureSettings = secureSettings;
+
+ mTrustedNetworksObserver = new ContentObserver(handler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ fetchTrustedNetworks();
+ }
+ };
+ }
+
+ /**
+ * Starts monitoring for trusted network connection. Ignores if already started.
+ */
+ @Override
+ protected void start() {
+ if (shouldLog()) Log.d(mTag, "start listening for wifi connections");
+
+ fetchTrustedNetworks();
+
+ final NetworkRequest wifiNetworkRequest = new NetworkRequest.Builder().addTransportType(
+ NetworkCapabilities.TRANSPORT_WIFI).build();
+ mConnectivityManager.registerNetworkCallback(wifiNetworkRequest, mNetworkCallback);
+ mSecureSettings.registerContentObserverForUser(
+ Settings.Secure.COMMUNAL_MODE_TRUSTED_NETWORKS, false, mTrustedNetworksObserver,
+ UserHandle.USER_SYSTEM);
+ }
+
+ /**
+ * Stops monitoring for trusted network connection.
+ */
+ @Override
+ protected void stop() {
+ if (shouldLog()) Log.d(mTag, "stop listening for wifi connections");
+
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+ mSecureSettings.unregisterContentObserver(mTrustedNetworksObserver);
+ }
+
+ private void updateWifiInfo(WifiInfo wifiInfo) {
+ if (wifiInfo == null) {
+ mWifiSSID = null;
+ } else {
+ // Remove the wrapping quotes around the SSID.
+ mWifiSSID = wifiInfo.getSSID().replace("\"", "");
+ }
+
+ checkIfConnectedToTrustedNetwork();
+ }
+
+ private void fetchTrustedNetworks() {
+ final String trustedNetworksString = mSecureSettings.getStringForUser(
+ Settings.Secure.COMMUNAL_MODE_TRUSTED_NETWORKS, UserHandle.USER_SYSTEM);
+ mTrustedNetworks.clear();
+
+ if (shouldLog()) Log.d(mTag, "fetched trusted networks: " + trustedNetworksString);
+
+ if (TextUtils.isEmpty(trustedNetworksString)) {
+ return;
+ }
+
+ mTrustedNetworks.addAll(
+ Arrays.asList(trustedNetworksString.split(SETTINGS_STRING_DELIMINATOR)));
+
+ checkIfConnectedToTrustedNetwork();
+ }
+
+ private void checkIfConnectedToTrustedNetwork() {
+ final boolean connectedToTrustedNetwork = mWifiSSID != null && mTrustedNetworks.contains(
+ mWifiSSID);
+
+ if (shouldLog()) {
+ Log.d(mTag, (connectedToTrustedNetwork ? "connected to" : "disconnected from")
+ + " a trusted network");
+ }
+
+ updateCondition(connectedToTrustedNetwork);
+ }
+
+ private final ConnectivityManager.NetworkCallback mNetworkCallback =
+ new ConnectivityManager.NetworkCallback(
+ ConnectivityManager.NetworkCallback.FLAG_INCLUDE_LOCATION_INFO) {
+ private boolean mIsConnected = false;
+ private WifiInfo mWifiInfo;
+
+ @Override
+ public void onAvailable(@NonNull Network network) {
+ super.onAvailable(network);
+
+ if (shouldLog()) Log.d(mTag, "connected to wifi");
+
+ mIsConnected = true;
+ if (mWifiInfo != null) {
+ updateWifiInfo(mWifiInfo);
+ }
+ }
+
+ @Override
+ public void onLost(@NonNull Network network) {
+ super.onLost(network);
+
+ if (shouldLog()) Log.d(mTag, "disconnected from wifi");
+
+ mIsConnected = false;
+ mWifiInfo = null;
+ updateWifiInfo(null);
+ }
+
+ @Override
+ public void onCapabilitiesChanged(@NonNull Network network,
+ @NonNull NetworkCapabilities networkCapabilities) {
+ super.onCapabilitiesChanged(network, networkCapabilities);
+
+ mWifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
+
+ if (mIsConnected) {
+ updateWifiInfo(mWifiInfo);
+ }
+ }
+ };
+
+ private boolean shouldLog() {
+ return Log.isLoggable(mTag, Log.DEBUG);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
index 31dd82d..3ebfb51 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
+++ b/packages/SystemUI/src/com/android/systemui/communal/dagger/CommunalModule.java
@@ -20,15 +20,23 @@
import android.view.View;
import android.widget.FrameLayout;
+import com.android.systemui.communal.conditions.CommunalCondition;
+import com.android.systemui.communal.conditions.CommunalSettingCondition;
+import com.android.systemui.communal.conditions.CommunalTrustedNetworkCondition;
import com.android.systemui.idle.AmbientLightModeMonitor;
import com.android.systemui.idle.LightSensorEventsDebounceAlgorithm;
import com.android.systemui.idle.dagger.IdleViewComponent;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
import javax.inject.Named;
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
+import dagger.multibindings.ElementsIntoSet;
/**
* Dagger Module providing Communal-related functionality.
@@ -39,6 +47,7 @@
})
public interface CommunalModule {
String IDLE_VIEW = "idle_view";
+ String COMMUNAL_CONDITIONS = "communal_conditions";
/** */
@Provides
@@ -56,4 +65,17 @@
@Binds
AmbientLightModeMonitor.DebounceAlgorithm ambientLightDebounceAlgorithm(
LightSensorEventsDebounceAlgorithm algorithm);
+
+ /**
+ * Provides a set of conditions that need to be fulfilled in order for Communal Mode to display.
+ */
+ @Provides
+ @ElementsIntoSet
+ @Named(COMMUNAL_CONDITIONS)
+ static Set<CommunalCondition> provideCommunalConditions(
+ CommunalSettingCondition communalSettingCondition,
+ CommunalTrustedNetworkCondition communalTrustedNetworkCondition) {
+ return new HashSet<>(
+ Arrays.asList(communalSettingCondition, communalTrustedNetworkCondition));
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index e5c6ab5..3426148 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -23,6 +23,7 @@
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.accessibility.WindowMagnification;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.communal.CommunalManagerUpdater;
import com.android.systemui.dreams.DreamOverlayRegistrant;
import com.android.systemui.dreams.appwidgets.AppWidgetOverlayPrimer;
import com.android.systemui.globalactions.GlobalActionsComponent;
@@ -204,4 +205,11 @@
@ClassKey(AppWidgetOverlayPrimer.class)
public abstract CoreStartable bindAppWidgetOverlayPrimer(
AppWidgetOverlayPrimer appWidgetOverlayPrimer);
+
+ /** Inject into CommunalManagerUpdater. */
+ @Binds
+ @IntoMap
+ @ClassKey(CommunalManagerUpdater.class)
+ public abstract CoreStartable bindCommunalManagerUpdater(
+ CommunalManagerUpdater communalManagerUpdater);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
index a424674..d73d9cd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewController.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.graphics.Color;
+import android.os.SystemClock;
import android.text.TextUtils;
import androidx.annotation.IntDef;
@@ -40,13 +41,24 @@
import java.util.Map;
/**
- * Rotates through messages to show on the keyguard bottom area on the lock screen
- * NOTE: This controller should not be used on AoD to avoid waking up the AP too often.
+ * Animates through messages to show on the keyguard bottom area on the lock screen.
+ * Utilizes a {@link KeyguardIndicationTextView} for animations. This class handles the rotating
+ * nature of the messages including:
+ * - ensuring a message is shown for its minimum amount of time. Minimum time is determined by
+ * {@link KeyguardIndication#getMinVisibilityMillis()}
+ * - showing the next message after a default of 3.5 seconds before animating to the next
+ * - statically showing a single message if there is only one message to show
+ * - showing certain messages immediately, assuming te current message has been shown for
+ * at least {@link KeyguardIndication#getMinVisibilityMillis()}. For example, transient and
+ * biometric messages are meant to be shown immediately.
+ * - ending animations when dozing begins, and resuming when dozing ends. Rotating messages on
+ * AoD is undesirable since it wakes up the AP too often.
*/
public class KeyguardIndicationRotateTextViewController extends
ViewController<KeyguardIndicationTextView> implements Dumpable {
public static String TAG = "KgIndicationRotatingCtrl";
private static final long DEFAULT_INDICATION_SHOW_LENGTH = 3500; // milliseconds
+ public static final long IMPORTANT_MSG_MIN_DURATION = 2000L + 600L; // 2000ms + [Y in duration]
private final StatusBarStateController mStatusBarStateController;
private final float mMaxAlpha;
@@ -62,6 +74,8 @@
// List of indication types to show. The next indication to show is always at index 0
private final List<Integer> mIndicationQueue = new LinkedList<>();
private @IndicationType int mCurrIndicationType = INDICATION_TYPE_NONE;
+ private CharSequence mCurrMessage;
+ private long mLastIndicationSwitch;
private boolean mIsDozing;
@@ -94,17 +108,19 @@
* Update the indication type with the given String.
* @param type of indication
* @param newIndication message to associate with this indication type
- * @param showImmediately if true: shows this indication message immediately. Else, the text
- * associated with this type is updated and will show when its turn in
- * the IndicationQueue comes around.
+ * @param showAsap if true: shows this indication message as soon as possible. If false,
+ * the text associated with this type is updated and will show when its turn
+ * in the IndicationQueue comes around.
*/
public void updateIndication(@IndicationType int type, KeyguardIndication newIndication,
- boolean updateImmediately) {
+ boolean showAsap) {
if (type == INDICATION_TYPE_REVERSE_CHARGING) {
// temporarily don't show here, instead use AmbientContainer b/181049781
return;
}
- final boolean hasPreviousIndication = mIndicationMessages.get(type) != null;
+ long minShowDuration = getMinVisibilityMillis(mIndicationMessages.get(mCurrIndicationType));
+ final boolean hasPreviousIndication = mIndicationMessages.get(type) != null
+ && !TextUtils.isEmpty(mIndicationMessages.get(type).getMessage());
final boolean hasNewIndication = newIndication != null;
if (!hasNewIndication) {
mIndicationMessages.remove(type);
@@ -121,25 +137,46 @@
return;
}
- final boolean showNow = updateImmediately
- || mCurrIndicationType == INDICATION_TYPE_NONE
- || mCurrIndicationType == type;
+ long currTime = SystemClock.uptimeMillis();
+ long timeSinceLastIndicationSwitch = currTime - mLastIndicationSwitch;
+ boolean currMsgShownForMinTime = timeSinceLastIndicationSwitch >= minShowDuration;
if (hasNewIndication) {
- if (showNow) {
+ if (mCurrIndicationType == INDICATION_TYPE_NONE || mCurrIndicationType == type) {
showIndication(type);
+ } else if (showAsap) {
+ if (currMsgShownForMinTime) {
+ showIndication(type);
+ } else {
+ mIndicationQueue.removeIf(x -> x == type);
+ mIndicationQueue.add(0 /* index */, type /* type */);
+ scheduleShowNextIndication(minShowDuration - timeSinceLastIndicationSwitch);
+ }
} else if (!isNextIndicationScheduled()) {
- scheduleShowNextIndication();
+ long nextShowTime = Math.max(
+ getMinVisibilityMillis(mIndicationMessages.get(type)),
+ DEFAULT_INDICATION_SHOW_LENGTH);
+ if (timeSinceLastIndicationSwitch >= nextShowTime) {
+ showIndication(type);
+ } else {
+ scheduleShowNextIndication(
+ nextShowTime - timeSinceLastIndicationSwitch);
+ }
}
return;
}
+ // current indication is updated to empty
if (mCurrIndicationType == type
&& !hasNewIndication
- && updateImmediately) {
- if (mShowNextIndicationRunnable != null) {
- mShowNextIndicationRunnable.runImmediately();
+ && showAsap) {
+ if (currMsgShownForMinTime) {
+ if (mShowNextIndicationRunnable != null) {
+ mShowNextIndicationRunnable.runImmediately();
+ } else {
+ showIndication(INDICATION_TYPE_NONE);
+ }
} else {
- showIndication(INDICATION_TYPE_NONE);
+ scheduleShowNextIndication(minShowDuration - timeSinceLastIndicationSwitch);
}
}
}
@@ -164,11 +201,10 @@
* - will continue to be in the rotation of messages shown until hideTransient is called.
*/
public void showTransient(CharSequence newIndication) {
- final long inAnimationDuration = 600L; // see KeyguardIndicationTextView.getYInDuration
updateIndication(INDICATION_TYPE_TRANSIENT,
new KeyguardIndication.Builder()
.setMessage(newIndication)
- .setMinVisibilityMillis(2000L + inAnimationDuration)
+ .setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION)
.setTextColor(mInitialTextColorState)
.build(),
/* showImmediately */true);
@@ -189,6 +225,15 @@
}
/**
+ * Clears all messages in the queue and sets the current message to an empty string.
+ */
+ public void clearMessages() {
+ mCurrIndicationType = INDICATION_TYPE_NONE;
+ mIndicationQueue.clear();
+ mView.clearMessages();
+ }
+
+ /**
* Immediately show the passed indication type and schedule the next indication to show.
* Will re-add this indication to be re-shown after all other indications have been
* rotated through.
@@ -196,27 +241,52 @@
private void showIndication(@IndicationType int type) {
cancelScheduledIndication();
+ final CharSequence previousMessage = mCurrMessage;
+ final @IndicationType int previousIndicationType = mCurrIndicationType;
mCurrIndicationType = type;
+ mCurrMessage = mIndicationMessages.get(type) != null
+ ? mIndicationMessages.get(type).getMessage()
+ : null;
+
mIndicationQueue.removeIf(x -> x == type);
if (mCurrIndicationType != INDICATION_TYPE_NONE) {
mIndicationQueue.add(type); // re-add to show later
}
- mView.switchIndication(mIndicationMessages.get(type));
+ mLastIndicationSwitch = SystemClock.uptimeMillis();
+ if (!TextUtils.equals(previousMessage, mCurrMessage)
+ || previousIndicationType != mCurrIndicationType) {
+ mView.switchIndication(mIndicationMessages.get(type));
+ }
// only schedule next indication if there's more than just this indication in the queue
if (mCurrIndicationType != INDICATION_TYPE_NONE && mIndicationQueue.size() > 1) {
- scheduleShowNextIndication();
+ scheduleShowNextIndication(Math.max(
+ getMinVisibilityMillis(mIndicationMessages.get(type)),
+ DEFAULT_INDICATION_SHOW_LENGTH));
}
}
+ private long getMinVisibilityMillis(KeyguardIndication indication) {
+ if (indication == null) {
+ return 0;
+ }
+
+ if (indication.getMinVisibilityMillis() == null) {
+ return 0;
+ }
+
+ return indication.getMinVisibilityMillis();
+ }
+
protected boolean isNextIndicationScheduled() {
return mShowNextIndicationRunnable != null;
}
- private void scheduleShowNextIndication() {
+
+ private void scheduleShowNextIndication(long msUntilShowNextMsg) {
cancelScheduledIndication();
- mShowNextIndicationRunnable = new ShowNextIndication(DEFAULT_INDICATION_SHOW_LENGTH);
+ mShowNextIndicationRunnable = new ShowNextIndication(msUntilShowNextMsg);
}
private void cancelScheduledIndication() {
@@ -292,7 +362,9 @@
}
}
+ // only used locally to stop showing any messages & stop the rotating messages
static final int INDICATION_TYPE_NONE = -1;
+
public static final int INDICATION_TYPE_OWNER_INFO = 0;
public static final int INDICATION_TYPE_DISCLOSURE = 1;
public static final int INDICATION_TYPE_LOGOUT = 2;
@@ -303,6 +375,7 @@
public static final int INDICATION_TYPE_RESTING = 7;
public static final int INDICATION_TYPE_USER_LOCKED = 8;
public static final int INDICATION_TYPE_REVERSE_CHARGING = 10;
+ public static final int INDICATION_TYPE_BIOMETRIC_MESSAGE = 11;
@IntDef({
INDICATION_TYPE_NONE,
@@ -316,6 +389,7 @@
INDICATION_TYPE_RESTING,
INDICATION_TYPE_USER_LOCKED,
INDICATION_TYPE_REVERSE_CHARGING,
+ INDICATION_TYPE_BIOMETRIC_MESSAGE
})
@Retention(RetentionPolicy.SOURCE)
public @interface IndicationType{}
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/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 25337b6..42b7cc3 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -109,6 +109,7 @@
UserTracker userTracker,
DumpManager dumpManager) {
mContext = context;
+ mContentResolver = mContext.getContentResolver();
mAccessibilityManager = accessibilityManager;
mAssistManagerLazy = assistManagerLazy;
mStatusBarOptionalLazy = statusBarOptionalLazy;
@@ -124,7 +125,6 @@
}
public void init() {
- mContentResolver = mContext.getContentResolver();
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ASSISTANT),
false /* notifyForDescendants */, mAssistContentObserver, UserHandle.USER_ALL);
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/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index f23a7ca..f43d9c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -21,8 +21,10 @@
import static android.view.View.VISIBLE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.IMPORTANT_MSG_MIN_DURATION;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_LOGOUT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO;
@@ -94,6 +96,15 @@
/**
* Controls the indications and error messages shown on the Keyguard
+ *
+ * On AoD, only one message shows with the following priorities:
+ * 1. Biometric
+ * 2. Transient
+ * 3. Charging alignment
+ * 4. Battery information
+ *
+ * On the lock screen, message rotate through different message types.
+ * See {@link KeyguardIndicationRotateTextViewController.IndicationType} for the list of types.
*/
@SysUISingleton
public class KeyguardIndicationController {
@@ -103,6 +114,7 @@
private static final int MSG_HIDE_TRANSIENT = 1;
private static final int MSG_SHOW_ACTION_TO_UNLOCK = 2;
+ private static final int MSG_HIDE_BIOMETRIC_MESSAGE = 3;
private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300;
private static final float BOUNCE_ANIMATION_FINAL_Y = 0f;
@@ -132,9 +144,9 @@
private String mRestingIndication;
private String mAlignmentIndication;
private CharSequence mTransientIndication;
+ private CharSequence mBiometricMessage;
protected ColorStateList mInitialTextColorState;
private boolean mVisible;
- private boolean mHideTransientMessageOnScreenOff;
private boolean mPowerPluggedIn;
private boolean mPowerPluggedInWired;
@@ -277,13 +289,15 @@
}
/**
- * Doesn't include disclosure which gets triggered separately.
+ * Doesn't include disclosure (also a persistent indication) which gets triggered separately.
+ *
+ * This method also doesn't update transient messages like biometrics since those messages
+ * are also updated separately.
*/
- private void updateIndications(boolean animate, int userId) {
+ private void updatePersistentIndications(boolean animate, int userId) {
updateOwnerInfo();
updateBattery(animate);
updateUserLocked(userId);
- updateTransient();
updateTrust(userId, getTrustGrantedIndication(), getTrustManagedIndication());
updateAlignment();
updateLogoutView();
@@ -383,12 +397,36 @@
}
}
+ private void updateBiometricMessage() {
+ if (!TextUtils.isEmpty(mBiometricMessage)) {
+ mRotateTextViewController.updateIndication(
+ INDICATION_TYPE_BIOMETRIC_MESSAGE,
+ new KeyguardIndication.Builder()
+ .setMessage(mBiometricMessage)
+ .setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION)
+ .setTextColor(mInitialTextColorState)
+ .build(),
+ true
+ );
+ } else {
+ mRotateTextViewController.hideIndication(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+ }
+
+ if (mDozing) {
+ updateIndication(false);
+ }
+ }
+
private void updateTransient() {
if (!TextUtils.isEmpty(mTransientIndication)) {
mRotateTextViewController.showTransient(mTransientIndication);
} else {
mRotateTextViewController.hideTransient();
}
+
+ if (mDozing) {
+ updateIndication(false);
+ }
}
private void updateTrust(int userId, CharSequence trustGrantedIndication,
@@ -577,6 +615,14 @@
}
/**
+ * Hides biometric indication in {@param delayMs}.
+ */
+ public void hideBiometricMessageDelayed(long delayMs) {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_HIDE_BIOMETRIC_MESSAGE), delayMs);
+ }
+
+ /**
* Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
*/
public void showTransientIndication(int transientIndication) {
@@ -586,23 +632,40 @@
/**
* Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
*/
- public void showTransientIndication(CharSequence transientIndication) {
- showTransientIndication(transientIndication, false /* isError */,
- false /* hideOnScreenOff */);
+ private void showTransientIndication(CharSequence transientIndication) {
+ mTransientIndication = transientIndication;
+ mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+ hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
+
+ updateTransient();
}
/**
- * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
+ * Shows {@param biometricMessage} until it is hidden by {@link #hideBiometricMessage}.
*/
- private void showTransientIndication(CharSequence transientIndication,
- boolean isError, boolean hideOnScreenOff) {
- mTransientIndication = transientIndication;
- mHideTransientMessageOnScreenOff = hideOnScreenOff && transientIndication != null;
- mHandler.removeMessages(MSG_HIDE_TRANSIENT);
- mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK);
- hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
+ public void showBiometricMessage(int biometricMessage) {
+ showBiometricMessage(mContext.getResources().getString(biometricMessage));
+ }
- updateIndication(false);
+ /**
+ * Shows {@param biometricMessage} until it is hidden by {@link #hideBiometricMessage}.
+ */
+ private void showBiometricMessage(CharSequence biometricMessage) {
+ mBiometricMessage = biometricMessage;
+
+ mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK);
+ mHandler.removeMessages(MSG_HIDE_BIOMETRIC_MESSAGE);
+ hideBiometricMessageDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
+
+ updateBiometricMessage();
+ }
+
+ private void hideBiometricMessage() {
+ if (mBiometricMessage != null) {
+ mBiometricMessage = null;
+ mHandler.removeMessages(MSG_HIDE_BIOMETRIC_MESSAGE);
+ updateBiometricMessage();
+ }
}
/**
@@ -611,10 +674,8 @@
public void hideTransientIndication() {
if (mTransientIndication != null) {
mTransientIndication = null;
- mHideTransientMessageOnScreenOff = false;
mHandler.removeMessages(MSG_HIDE_TRANSIENT);
- mRotateTextViewController.hideTransient();
- updateIndication(false);
+ updateTransient();
}
}
@@ -635,7 +696,11 @@
// When dozing we ignore any text color and use white instead, because
// colors can be hard to read in low brightness.
mTopIndicationView.setTextColor(Color.WHITE);
- if (!TextUtils.isEmpty(mTransientIndication)) {
+ if (!TextUtils.isEmpty(mBiometricMessage)) {
+ mWakeLock.setAcquired(true);
+ mTopIndicationView.switchIndication(mBiometricMessage, null,
+ true, () -> mWakeLock.setAcquired(false));
+ } else if (!TextUtils.isEmpty(mTransientIndication)) {
mWakeLock.setAcquired(true);
mTopIndicationView.switchIndication(mTransientIndication, null,
true, () -> mWakeLock.setAcquired(false));
@@ -669,7 +734,7 @@
mTopIndicationView.setVisibility(GONE);
mTopIndicationView.setText(null);
mLockScreenIndicationView.setVisibility(View.VISIBLE);
- updateIndications(animate, KeyguardUpdateMonitor.getCurrentUser());
+ updatePersistentIndications(animate, KeyguardUpdateMonitor.getCurrentUser());
}
// animates textView - textView moves up and bounces down
@@ -798,6 +863,8 @@
hideTransientIndication();
} else if (msg.what == MSG_SHOW_ACTION_TO_UNLOCK) {
showActionToUnlock();
+ } else if (msg.what == MSG_HIDE_BIOMETRIC_MESSAGE) {
+ hideBiometricMessage();
}
}
};
@@ -820,8 +887,7 @@
mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
}
} else {
- showTransientIndication(mContext.getString(R.string.keyguard_unlock),
- false /* isError */, true /* hideOnScreenOff */);
+ showBiometricMessage(mContext.getString(R.string.keyguard_unlock));
}
}
@@ -830,15 +896,15 @@
// if udfps available, there will always be a tappable affordance to unlock
// For example, the lock icon
if (mKeyguardBypassController.getUserHasDeviceEntryIntent()) {
- showTransientIndication(R.string.keyguard_unlock_press);
+ showBiometricMessage(R.string.keyguard_unlock_press);
} else if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
// since face is locked out, simply show "try fingerprint"
- showTransientIndication(R.string.keyguard_try_fingerprint);
+ showBiometricMessage(R.string.keyguard_try_fingerprint);
} else {
- showTransientIndication(R.string.keyguard_face_failed_use_fp);
+ showBiometricMessage(R.string.keyguard_face_failed_use_fp);
}
} else {
- showTransientIndication(R.string.keyguard_try_fingerprint);
+ showBiometricMessage(R.string.keyguard_try_fingerprint);
}
// Although we suppress face auth errors visually, we still announce them for a11y
@@ -857,6 +923,8 @@
pw.println(" mChargingWattage: " + mChargingWattage);
pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn);
pw.println(" mDozing: " + mDozing);
+ pw.println(" mTransientIndication: " + mTransientIndication);
+ pw.println(" mBiometricMessage: " + mBiometricMessage);
pw.println(" mBatteryLevel: " + mBatteryLevel);
pw.println(" mBatteryPresent: " + mBatteryPresent);
pw.println(" mTextView.getText(): " + (
@@ -871,7 +939,7 @@
@Override
public void onRefreshBatteryInfo(BatteryStatus status) {
boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
- || status.status == BatteryManager.BATTERY_STATUS_FULL;
+ || status.isCharged();
boolean wasPluggedIn = mPowerPluggedIn;
mPowerPluggedInWired = status.isPluggedInWired() && isChargingOrFull;
mPowerPluggedInWireless = status.isPluggedInWireless() && isChargingOrFull;
@@ -912,7 +980,6 @@
.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)) {
return;
}
-
boolean showActionToUnlock =
msgId == KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
@@ -921,14 +988,10 @@
} else if (mKeyguardUpdateMonitor.isScreenOn()) {
if (biometricSourceType == BiometricSourceType.FACE
&& shouldSuppressFaceMsgAndShowTryFingerprintMsg()) {
- // don't show any help messages, b/c they can come in right before a success
- // However, continue to announce help messages for a11y
- if (!TextUtils.isEmpty(helpString)) {
- mLockScreenIndicationView.announceForAccessibility(helpString);
- }
+ showTryFingerprintMsg(msgId, helpString);
return;
}
- showTransientIndication(helpString, false /* isError */, showActionToUnlock);
+ showBiometricMessage(helpString);
} else if (showActionToUnlock) {
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SHOW_ACTION_TO_UNLOCK),
TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
@@ -967,8 +1030,7 @@
} else if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState);
} else if (mKeyguardUpdateMonitor.isScreenOn()) {
- showTransientIndication(errString, /* isError */ true,
- /* hideOnScreenOff */ true);
+ showBiometricMessage(errString);
} else {
mMessageToShowOnScreenOn = errString;
}
@@ -1014,16 +1076,15 @@
@Override
public void onTrustAgentErrorMessage(CharSequence message) {
- showTransientIndication(message, true /* isError */, false /* hideOnScreenOff */);
+ showBiometricMessage(message);
}
@Override
public void onScreenTurnedOn() {
if (mMessageToShowOnScreenOn != null) {
- showTransientIndication(mMessageToShowOnScreenOn, true /* isError */,
- false /* hideOnScreenOff */);
+ showBiometricMessage(mMessageToShowOnScreenOn);
// We want to keep this message around in case the screen was off
- hideTransientIndicationDelayed(HIDE_DELAY_MS);
+ hideBiometricMessageDelayed(HIDE_DELAY_MS);
mMessageToShowOnScreenOn = null;
}
}
@@ -1034,7 +1095,7 @@
if (running && biometricSourceType == BiometricSourceType.FACE) {
// Let's hide any previous messages when authentication starts, otherwise
// multiple auth attempts would overlap.
- hideTransientIndication();
+ hideBiometricMessage();
mMessageToShowOnScreenOn = null;
}
}
@@ -1043,11 +1104,11 @@
public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
boolean isStrongBiometric) {
super.onBiometricAuthenticated(userId, biometricSourceType, isStrongBiometric);
- mHandler.sendEmptyMessage(MSG_HIDE_TRANSIENT);
+ hideBiometricMessage();
if (biometricSourceType == BiometricSourceType.FACE
&& !mKeyguardBypassController.canBypass()) {
- mHandler.sendEmptyMessage(MSG_SHOW_ACTION_TO_UNLOCK);
+ showActionToUnlock();
}
}
@@ -1074,8 +1135,7 @@
@Override
public void onRequireUnlockForNfc() {
- showTransientIndication(mContext.getString(R.string.require_unlock_for_nfc),
- false /* isError */, false /* hideOnScreenOff */);
+ showTransientIndication(mContext.getString(R.string.require_unlock_for_nfc));
hideTransientIndicationDelayed(HIDE_DELAY_MS);
}
}
@@ -1094,8 +1154,8 @@
}
mDozing = dozing;
- if (mHideTransientMessageOnScreenOff && mDozing) {
- hideTransientIndication();
+ if (mDozing) {
+ hideBiometricMessage();
}
updateIndication(false);
}
@@ -1112,7 +1172,7 @@
public void onKeyguardShowingChanged() {
if (!mKeyguardStateController.isShowing()) {
mTopIndicationView.clearMessages();
- mLockScreenIndicationView.clearMessages();
+ mRotateTextViewController.clearMessages();
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
index 3a68b9c..339f371 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardIndicationTextView.java
@@ -35,23 +35,20 @@
import com.android.systemui.animation.Interpolators;
import com.android.systemui.keyguard.KeyguardIndication;
-import java.util.LinkedList;
-
/**
* A view to show hints on Keyguard ("Swipe up to unlock", "Tap again to open").
*/
public class KeyguardIndicationTextView extends TextView {
- private static final long MSG_MIN_DURATION_MILLIS_DEFAULT = 1500;
-
@StyleRes
private static int sStyleId = R.style.TextAppearance_Keyguard_BottomArea;
@StyleRes
private static int sButtonStyleId = R.style.TextAppearance_Keyguard_BottomArea_Button;
- private long mNextAnimationTime = 0;
private boolean mAnimationsEnabled = true;
- private LinkedList<CharSequence> mMessages = new LinkedList<>();
- private LinkedList<KeyguardIndication> mKeyguardIndicationInfo = new LinkedList<>();
+ private CharSequence mMessage;
+ private KeyguardIndication mKeyguardIndicationInfo;
+
+ private Animator mLastAnimator;
public KeyguardIndicationTextView(Context context) {
super(context);
@@ -71,22 +68,24 @@
}
/**
- * Clears message queue.
+ * Clears message queue and currently shown message.
*/
public void clearMessages() {
- mMessages.clear();
- mKeyguardIndicationInfo.clear();
+ if (mLastAnimator != null) {
+ mLastAnimator.cancel();
+ }
+ setText("");
}
/**
- * Changes the text with an animation and makes sure a single indication is shown long enough.
+ * Changes the text with an animation.
*/
public void switchIndication(int textResId) {
switchIndication(getResources().getText(textResId), null);
}
/**
- * Changes the text with an animation and makes sure a single indication is shown long enough.
+ * Changes the text with an animation.
*
* @param indication The text to show.
*/
@@ -95,15 +94,14 @@
}
/**
- * Changes the text with an animation. Makes sure a single indication is shown long enough.
+ * Changes the text with an animation.
*/
public void switchIndication(CharSequence text, KeyguardIndication indication) {
switchIndication(text, indication, true, null);
}
/**
- * Changes the text with an optional animation. For animating text, makes sure a single
- * indication is shown long enough.
+ * Updates the text with an optional animation.
*
* @param text The text to show.
* @param indication optional display information for the text
@@ -112,33 +110,15 @@
*/
public void switchIndication(CharSequence text, KeyguardIndication indication,
boolean animate, Runnable onAnimationEndCallback) {
- if (text == null) text = "";
-
- CharSequence lastPendingMessage = mMessages.peekLast();
- if (TextUtils.equals(lastPendingMessage, text)
- || (lastPendingMessage == null && TextUtils.equals(text, getText()))) {
- if (onAnimationEndCallback != null) {
- onAnimationEndCallback.run();
- }
- return;
- }
- mMessages.add(text);
- mKeyguardIndicationInfo.add(indication);
+ mMessage = text;
+ mKeyguardIndicationInfo = indication;
if (animate) {
final boolean hasIcon = indication != null && indication.getIcon() != null;
- final AnimatorSet animator = new AnimatorSet();
+ AnimatorSet animator = new AnimatorSet();
// Make sure each animation is visible for a minimum amount of time, while not worrying
// about fading in blank text
- long timeInMillis = System.currentTimeMillis();
- long delay = Math.max(0, mNextAnimationTime - timeInMillis);
- setNextAnimationTime(timeInMillis + delay + getFadeOutDuration());
- final long minDurationMillis =
- (indication != null && indication.getMinVisibilityMillis() != null)
- ? indication.getMinVisibilityMillis()
- : MSG_MIN_DURATION_MILLIS_DEFAULT;
- if (!text.equals("") || hasIcon) {
- setNextAnimationTime(mNextAnimationTime + minDurationMillis);
+ if (!TextUtils.isEmpty(mMessage) || hasIcon) {
Animator inAnimator = getInAnimator();
inAnimator.addListener(new AnimatorListenerAdapter() {
@Override
@@ -164,7 +144,10 @@
animator.play(outAnimator);
}
- animator.setStartDelay(delay);
+ if (mLastAnimator != null) {
+ mLastAnimator.cancel();
+ }
+ mLastAnimator = animator;
animator.start();
} else {
setAlpha(1f);
@@ -173,6 +156,10 @@
if (onAnimationEndCallback != null) {
onAnimationEndCallback.run();
}
+ if (mLastAnimator != null) {
+ mLastAnimator.cancel();
+ mLastAnimator = null;
+ }
}
}
@@ -182,10 +169,20 @@
fadeOut.setDuration(getFadeOutDuration());
fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
fadeOut.addListener(new AnimatorListenerAdapter() {
+ private boolean mCancelled = false;
@Override
public void onAnimationEnd(Animator animator) {
super.onAnimationEnd(animator);
- setNextIndication();
+ if (!mCancelled) {
+ setNextIndication();
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animator) {
+ super.onAnimationCancel(animator);
+ mCancelled = true;
+ setAlpha(0);
}
});
@@ -198,20 +195,19 @@
}
private void setNextIndication() {
- KeyguardIndication info = mKeyguardIndicationInfo.poll();
- if (info != null) {
+ if (mKeyguardIndicationInfo != null) {
// First, update the style.
// If a background is set on the text, we don't want shadow on the text
- if (info.getBackground() != null) {
+ if (mKeyguardIndicationInfo.getBackground() != null) {
setTextAppearance(sButtonStyleId);
} else {
setTextAppearance(sStyleId);
}
- setBackground(info.getBackground());
- setTextColor(info.getTextColor());
- setOnClickListener(info.getClickListener());
- setClickable(info.getClickListener() != null);
- final Drawable icon = info.getIcon();
+ setBackground(mKeyguardIndicationInfo.getBackground());
+ setTextColor(mKeyguardIndicationInfo.getTextColor());
+ setOnClickListener(mKeyguardIndicationInfo.getClickListener());
+ setClickable(mKeyguardIndicationInfo.getClickListener() != null);
+ final Drawable icon = mKeyguardIndicationInfo.getIcon();
if (icon != null) {
icon.setTint(getCurrentTextColor());
if (icon instanceof AnimatedVectorDrawable) {
@@ -220,7 +216,7 @@
}
setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null);
}
- setText(mMessages.poll());
+ setText(mMessage);
}
private AnimatorSet getInAnimator() {
@@ -238,6 +234,7 @@
public void onAnimationCancel(Animator animation) {
super.onAnimationCancel(animation);
setTranslationY(0);
+ setAlpha(1f);
}
});
animatorSet.playTogether(yTranslate, fadeIn);
@@ -270,14 +267,6 @@
return 167L;
}
- private void setNextAnimationTime(long time) {
- if (mAnimationsEnabled) {
- mNextAnimationTime = time;
- } else {
- mNextAnimationTime = 0L;
- }
- }
-
private int getYTranslationPixels() {
return mContext.getResources().getDimensionPixelSize(
com.android.systemui.R.dimen.keyguard_indication_y_translation);
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/tests/src/com/android/systemui/communal/CommunalConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalConditionTest.java
new file mode 100644
index 0000000..9d7ef0f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalConditionTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.conditions.CommunalCondition;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class CommunalConditionTest extends SysuiTestCase {
+ private FakeCommunalCondition mCondition;
+
+ @Before
+ public void setup() {
+ mCondition = spy(new FakeCommunalCondition());
+ }
+
+ @Test
+ public void addCallback_addFirstCallback_triggerStart() {
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+ verify(mCondition).start();
+ }
+
+ @Test
+ public void addCallback_addMultipleCallbacks_triggerStartOnlyOnce() {
+ final CommunalCondition.Callback callback1 = mock(CommunalCondition.Callback.class);
+ final CommunalCondition.Callback callback2 = mock(CommunalCondition.Callback.class);
+ final CommunalCondition.Callback callback3 = mock(CommunalCondition.Callback.class);
+
+ mCondition.addCallback(callback1);
+ mCondition.addCallback(callback2);
+ mCondition.addCallback(callback3);
+
+ verify(mCondition, times(1)).start();
+ }
+
+ @Test
+ public void addCallback_alreadyStarted_triggerUpdate() {
+ final CommunalCondition.Callback callback1 = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback1);
+
+ mCondition.fakeUpdateCondition(true);
+
+ final CommunalCondition.Callback callback2 = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback2);
+ verify(callback2).onConditionChanged(mCondition, true);
+ }
+
+ @Test
+ public void removeCallback_removeLastCallback_triggerStop() {
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+ verify(mCondition, never()).stop();
+
+ mCondition.removeCallback(callback);
+ verify(mCondition).stop();
+ }
+
+ @Test
+ public void updateCondition_falseToTrue_reportTrue() {
+ mCondition.fakeUpdateCondition(false);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+
+ mCondition.fakeUpdateCondition(true);
+ verify(callback).onConditionChanged(eq(mCondition), eq(true));
+ }
+
+ @Test
+ public void updateCondition_trueToFalse_reportFalse() {
+ mCondition.fakeUpdateCondition(true);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+
+ mCondition.fakeUpdateCondition(false);
+ verify(callback).onConditionChanged(eq(mCondition), eq(false));
+ }
+
+ @Test
+ public void updateCondition_trueToTrue_reportNothing() {
+ mCondition.fakeUpdateCondition(true);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+
+ mCondition.fakeUpdateCondition(true);
+ verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+ }
+
+ @Test
+ public void updateCondition_falseToFalse_reportNothing() {
+ mCondition.fakeUpdateCondition(false);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+
+ mCondition.fakeUpdateCondition(false);
+ verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalConditionsMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalConditionsMonitorTest.java
new file mode 100644
index 0000000..59ddba1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalConditionsMonitorTest.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.conditions.CommunalCondition;
+import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class CommunalConditionsMonitorTest extends SysuiTestCase {
+ private FakeCommunalCondition mCondition1;
+ private FakeCommunalCondition mCondition2;
+ private FakeCommunalCondition mCondition3;
+ private HashSet<CommunalCondition> mConditions;
+
+ private CommunalConditionsMonitor mCommunalConditionsMonitor;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ mCondition1 = spy(new FakeCommunalCondition());
+ mCondition2 = spy(new FakeCommunalCondition());
+ mCondition3 = spy(new FakeCommunalCondition());
+ mConditions = new HashSet<>(Arrays.asList(mCondition1, mCondition2, mCondition3));
+
+ mCommunalConditionsMonitor = new CommunalConditionsMonitor(mConditions);
+ }
+
+ @Test
+ public void addCallback_addFirstCallback_addCallbackToAllConditions() {
+ final CommunalConditionsMonitor.Callback callback1 =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback1);
+ mConditions.forEach(condition -> verify(condition).addCallback(any()));
+
+ final CommunalConditionsMonitor.Callback callback2 =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback2);
+ mConditions.forEach(condition -> verify(condition, times(1)).addCallback(any()));
+ }
+
+ @Test
+ public void addCallback_addFirstCallback_reportWithDefaultValue() {
+ final CommunalConditionsMonitor.Callback callback =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback);
+ verify(callback).onConditionsChanged(false);
+ }
+
+ @Test
+ public void addCallback_addSecondCallback_reportWithExistingValue() {
+ final CommunalConditionsMonitor.Callback callback1 =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback1);
+
+ mCommunalConditionsMonitor.overrideAllConditionsMet(true);
+
+ final CommunalConditionsMonitor.Callback callback2 =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback2);
+ verify(callback2).onConditionsChanged(true);
+ }
+
+ @Test
+ public void removeCallback_shouldNoLongerReceiveUpdate() {
+ final CommunalConditionsMonitor.Callback callback =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback);
+ clearInvocations(callback);
+ mCommunalConditionsMonitor.removeCallback(callback);
+
+ mCommunalConditionsMonitor.overrideAllConditionsMet(true);
+ verify(callback, never()).onConditionsChanged(true);
+
+ mCommunalConditionsMonitor.overrideAllConditionsMet(false);
+ verify(callback, never()).onConditionsChanged(false);
+ }
+
+ @Test
+ public void removeCallback_removeLastCallback_removeCallbackFromAllConditions() {
+ final CommunalConditionsMonitor.Callback callback1 =
+ mock(CommunalConditionsMonitor.Callback.class);
+ final CommunalConditionsMonitor.Callback callback2 =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback1);
+ mCommunalConditionsMonitor.addCallback(callback2);
+
+ mCommunalConditionsMonitor.removeCallback(callback1);
+ mConditions.forEach(condition -> verify(condition, never()).removeCallback(any()));
+
+ mCommunalConditionsMonitor.removeCallback(callback2);
+ mConditions.forEach(condition -> verify(condition).removeCallback(any()));
+ }
+
+ @Test
+ public void updateCallbacks_allConditionsMet_reportTrue() {
+ final CommunalConditionsMonitor.Callback callback =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback);
+ clearInvocations(callback);
+
+ mCondition1.fakeUpdateCondition(true);
+ mCondition2.fakeUpdateCondition(true);
+ mCondition3.fakeUpdateCondition(true);
+
+ verify(callback).onConditionsChanged(true);
+ }
+
+ @Test
+ public void updateCallbacks_oneConditionStoppedMeeting_reportFalse() {
+ final CommunalConditionsMonitor.Callback callback =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback);
+
+ mCondition1.fakeUpdateCondition(true);
+ mCondition2.fakeUpdateCondition(true);
+ mCondition3.fakeUpdateCondition(true);
+ clearInvocations(callback);
+
+ mCondition1.fakeUpdateCondition(false);
+ verify(callback).onConditionsChanged(false);
+ }
+
+ @Test
+ public void updateCallbacks_shouldOnlyUpdateWhenValueChanges() {
+ final CommunalConditionsMonitor.Callback callback =
+ mock(CommunalConditionsMonitor.Callback.class);
+ mCommunalConditionsMonitor.addCallback(callback);
+ verify(callback).onConditionsChanged(false);
+ clearInvocations(callback);
+
+ mCondition1.fakeUpdateCondition(true);
+ verify(callback, never()).onConditionsChanged(anyBoolean());
+
+ mCondition2.fakeUpdateCondition(true);
+ verify(callback, never()).onConditionsChanged(anyBoolean());
+
+ mCondition3.fakeUpdateCondition(true);
+ verify(callback).onConditionsChanged(true);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
new file mode 100644
index 0000000..fb8efa9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalManagerUpdaterTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.app.communal.CommunalManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.conditions.CommunalConditionsMonitor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class CommunalManagerUpdaterTest extends SysuiTestCase {
+ 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(mCommunalConditionsMonitor);
+ final CommunalManagerUpdater updater = new CommunalManagerUpdater(mContext, mMonitor);
+ updater.start();
+ }
+
+ @Test
+ public void testUpdateSystemService_false() {
+ mMonitor.setSource(null);
+ verify(mCommunalManager).setCommunalViewShowing(false);
+ }
+
+ @Test
+ public void testUpdateSystemService_true() {
+ final CommunalSource source = mock(CommunalSource.class);
+ mMonitor.setSource(source);
+ verify(mCommunalManager).setCommunalViewShowing(true);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java
new file mode 100644
index 0000000..cf147f0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSettingConditionTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.conditions.CommunalCondition;
+import com.android.systemui.communal.conditions.CommunalSettingCondition;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.utils.os.FakeHandler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class CommunalSettingConditionTest extends SysuiTestCase {
+ private FakeSettings mSecureSettings;
+ private CommunalSettingCondition mCondition;
+
+ @Before
+ public void setup() {
+ final FakeHandler handler = new FakeHandler(Looper.getMainLooper());
+ mSecureSettings = new FakeSettings();
+ mCondition = new CommunalSettingCondition(handler, mSecureSettings);
+ }
+
+ @Test
+ public void addCallback_communalSettingEnabled_immediatelyReportsTrue() {
+ updateCommunalSetting(true);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+ verify(callback).onConditionChanged(mCondition, true);
+ }
+
+ @Test
+ public void addCallback_communalSettingDisabled_noReport() {
+ updateCommunalSetting(false);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+ verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+ }
+
+ @Test
+ public void updateCallback_communalSettingEnabled_reportsTrue() {
+ updateCommunalSetting(false);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+ clearInvocations(callback);
+
+ updateCommunalSetting(true);
+ verify(callback).onConditionChanged(mCondition, true);
+ }
+
+ @Test
+ public void updateCallback_communalSettingDisabled_reportsFalse() {
+ updateCommunalSetting(true);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+ clearInvocations(callback);
+
+ updateCommunalSetting(false);
+ verify(callback).onConditionChanged(mCondition, false);
+ }
+
+ @Test
+ public void updateCallback_communalSettingDidNotChange_neverReportDup() {
+ updateCommunalSetting(true);
+
+ final CommunalCondition.Callback callback = mock(CommunalCondition.Callback.class);
+ mCondition.addCallback(callback);
+ clearInvocations(callback);
+
+ updateCommunalSetting(true);
+ verify(callback, never()).onConditionChanged(mCondition, true);
+ }
+
+ private void updateCommunalSetting(boolean value) {
+ mSecureSettings.putIntForUser(Settings.Secure.COMMUNAL_MODE_ENABLED, value ? 1 : 0,
+ UserHandle.USER_SYSTEM);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
index 68600de..df9a2cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalSourceMonitorTest.java
@@ -20,25 +20,25 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.provider.Settings;
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;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.lang.ref.WeakReference;
@@ -47,15 +47,16 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
public class CommunalSourceMonitorTest extends SysuiTestCase {
+ @Mock private CommunalConditionsMonitor mCommunalConditionsMonitor;
+
+ @Captor private ArgumentCaptor<CommunalConditionsMonitor.Callback> mConditionsCallbackCaptor;
+
private CommunalSourceMonitor mCommunalSourceMonitor;
- private FakeSettings mSecureSettings;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mSecureSettings = new FakeSettings();
- mCommunalSourceMonitor = new CommunalSourceMonitor(
- Handler.createAsync(TestableLooper.get(this).getLooper()), mSecureSettings);
+ mCommunalSourceMonitor = new CommunalSourceMonitor(mCommunalConditionsMonitor);
}
@Test
@@ -65,6 +66,7 @@
mCommunalSourceMonitor.setSource(source);
mCommunalSourceMonitor.addCallback(callback);
+ setConditionsMet(true);
verifyOnSourceAvailableCalledWith(callback, source);
}
@@ -82,33 +84,32 @@
}
@Test
- public void testAddCallbackWithDefaultSetting() {
+ public void testAddCallbackWithConditionsMet() {
final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
final CommunalSource source = mock(CommunalSource.class);
mCommunalSourceMonitor.addCallback(callback);
+ setConditionsMet(true);
+ clearInvocations(callback);
mCommunalSourceMonitor.setSource(source);
verifyOnSourceAvailableCalledWith(callback, source);
}
@Test
- public void testAddCallbackWithSettingDisabled() {
- setCommunalEnabled(false);
-
+ public void testAddCallbackWithConditionsNotMet() {
final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
final CommunalSource source = mock(CommunalSource.class);
mCommunalSourceMonitor.addCallback(callback);
+ setConditionsMet(false);
mCommunalSourceMonitor.setSource(source);
verify(callback, never()).onSourceAvailable(any());
}
@Test
- public void testSettingEnabledAfterCallbackAdded() {
- setCommunalEnabled(false);
-
+ public void testConditionsAreMetAfterCallbackAdded() {
final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
final CommunalSource source = mock(CommunalSource.class);
@@ -118,33 +119,27 @@
// The callback should not have executed since communal is disabled.
verify(callback, never()).onSourceAvailable(any());
- // The callback should execute when the user changes the setting to enabled.
- setCommunalEnabled(true);
+ // The callback should execute when all conditions are met.
+ setConditionsMet(true);
verifyOnSourceAvailableCalledWith(callback, source);
}
@Test
- public void testSettingDisabledAfterCallbackAdded() {
+ public void testConditionsNoLongerMetAfterCallbackAdded() {
final CommunalSourceMonitor.Callback callback = mock(CommunalSourceMonitor.Callback.class);
final CommunalSource source = mock(CommunalSource.class);
mCommunalSourceMonitor.addCallback(callback);
mCommunalSourceMonitor.setSource(source);
+ setConditionsMet(true);
verifyOnSourceAvailableCalledWith(callback, source);
- // The callback should execute again when the setting is disabled, with a value of null.
- setCommunalEnabled(false);
+ // The callback should execute again when conditions are no longer met, with a value of
+ // null.
+ setConditionsMet(false);
verify(callback).onSourceAvailable(null);
}
- private void setCommunalEnabled(boolean enabled) {
- mSecureSettings.putIntForUser(
- Settings.Secure.COMMUNAL_MODE_ENABLED,
- enabled ? 1 : 0,
- UserHandle.USER_SYSTEM);
- TestableLooper.get(this).processAllMessages();
- }
-
private void verifyOnSourceAvailableCalledWith(CommunalSourceMonitor.Callback callback,
CommunalSource source) {
final ArgumentCaptor<WeakReference<CommunalSource>> sourceCapture =
@@ -152,4 +147,13 @@
verify(callback).onSourceAvailable(sourceCapture.capture());
assertThat(sourceCapture.getValue().get()).isEqualTo(source);
}
+
+ // Pushes an update on whether the communal conditions are met, assuming that a callback has
+ // been registered with the communal conditions monitor.
+ private void setConditionsMet(boolean value) {
+ verify(mCommunalConditionsMonitor).addCallback(mConditionsCallbackCaptor.capture());
+ final CommunalConditionsMonitor.Callback conditionsCallback =
+ mConditionsCallbackCaptor.getValue();
+ conditionsCallback.onConditionsChanged(value);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalStateControllerTest.java
index 42abff0..7f85c35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalStateControllerTest.java
@@ -48,13 +48,13 @@
@Test
public void testDefaultCommunalViewShowingState() {
// The state controller should report the communal view as not showing by default.
- final CommunalStateController stateController = new CommunalStateController(getContext());
+ final CommunalStateController stateController = new CommunalStateController();
assertThat(stateController.getCommunalViewShowing()).isFalse();
}
@Test
public void testNotifyCommunalSurfaceShow() {
- final CommunalStateController stateController = new CommunalStateController(getContext());
+ final CommunalStateController stateController = new CommunalStateController();
stateController.addCallback(mCallback);
// Verify setting communal view to showing propagates to callback.
@@ -72,7 +72,7 @@
@Test
public void testCallbackRegistration() {
- final CommunalStateController stateController = new CommunalStateController(getContext());
+ final CommunalStateController stateController = new CommunalStateController();
stateController.addCallback(mCallback);
// Verify setting communal view to showing propagates to callback.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java
new file mode 100644
index 0000000..61a5126
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/CommunalTrustedNetworkConditionTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.clearInvocations;
+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.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.wifi.WifiInfo;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.communal.conditions.CommunalTrustedNetworkCondition;
+import com.android.systemui.util.settings.FakeSettings;
+import com.android.systemui.utils.os.FakeHandler;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class CommunalTrustedNetworkConditionTest extends SysuiTestCase {
+ @Mock private ConnectivityManager mConnectivityManager;
+
+ @Captor private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor;
+
+ private final Handler mHandler = new FakeHandler(Looper.getMainLooper());
+ private CommunalTrustedNetworkCondition mCondition;
+
+ private final String mTrustedWifi1 = "wifi-1";
+ private final String mTrustedWifi2 = "wifi-2";
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ final FakeSettings secureSettings = new FakeSettings();
+ secureSettings.putStringForUser(Settings.Secure.COMMUNAL_MODE_TRUSTED_NETWORKS,
+ mTrustedWifi1 + CommunalTrustedNetworkCondition.SETTINGS_STRING_DELIMINATOR
+ + mTrustedWifi2, UserHandle.USER_SYSTEM);
+ mCondition = new CommunalTrustedNetworkCondition(mHandler, mConnectivityManager,
+ secureSettings);
+ }
+
+ @Test
+ public void updateCallback_connectedToTrustedNetwork_reportsTrue() {
+ final CommunalTrustedNetworkCondition.Callback callback =
+ mock(CommunalTrustedNetworkCondition.Callback.class);
+ mCondition.addCallback(callback);
+
+ final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();
+
+ // Connected to trusted Wi-Fi network.
+ final Network network = mock(Network.class);
+ networkCallback.onAvailable(network);
+ networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
+
+ // Verifies that the callback is triggered.
+ verify(callback).onConditionChanged(mCondition, true);
+ }
+
+ @Test
+ public void updateCallback_switchedToAnotherTrustedNetwork_reportsNothing() {
+ final CommunalTrustedNetworkCondition.Callback callback =
+ mock(CommunalTrustedNetworkCondition.Callback.class);
+ mCondition.addCallback(callback);
+
+ final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();
+
+ // Connected to a trusted Wi-Fi network.
+ final Network network = mock(Network.class);
+ networkCallback.onAvailable(network);
+ networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
+ clearInvocations(callback);
+
+ // Connected to another trusted Wi-Fi network.
+ networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi2));
+
+ // Verifies that the callback is not triggered.
+ verify(callback, never()).onConditionChanged(eq(mCondition), anyBoolean());
+ }
+
+ @Test
+ public void updateCallback_connectedToNonTrustedNetwork_reportsFalse() {
+ final CommunalTrustedNetworkCondition.Callback callback =
+ mock(CommunalTrustedNetworkCondition.Callback.class);
+ mCondition.addCallback(callback);
+
+ final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();
+
+ // Connected to trusted Wi-Fi network.
+ final Network network = mock(Network.class);
+ networkCallback.onAvailable(network);
+ networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
+
+ // Connected to non-trusted Wi-Fi network.
+ networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities("random-wifi"));
+
+ // Verifies that the callback is triggered.
+ verify(callback).onConditionChanged(mCondition, false);
+ }
+
+ @Test
+ public void updateCallback_disconnectedFromNetwork_reportsFalse() {
+ final CommunalTrustedNetworkCondition.Callback callback =
+ mock(CommunalTrustedNetworkCondition.Callback.class);
+ mCondition.addCallback(callback);
+
+ final ConnectivityManager.NetworkCallback networkCallback = captureNetworkCallback();
+
+ // Connected to Wi-Fi.
+ final Network network = mock(Network.class);
+ networkCallback.onAvailable(network);
+ networkCallback.onCapabilitiesChanged(network, fakeNetworkCapabilities(mTrustedWifi1));
+ clearInvocations(callback);
+
+ // Disconnected from Wi-Fi.
+ networkCallback.onLost(network);
+
+ // Verifies that the callback is triggered.
+ verify(callback).onConditionChanged(mCondition, false);
+ }
+
+ // Captures and returns the network callback, assuming it is registered with the connectivity
+ // manager.
+ private ConnectivityManager.NetworkCallback captureNetworkCallback() {
+ verify(mConnectivityManager).registerNetworkCallback(any(NetworkRequest.class),
+ mNetworkCallbackCaptor.capture());
+ return mNetworkCallbackCaptor.getValue();
+ }
+
+ private NetworkCapabilities fakeNetworkCapabilities(String ssid) {
+ final NetworkCapabilities networkCapabilities = mock(NetworkCapabilities.class);
+ final WifiInfo wifiInfo = mock(WifiInfo.class);
+ when(wifiInfo.getSSID()).thenReturn(ssid);
+ when(networkCapabilities.getTransportInfo()).thenReturn(wifiInfo);
+ return networkCapabilities;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/FakeCommunalCondition.java b/packages/SystemUI/tests/src/com/android/systemui/communal/FakeCommunalCondition.java
new file mode 100644
index 0000000..882effd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/communal/FakeCommunalCondition.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal;
+
+import com.android.systemui.communal.conditions.CommunalCondition;
+
+/**
+ * Fake implementation of {@link CommunalCondition}, and provides a way for tests to update
+ * condition fulfillment.
+ */
+public class FakeCommunalCondition extends CommunalCondition {
+ @Override
+ public void start() {}
+
+ @Override
+ public void stop() {}
+
+ public void fakeUpdateCondition(boolean isConditionMet) {
+ updateCondition(isConditionMet);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
index 61b4041..2290676 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
@@ -18,6 +18,7 @@
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO;
@@ -56,7 +57,8 @@
public class KeyguardIndicationRotateTextViewControllerTest extends SysuiTestCase {
private static final String TEST_MESSAGE = "test message";
- private static final String TEST_MESSAGE_2 = "test message 2";
+ private static final String TEST_MESSAGE_2 = "test message two";
+ private int mMsgId = 0;
@Mock
private DelayableExecutor mExecutor;
@@ -201,6 +203,24 @@
}
@Test
+ public void testSameMessage_noIndicationUpdate() {
+ // GIVEN we are showing and indication with a test message
+ mController.updateIndication(
+ INDICATION_TYPE_OWNER_INFO, createIndication(TEST_MESSAGE), true);
+ reset(mView);
+ reset(mExecutor);
+
+ // WHEN the same type tries to show the same exact message
+ final KeyguardIndication sameIndication = createIndication(TEST_MESSAGE);
+ mController.updateIndication(
+ INDICATION_TYPE_OWNER_INFO, sameIndication, true);
+
+ // THEN
+ // - we don't update the indication b/c there's no reason the animate the same text
+ verify(mView, never()).switchIndication(sameIndication);
+ }
+
+ @Test
public void testTransientIndication() {
// GIVEN we already have two indication messages
mController.updateIndication(
@@ -223,8 +243,11 @@
@Test
public void testHideIndicationOneMessage() {
// GIVEN we have one indication message
+ KeyguardIndication indication = createIndication();
mController.updateIndication(
- INDICATION_TYPE_OWNER_INFO, createIndication(), false);
+ INDICATION_TYPE_OWNER_INFO, indication, false);
+ verify(mView).switchIndication(indication);
+ reset(mView);
// WHEN we hide the current indication type
mController.hideIndication(INDICATION_TYPE_OWNER_INFO);
@@ -254,6 +277,10 @@
@Test
public void testStartDozing() {
+ // GIVEN a biometric message is showing
+ mController.updateIndication(INDICATION_TYPE_BIOMETRIC_MESSAGE,
+ createIndication(), true);
+
// WHEN the device is dozing
mStatusBarStateListener.onDozingChanged(true);
@@ -293,9 +320,19 @@
verify(mView, never()).switchIndication(any());
}
+ /**
+ * Create an indication with a unique message.
+ */
private KeyguardIndication createIndication() {
+ return createIndication(TEST_MESSAGE + " " + mMsgId++);
+ }
+
+ /**
+ * Create an indication with the given message.
+ */
+ private KeyguardIndication createIndication(String msg) {
return new KeyguardIndication.Builder()
- .setMessage(TEST_MESSAGE)
+ .setMessage(msg)
.setTextColor(ColorStateList.valueOf(Color.WHITE))
.build();
}
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/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 8afefde..e427d53 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -22,6 +22,7 @@
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_RESTING;
@@ -112,6 +113,8 @@
private static final ComponentName DEVICE_OWNER_COMPONENT = new ComponentName("com.android.foo",
"bar");
+ private static final int TEST_STRING_RES = R.string.keyguard_indication_trust_unlocked;
+
private String mKeyguardTryFingerprintMsg;
private String mDisclosureWithOrganization;
private String mDisclosureGeneric;
@@ -419,7 +422,7 @@
// WHEN transient text is shown
mStatusBarStateListener.onDozingChanged(true);
- mController.showTransientIndication("Test");
+ mController.showTransientIndication(TEST_STRING_RES);
// THEN wake lock is held while the animation is running
assertTrue("WakeLock expected: HELD, was: RELEASED", mWakeLock.isHeld());
@@ -434,7 +437,7 @@
// WHEN we show the transient indication
mStatusBarStateListener.onDozingChanged(true);
- mController.showTransientIndication("Test");
+ mController.showTransientIndication(TEST_STRING_RES);
// THEN wake lock is RELEASED, not held
assertFalse("WakeLock expected: RELEASED, was: HELD", mWakeLock.isHeld());
@@ -445,10 +448,11 @@
createController();
mController.setVisible(true);
- mController.showTransientIndication("Test");
+ mController.showTransientIndication(TEST_STRING_RES);
mStatusBarStateListener.onDozingChanged(true);
- assertThat(mTextView.getText()).isEqualTo("Test");
+ assertThat(mTextView.getText()).isEqualTo(
+ mContext.getResources().getString(TEST_STRING_RES));
assertThat(mTextView.getCurrentTextColor()).isEqualTo(Color.WHITE);
assertThat(mTextView.getAlpha()).isEqualTo(1f);
}
@@ -462,11 +466,11 @@
mController.getKeyguardCallback().onBiometricHelp(
KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, message,
BiometricSourceType.FACE);
- verifyTransientMessage(message);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, message);
reset(mRotateTextViewController);
mStatusBarStateListener.onDozingChanged(true);
- verifyHideIndication(INDICATION_TYPE_TRANSIENT);
+ verifyHideIndication(INDICATION_TYPE_BIOMETRIC_MESSAGE);
}
@Test
@@ -478,7 +482,7 @@
mController.getKeyguardCallback().onBiometricError(FaceManager.FACE_ERROR_TIMEOUT,
"A message", BiometricSourceType.FACE);
- verifyTransientMessage(message);
+ verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, message);
mStatusBarStateListener.onDozingChanged(true);
assertThat(mTextView.getText()).isNotEqualTo(message);
@@ -497,7 +501,8 @@
FingerprintManager.FINGERPRINT_ERROR_CANCELED, "bar",
BiometricSourceType.FINGERPRINT);
- verifyNoTransientMessage();
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+ verifyNoMessage(INDICATION_TYPE_TRANSIENT);
}
@Test
@@ -757,7 +762,12 @@
verify(mRotateTextViewController).showTransient(eq(message));
}
- private void verifyNoTransientMessage() {
- verify(mRotateTextViewController, never()).showTransient(any());
+ private void verifyNoMessage(int type) {
+ if (type == INDICATION_TYPE_TRANSIENT) {
+ verify(mRotateTextViewController, never()).showTransient(anyString());
+ } else {
+ verify(mRotateTextViewController, never()).updateIndication(eq(type),
+ anyObject(), anyBoolean());
+ }
}
}
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..d79f2f3 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();
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 2d61773..9b21790 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -2232,11 +2232,12 @@
return false;
}
- public final boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
+ private boolean filterStaticSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
int userId, @PackageManager.ComponentInfoFlags long flags) {
- // Callers can access only the libs they depend on, otherwise they need to explicitly
- // ask for the shared libraries given the caller is allowed to access all static libs.
- if ((flags & PackageManager.MATCH_STATIC_SHARED_LIBRARIES) != 0) {
+ // Callers can access only the static shared libs they depend on, otherwise they need to
+ // explicitly ask for the static shared libraries given the caller is allowed to access
+ // all static libs.
+ if ((flags & PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES) != 0) {
// System/shell/root get to see all static libs
final int appId = UserHandle.getAppId(uid);
if (appId == Process.SYSTEM_UID || appId == Process.SHELL_UID
@@ -2287,6 +2288,69 @@
return true;
}
+ private boolean filterSdkLibPackage(@Nullable PackageStateInternal ps, int uid,
+ int userId, @PackageManager.ComponentInfoFlags long flags) {
+ // Callers can access only the SDK libs they depend on, otherwise they need to
+ // explicitly ask for the SDKs given the caller is allowed to access
+ // all shared libs.
+ if ((flags & PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES) != 0) {
+ // System/shell/root get to see all SDK libs.
+ final int appId = UserHandle.getAppId(uid);
+ if (appId == Process.SYSTEM_UID || appId == Process.SHELL_UID
+ || appId == Process.ROOT_UID) {
+ return false;
+ }
+ // Installer gets to see all SDK libs.
+ if (PackageManager.PERMISSION_GRANTED
+ == checkUidPermission(Manifest.permission.INSTALL_PACKAGES, uid)) {
+ return false;
+ }
+ }
+
+ // No package means no static lib as it is always on internal storage
+ if (ps == null || ps.getPkg() == null || !ps.getPkg().isSdkLibrary()) {
+ return false;
+ }
+
+ final SharedLibraryInfo libraryInfo = getSharedLibraryInfo(
+ ps.getPkg().getSdkLibName(), ps.getPkg().getSdkLibVersionMajor());
+ if (libraryInfo == null) {
+ return false;
+ }
+
+ final int resolvedUid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
+ final String[] uidPackageNames = getPackagesForUid(resolvedUid);
+ if (uidPackageNames == null) {
+ return true;
+ }
+
+ for (String uidPackageName : uidPackageNames) {
+ if (ps.getPackageName().equals(uidPackageName)) {
+ return false;
+ }
+ PackageStateInternal uidPs = mSettings.getPackage(uidPackageName);
+ if (uidPs != null) {
+ final int index = ArrayUtils.indexOf(uidPs.getUsesSdkLibraries(),
+ libraryInfo.getName());
+ if (index < 0) {
+ continue;
+ }
+ if (uidPs.getPkg().getUsesSdkLibrariesVersionsMajor()[index]
+ == libraryInfo.getLongVersion()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ @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,
+ userId, flags);
+ }
+
private boolean hasCrossUserPermission(
int callingUid, int callingUserId, int userId, boolean requireFullPermission,
boolean requirePermissionWhenSameUser) {
@@ -3719,7 +3783,7 @@
flags = updateFlagsForPackage(flags, userId);
- final boolean canSeeStaticLibraries =
+ final boolean canSeeStaticAndSdkLibraries =
mContext.checkCallingOrSelfPermission(INSTALL_PACKAGES)
== PERMISSION_GRANTED
|| mContext.checkCallingOrSelfPermission(DELETE_PACKAGES)
@@ -3744,7 +3808,7 @@
final int versionCount = versionedLib.size();
for (int j = 0; j < versionCount; j++) {
SharedLibraryInfo libInfo = versionedLib.valueAt(j);
- if (!canSeeStaticLibraries && libInfo.isStatic()) {
+ if (!canSeeStaticAndSdkLibraries && (libInfo.isStatic() || libInfo.isSdk())) {
break;
}
final long identity = Binder.clearCallingIdentity();
@@ -3753,7 +3817,7 @@
PackageInfo packageInfo = getPackageInfoInternal(
declaringPackage.getPackageName(),
declaringPackage.getLongVersionCode(),
- flags | PackageManager.MATCH_STATIC_SHARED_LIBRARIES,
+ flags | PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES,
Binder.getCallingUid(), userId);
if (packageInfo == null) {
continue;
@@ -3853,12 +3917,17 @@
}
final String libName = libInfo.getName();
- if (libInfo.isStatic()) {
- final int libIdx = ArrayUtils.indexOf(ps.getUsesStaticLibraries(), libName);
+ if (libInfo.isStatic() || libInfo.isSdk()) {
+ final String[] libs =
+ libInfo.isStatic() ? ps.getUsesStaticLibraries() : ps.getUsesSdkLibraries();
+ final long[] libsVersions = libInfo.isStatic() ? ps.getUsesStaticLibrariesVersions()
+ : ps.getUsesSdkLibrariesVersionsMajor();
+
+ final int libIdx = ArrayUtils.indexOf(libs, libName);
if (libIdx < 0) {
continue;
}
- if (ps.getUsesStaticLibrariesVersions()[libIdx] != libInfo.getLongVersion()) {
+ if (libsVersions[libIdx] != libInfo.getLongVersion()) {
continue;
}
if (shouldFilterApplication(ps, callingUid, userId)) {
@@ -3939,7 +4008,7 @@
PackageInfo packageInfo = getPackageInfoInternal(
declaringPackage.getPackageName(),
declaringPackage.getLongVersionCode(),
- flags | PackageManager.MATCH_STATIC_SHARED_LIBRARIES,
+ flags | PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES,
Binder.getCallingUid(), userId);
if (packageInfo == null) {
continue;
@@ -4034,7 +4103,7 @@
getPackageStateInternal(libraryInfo.getPackageName());
if (ps != null && !filterSharedLibPackage(ps, Binder.getCallingUid(),
UserHandle.getUserId(Binder.getCallingUid()),
- PackageManager.MATCH_STATIC_SHARED_LIBRARIES)) {
+ PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES)) {
if (libs == null) {
libs = new ArraySet<>();
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 43d60cc..641f24f 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -164,9 +164,16 @@
allUsers = mUserManagerInternal.getUserIds();
- if (pkg != null && pkg.getStaticSharedLibName() != null) {
- SharedLibraryInfo libraryInfo = mPm.getSharedLibraryInfo(
- pkg.getStaticSharedLibName(), pkg.getStaticSharedLibVersion());
+ if (pkg != null) {
+ SharedLibraryInfo libraryInfo = null;
+ if (pkg.getStaticSharedLibName() != null) {
+ libraryInfo = mPm.getSharedLibraryInfo(pkg.getStaticSharedLibName(),
+ pkg.getStaticSharedLibVersion());
+ } else if (pkg.getSdkLibName() != null) {
+ libraryInfo = mPm.getSharedLibraryInfo(pkg.getSdkLibName(),
+ pkg.getSdkLibVersionMajor());
+ }
+
if (libraryInfo != null) {
for (int currUserId : allUsers) {
if (removeUser != UserHandle.USER_ALL && removeUser != currUserId) {
@@ -828,9 +835,10 @@
continue;
}
final String packageName = ps.getPkg().getPackageName();
- // Skip over if system app or static shared library
+ // Skip over if system app, static shared library or and SDK library.
if ((ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0
- || !TextUtils.isEmpty(ps.getPkg().getStaticSharedLibName())) {
+ || !TextUtils.isEmpty(ps.getPkg().getStaticSharedLibName())
+ || !TextUtils.isEmpty(ps.getPkg().getSdkLibName())) {
continue;
}
if (DEBUG_CLEAN_APKS) {
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 5ca0618..d2087ee 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -254,12 +254,11 @@
final List<SharedLibraryInfo> allowedSharedLibInfos =
SharedLibraryHelper.getAllowedSharedLibInfos(scanResult,
request.mSharedLibrarySource);
- final SharedLibraryInfo staticLib = scanResult.mStaticSharedLibraryInfo;
if (allowedSharedLibInfos != null) {
for (SharedLibraryInfo info : allowedSharedLibInfos) {
if (!SharedLibraryHelper.addSharedLibraryToPackageVersionMap(
incomingSharedLibraries, info)) {
- throw new ReconcileFailure("Static Shared Library " + staticLib.getName()
+ throw new ReconcileFailure("Shared Library " + info.getName()
+ " is being installed twice in this set!");
}
}
@@ -1188,7 +1187,8 @@
createdAppId.put(packageName, optimisticallyRegisterAppId(result));
versionInfos.put(result.mPkgSetting.getPkg().getPackageName(),
mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg()));
- if (result.mStaticSharedLibraryInfo != null) {
+ if (result.mStaticSharedLibraryInfo != null
+ || result.mSdkSharedLibraryInfo != null) {
final PackageSetting sharedLibLatestVersionSetting =
mPm.getSharedLibLatestVersionSetting(result);
if (sharedLibLatestVersionSetting != null) {
@@ -2695,7 +2695,7 @@
}
}
- if (dataOwnerPkg != null) {
+ if (dataOwnerPkg != null && !dataOwnerPkg.isSdkLibrary()) {
if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags,
dataOwnerPkg.isDebuggable())) {
try {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 4767d3a..26a5bbb 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1328,7 +1328,7 @@
PackageInfo packageInfo = null;
try {
packageInfo = AppGlobals.getPackageManager().getPackageInfo(
- basePackageName, PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId);
+ basePackageName, PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES, userId);
} catch (RemoteException ignored) {
}
if (packageInfo == null || packageInfo.applicationInfo == null) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index dbbc163..55a0c96 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2901,7 +2901,7 @@
final PackageInfo pkgInfo = mPm.getPackageInfo(
params.appPackageName, PackageManager.GET_SIGNATURES
- | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
+ | PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES /*flags*/, userId);
// Partial installs must be consistent with existing install
if (params.mode == SessionParams.MODE_INHERIT_EXISTING
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9f5adcb..fe5bce13 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4034,7 +4034,13 @@
// - Package manager is in a state where package isn't scanned yet. This will
// get called again after scanning to fix the dependencies.
if (AndroidPackageUtils.isLibrary(pkg)) {
- if (pkg.getStaticSharedLibName() != null) {
+ if (pkg.getSdkLibName() != null) {
+ SharedLibraryInfo definedLibrary = getSharedLibraryInfo(
+ pkg.getSdkLibName(), pkg.getSdkLibVersionMajor());
+ if (definedLibrary != null) {
+ action.accept(definedLibrary, libInfo);
+ }
+ } else if (pkg.getStaticSharedLibName() != null) {
SharedLibraryInfo definedLibrary = getSharedLibraryInfo(
pkg.getStaticSharedLibName(), pkg.getStaticSharedLibVersion());
if (definedLibrary != null) {
@@ -4186,7 +4192,9 @@
&& !hasString(pkg.getUsesLibraries(), changingPkg.getLibraryNames())
&& !hasString(pkg.getUsesOptionalLibraries(), changingPkg.getLibraryNames())
&& !ArrayUtils.contains(pkg.getUsesStaticLibraries(),
- changingPkg.getStaticSharedLibName())) {
+ changingPkg.getStaticSharedLibName())
+ && !ArrayUtils.contains(pkg.getUsesSdkLibraries(),
+ changingPkg.getSdkLibName())) {
continue;
}
if (resultList == null) {
@@ -4477,15 +4485,24 @@
Slog.w(TAG, "Cannot hide package: android");
return false;
}
- // Cannot hide static shared libs as they are considered
- // a part of the using app (emulating static linking). Also
- // static libs are installed always on internal storage.
AndroidPackage pkg = mPackages.get(packageName);
- if (pkg != null && pkg.getStaticSharedLibName() != null) {
- Slog.w(TAG, "Cannot hide package: " + packageName
- + " providing static shared library: "
- + pkg.getStaticSharedLibName());
- return false;
+ if (pkg != null) {
+ // Cannot hide SDK libs as they are controlled by SDK manager.
+ if (pkg.getSdkLibName() != null) {
+ Slog.w(TAG, "Cannot hide package: " + packageName
+ + " providing SDK library: "
+ + pkg.getSdkLibName());
+ return false;
+ }
+ // Cannot hide static shared libs as they are considered
+ // a part of the using app (emulating static linking). Also
+ // static libs are installed always on internal storage.
+ if (pkg.getStaticSharedLibName() != null) {
+ Slog.w(TAG, "Cannot hide package: " + packageName
+ + " providing static shared library: "
+ + pkg.getStaticSharedLibName());
+ return false;
+ }
}
// Only allow protected packages to hide themselves.
if (hidden && !UserHandle.isSameApp(callingUid, pkgSetting.getAppId())
@@ -5154,15 +5171,24 @@
continue;
}
- // Cannot suspend static shared libs as they are considered
- // a part of the using app (emulating static linking). Also
- // static libs are installed always on internal storage.
AndroidPackage pkg = mPackages.get(packageName);
- if (pkg != null && pkg.isStaticSharedLibrary()) {
- Slog.w(TAG, "Cannot suspend package: " + packageName
- + " providing static shared library: "
- + pkg.getStaticSharedLibName());
- continue;
+ if (pkg != null) {
+ // Cannot suspend SDK libs as they are controlled by SDK manager.
+ if (pkg.isSdkLibrary()) {
+ Slog.w(TAG, "Cannot suspend package: " + packageName
+ + " providing SDK library: "
+ + pkg.getSdkLibName());
+ continue;
+ }
+ // Cannot suspend static shared libs as they are considered
+ // a part of the using app (emulating static linking). Also
+ // static libs are installed always on internal storage.
+ if (pkg.isStaticSharedLibrary()) {
+ Slog.w(TAG, "Cannot suspend package: " + packageName
+ + " providing static shared library: "
+ + pkg.getStaticSharedLibName());
+ continue;
+ }
}
}
if (PLATFORM_PACKAGE_NAME.equals(packageName)) {
@@ -5612,14 +5638,22 @@
android.Manifest.permission.DELETE_PACKAGES, null);
// TODO (b/157774108): This should fail on non-existent packages.
synchronized (mLock) {
- // Cannot block uninstall of static shared libs as they are
- // considered a part of the using app (emulating static linking).
- // Also static libs are installed always on internal storage.
AndroidPackage pkg = mPackages.get(packageName);
- if (pkg != null && pkg.getStaticSharedLibName() != null) {
- Slog.w(TAG, "Cannot block uninstall of package: " + packageName
- + " providing static shared library: " + pkg.getStaticSharedLibName());
- return false;
+ if (pkg != null) {
+ // Cannot block uninstall SDK libs as they are controlled by SDK manager.
+ if (pkg.getSdkLibName() != null) {
+ Slog.w(TAG, "Cannot block uninstall of package: " + packageName
+ + " providing SDK library: " + pkg.getSdkLibName());
+ return false;
+ }
+ // Cannot block uninstall of static shared libs as they are
+ // considered a part of the using app (emulating static linking).
+ // Also static libs are installed always on internal storage.
+ if (pkg.getStaticSharedLibName() != null) {
+ Slog.w(TAG, "Cannot block uninstall of package: " + packageName
+ + " providing static shared library: " + pkg.getStaticSharedLibName());
+ return false;
+ }
}
mSettings.setBlockUninstallLPw(userId, packageName, blockUninstall);
mSettings.writePackageRestrictionsLPr(userId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index fb70470..0564e85 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -49,6 +49,7 @@
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
+import android.content.pm.SharedLibraryInfo;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
@@ -667,6 +668,8 @@
return runListPermissions();
case "staged-sessions":
return runListStagedSessions();
+ case "sdks":
+ return runListSdks();
case "users":
ServiceManager.getService("user").shellCommand(
getInFileDescriptor(), getOutFileDescriptor(), getErrFileDescriptor(),
@@ -792,6 +795,15 @@
}
private int runListPackages(boolean showSourceDir) throws RemoteException {
+ return runListPackages(showSourceDir, false);
+ }
+
+ private int runListSdks() throws RemoteException {
+ return runListPackages(false, true);
+ }
+
+ private int runListPackages(boolean showSourceDir, boolean showSdks) throws RemoteException {
+ final String prefix = showSdks ? "sdk:" : "package:";
final PrintWriter pw = getOutPrintWriter();
int getFlags = 0;
boolean listDisabled = false, listEnabled = false;
@@ -866,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")
@@ -885,37 +900,61 @@
}
final boolean isSystem = !isApex &&
- (info.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0;
+ (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
final boolean isEnabled = !isApex && info.applicationInfo.enabled;
- if ((!listDisabled || !isEnabled) &&
- (!listEnabled || isEnabled) &&
- (!listSystem || isSystem) &&
- (!listThirdParty || !isSystem) &&
- (!listApexOnly || isApex)) {
- pw.print("package:");
- if (showSourceDir) {
- pw.print(info.applicationInfo.sourceDir);
- pw.print("=");
+ if ((listDisabled && isEnabled) ||
+ (listEnabled && !isEnabled) ||
+ (listSystem && !isSystem) ||
+ (listThirdParty && isSystem) ||
+ (listApexOnly && !isApex)) {
+ continue;
+ }
+
+ String name = null;
+ if (showSdks) {
+ final ParceledListSlice<SharedLibraryInfo> libsSlice =
+ mInterface.getDeclaredSharedLibraries(info.packageName, getFlags, userId);
+ if (libsSlice == null) {
+ continue;
}
- pw.print(info.packageName);
- if (showVersionCode) {
- pw.print(" versionCode:");
- if (info.applicationInfo != null) {
- pw.print(info.applicationInfo.longVersionCode);
- } else {
- pw.print(info.getLongVersionCode());
+ final List<SharedLibraryInfo> libs = libsSlice.getList();
+ for (int l = 0, lsize = libs.size(); l < lsize; ++l) {
+ SharedLibraryInfo lib = libs.get(l);
+ if (lib.getType() == SharedLibraryInfo.TYPE_SDK) {
+ name = lib.getName() + ":" + lib.getLongVersion();
+ break;
}
}
- if (listInstaller) {
- pw.print(" installer=");
- pw.print(mInterface.getInstallerPackageName(info.packageName));
+ if (name == null) {
+ continue;
}
- if (showUid && !isApex) {
- pw.print(" uid:");
- pw.print(info.applicationInfo.uid);
- }
- pw.println();
+ } else {
+ name = info.packageName;
}
+
+ pw.print(prefix);
+ if (showSourceDir) {
+ pw.print(info.applicationInfo.sourceDir);
+ pw.print("=");
+ }
+ pw.print(name);
+ if (showVersionCode) {
+ pw.print(" versionCode:");
+ if (info.applicationInfo != null) {
+ pw.print(info.applicationInfo.longVersionCode);
+ } else {
+ pw.print(info.getLongVersionCode());
+ }
+ }
+ if (listInstaller) {
+ pw.print(" installer=");
+ pw.print(mInterface.getInstallerPackageName(info.packageName));
+ }
+ if (showUid && !isApex) {
+ pw.print(" uid:");
+ pw.print(info.applicationInfo.uid);
+ }
+ pw.println();
}
return 0;
}
@@ -2060,7 +2099,7 @@
} else {
if ((flags & PackageManager.DELETE_ALL_USERS) == 0) {
final PackageInfo info = mInterface.getPackageInfo(packageName,
- PackageManager.MATCH_STATIC_SHARED_LIBRARIES, translatedUserId);
+ PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES, translatedUserId);
if (info == null) {
pw.println("Failure [not installed for " + translatedUserId + "]");
return 1;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index dc514c1..923a133 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -32,12 +32,6 @@
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.content.pm.overlay.OverlayPaths;
-
-import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.PackageUserState;
-import com.android.server.pm.pkg.PackageUserStateImpl;
-import com.android.server.pm.pkg.PackageUserStateInternal;
-import com.android.server.pm.pkg.SuspendParams;
import android.os.PersistableBundle;
import android.service.pm.PackageProto;
import android.util.ArrayMap;
@@ -53,7 +47,12 @@
import com.android.server.pm.permission.LegacyPermissionState;
import com.android.server.pm.pkg.AndroidPackageApi;
import com.android.server.pm.pkg.PackageState;
+import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUnserialized;
+import com.android.server.pm.pkg.PackageUserState;
+import com.android.server.pm.pkg.PackageUserStateImpl;
+import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.SuspendParams;
import com.android.server.utils.SnapshotCache;
import libcore.util.EmptyArray;
@@ -94,6 +93,12 @@
private Set<String> mOldCodePaths;
@Nullable
+ private String[] usesSdkLibraries;
+
+ @Nullable
+ private long[] usesSdkLibrariesVersionsMajor;
+
+ @Nullable
private String[] usesStaticLibraries;
@Nullable
@@ -208,12 +213,16 @@
String legacyNativeLibraryPath, String primaryCpuAbi,
String secondaryCpuAbi, String cpuAbiOverride,
long longVersionCode, int pkgFlags, int pkgPrivateFlags,
- int sharedUserId, String[] usesStaticLibraries,
- long[] usesStaticLibrariesVersions, Map<String, Set<String>> mimeGroups,
+ int sharedUserId,
+ String[] usesSdkLibraries, long[] usesSdkLibrariesVersionsMajor,
+ String[] usesStaticLibraries, long[] usesStaticLibrariesVersions,
+ Map<String, Set<String>> mimeGroups,
@NonNull UUID domainSetId) {
super(pkgFlags, pkgPrivateFlags);
this.mName = name;
this.mRealName = realName;
+ this.usesSdkLibraries = usesSdkLibraries;
+ this.usesSdkLibrariesVersionsMajor = usesSdkLibrariesVersionsMajor;
this.usesStaticLibraries = usesStaticLibraries;
this.usesStaticLibrariesVersions = usesStaticLibrariesVersions;
this.mPath = path;
@@ -617,6 +626,13 @@
forceQueryableOverride = other.forceQueryableOverride;
mDomainSetId = other.mDomainSetId;
+ usesSdkLibraries = other.usesSdkLibraries != null
+ ? Arrays.copyOf(other.usesSdkLibraries,
+ other.usesSdkLibraries.length) : null;
+ usesSdkLibrariesVersionsMajor = other.usesSdkLibrariesVersionsMajor != null
+ ? Arrays.copyOf(other.usesSdkLibrariesVersionsMajor,
+ other.usesSdkLibrariesVersionsMajor.length) : null;
+
usesStaticLibraries = other.usesStaticLibraries != null
? Arrays.copyOf(other.usesStaticLibraries,
other.usesStaticLibraries.length) : null;
@@ -1225,6 +1241,19 @@
@NonNull
@Override
+ public String[] getUsesSdkLibraries() {
+ return usesSdkLibraries == null ? EmptyArray.STRING : usesSdkLibraries;
+ }
+
+ @NonNull
+ @Override
+ public long[] getUsesSdkLibrariesVersionsMajor() {
+ return usesSdkLibrariesVersionsMajor == null ? EmptyArray.LONG
+ : usesSdkLibrariesVersionsMajor;
+ }
+
+ @NonNull
+ @Override
public String[] getUsesStaticLibraries() {
return usesStaticLibraries == null ? EmptyArray.STRING : usesStaticLibraries;
}
@@ -1300,6 +1329,18 @@
return this;
}
+ public PackageSetting setUsesSdkLibraries(String[] usesSdkLibraries) {
+ this.usesSdkLibraries = usesSdkLibraries;
+ onChanged();
+ return this;
+ }
+
+ public PackageSetting setUsesSdkLibrariesVersionsMajor(long[] usesSdkLibrariesVersions) {
+ this.usesSdkLibrariesVersionsMajor = usesSdkLibrariesVersions;
+ onChanged();
+ return this;
+ }
+
public PackageSetting setUsesStaticLibraries(String[] usesStaticLibraries) {
this.usesStaticLibraries = usesStaticLibraries;
onChanged();
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index 48b893b..749495c 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -189,7 +189,19 @@
r = null;
- // Any package can hold static shared libraries.
+ // Any package can hold SDK or static shared libraries.
+ if (pkg.getSdkLibName() != null) {
+ if (removeSharedLibraryLPw(pkg.getSdkLibName(), pkg.getSdkLibVersionMajor())) {
+ if (DEBUG_REMOVE && chatty) {
+ if (r == null) {
+ r = new StringBuilder(256);
+ } else {
+ r.append(' ');
+ }
+ r.append(pkg.getSdkLibName());
+ }
+ }
+ }
if (pkg.getStaticSharedLibName() != null) {
if (removeSharedLibraryLPw(pkg.getStaticSharedLibName(),
pkg.getStaticSharedLibVersion())) {
diff --git a/services/core/java/com/android/server/pm/ScanPackageHelper.java b/services/core/java/com/android/server/pm/ScanPackageHelper.java
index 9b08ef9..eafe0d98 100644
--- a/services/core/java/com/android/server/pm/ScanPackageHelper.java
+++ b/services/core/java/com/android/server/pm/ScanPackageHelper.java
@@ -294,6 +294,12 @@
}
}
+ String[] usesSdkLibraries = null;
+ if (!parsedPackage.getUsesSdkLibraries().isEmpty()) {
+ usesSdkLibraries = new String[parsedPackage.getUsesSdkLibraries().size()];
+ parsedPackage.getUsesSdkLibraries().toArray(usesSdkLibraries);
+ }
+
String[] usesStaticLibraries = null;
if (!parsedPackage.getUsesStaticLibraries().isEmpty()) {
usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];
@@ -324,7 +330,8 @@
AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),
parsedPackage.getLongVersionCode(), pkgFlags, pkgPrivateFlags, user,
true /*allowInstall*/, instantApp, virtualPreload,
- UserManagerService.getInstance(), usesStaticLibraries,
+ UserManagerService.getInstance(), usesSdkLibraries,
+ parsedPackage.getUsesSdkLibrariesVersionsMajor(), usesStaticLibraries,
parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),
newDomainSetId);
} else {
@@ -344,6 +351,7 @@
PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),
PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),
UserManagerService.getInstance(),
+ usesSdkLibraries, parsedPackage.getUsesSdkLibrariesVersionsMajor(),
usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),
parsedPackage.getMimeGroups(), newDomainSetId);
}
@@ -552,6 +560,10 @@
pkgSetting.setVolumeUuid(volumeUuid);
}
+ SharedLibraryInfo sdkLibraryInfo = null;
+ if (!TextUtils.isEmpty(parsedPackage.getSdkLibName())) {
+ sdkLibraryInfo = AndroidPackageUtils.createSharedLibraryForSdk(parsedPackage);
+ }
SharedLibraryInfo staticSharedLibraryInfo = null;
if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {
staticSharedLibraryInfo =
@@ -568,7 +580,7 @@
return new ScanResult(request, true, pkgSetting, changedAbiCodePath,
!createNewPackage /* existingSettingCopied */,
- previousAppId, staticSharedLibraryInfo,
+ previousAppId, sdkLibraryInfo, staticSharedLibraryInfo,
dynamicSharedLibraryInfos);
}
diff --git a/services/core/java/com/android/server/pm/ScanResult.java b/services/core/java/com/android/server/pm/ScanResult.java
index eb44a82..f77be1f 100644
--- a/services/core/java/com/android/server/pm/ScanResult.java
+++ b/services/core/java/com/android/server/pm/ScanResult.java
@@ -51,6 +51,8 @@
/** ABI code paths that have changed in the package scan */
@Nullable public final List<String> mChangedAbiCodePath;
+ public final SharedLibraryInfo mSdkSharedLibraryInfo;
+
public final SharedLibraryInfo mStaticSharedLibraryInfo;
public final List<SharedLibraryInfo> mDynamicSharedLibraryInfos;
@@ -60,6 +62,7 @@
@Nullable PackageSetting pkgSetting,
@Nullable List<String> changedAbiCodePath, boolean existingSettingCopied,
int previousAppId,
+ SharedLibraryInfo sdkSharedLibraryInfo,
SharedLibraryInfo staticSharedLibraryInfo,
List<SharedLibraryInfo> dynamicSharedLibraryInfos) {
mRequest = request;
@@ -68,6 +71,7 @@
mChangedAbiCodePath = changedAbiCodePath;
mExistingSettingCopied = existingSettingCopied;
mPreviousAppId = previousAppId;
+ mSdkSharedLibraryInfo = sdkSharedLibraryInfo;
mStaticSharedLibraryInfo = staticSharedLibraryInfo;
mDynamicSharedLibraryInfos = dynamicSharedLibraryInfos;
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 6a163b2..45994f6 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -276,6 +276,7 @@
private static final String TAG_RUNTIME_PERMISSIONS = "runtime-permissions";
private static final String TAG_PERMISSIONS = "perms";
private static final String TAG_CHILD_PACKAGE = "child-package";
+ private static final String TAG_USES_SDK_LIB = "uses-sdk-lib";
private static final String TAG_USES_STATIC_LIB = "uses-static-lib";
private static final String TAG_BLOCK_UNINSTALL_PACKAGES = "block-uninstall-packages";
private static final String TAG_BLOCK_UNINSTALL = "block-uninstall";
@@ -826,6 +827,7 @@
p.getLegacyNativeLibraryPath(), p.getPrimaryCpuAbi(),
p.getSecondaryCpuAbi(), p.getCpuAbiOverride(),
p.getAppId(), p.getVersionCode(), p.getFlags(), p.getPrivateFlags(),
+ p.getUsesSdkLibraries(), p.getUsesSdkLibrariesVersionsMajor(),
p.getUsesStaticLibraries(), p.getUsesStaticLibrariesVersions(), p.getMimeGroups(),
mDomainVerificationManager.generateNewId());
if (ret != null) {
@@ -849,9 +851,10 @@
PackageSetting addPackageLPw(String name, String realName, File codePath,
String legacyNativeLibraryPathString, String primaryCpuAbiString,
- String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc, int
- pkgFlags, int pkgPrivateFlags, String[] usesStaticLibraries,
- long[] usesStaticLibraryNames, Map<String, Set<String>> mimeGroups,
+ String secondaryCpuAbiString, String cpuAbiOverrideString, int uid, long vc,
+ int pkgFlags, int pkgPrivateFlags, String[] usesSdkLibraries,
+ long[] usesSdkLibrariesVersions, String[] usesStaticLibraries,
+ long[] usesStaticLibrariesVersions, Map<String, Set<String>> mimeGroups,
@NonNull UUID domainSetId) {
PackageSetting p = mPackages.get(name);
if (p != null) {
@@ -864,8 +867,8 @@
}
p = new PackageSetting(name, realName, codePath, legacyNativeLibraryPathString,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString, vc, pkgFlags,
- pkgPrivateFlags, 0 /*userId*/, usesStaticLibraries, usesStaticLibraryNames,
- mimeGroups, domainSetId);
+ pkgPrivateFlags, 0 /*userId*/, usesSdkLibraries, usesSdkLibrariesVersions,
+ usesStaticLibraries, usesStaticLibrariesVersions, mimeGroups, domainSetId);
p.setAppId(uid);
if (registerExistingAppIdLPw(uid, p, name)) {
mPackages.put(name, p);
@@ -925,6 +928,7 @@
String secondaryCpuAbi, long versionCode, int pkgFlags, int pkgPrivateFlags,
UserHandle installUser, boolean allowInstall, boolean instantApp,
boolean virtualPreload, UserManagerService userManager,
+ String[] usesSdkLibraries, long[] usesSdkLibrariesVersions,
String[] usesStaticLibraries, long[] usesStaticLibrariesVersions,
Set<String> mimeGroupNames, @NonNull UUID domainSetId) {
final PackageSetting pkgSetting;
@@ -940,6 +944,8 @@
// overwrite the signatures in the original package setting.
.setSignatures(new PackageSignatures())
.setLongVersionCode(versionCode)
+ .setUsesSdkLibraries(usesSdkLibraries)
+ .setUsesSdkLibrariesVersionsMajor(usesSdkLibrariesVersions)
.setUsesStaticLibraries(usesStaticLibraries)
.setUsesStaticLibrariesVersions(usesStaticLibrariesVersions)
// Update new package state.
@@ -951,8 +957,9 @@
pkgSetting = new PackageSetting(pkgName, realPkgName, codePath,
legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi,
null /*cpuAbiOverrideString*/, versionCode, pkgFlags, pkgPrivateFlags,
- 0 /*sharedUserId*/, usesStaticLibraries,
- usesStaticLibrariesVersions, createMimeGroups(mimeGroupNames), domainSetId);
+ 0 /*sharedUserId*/, usesSdkLibraries, usesSdkLibrariesVersions,
+ usesStaticLibraries, usesStaticLibrariesVersions,
+ createMimeGroups(mimeGroupNames), domainSetId);
pkgSetting.setLastModifiedTime(codePath.lastModified());
pkgSetting.setSharedUser(sharedUser);
// If this is not a system app, it starts out stopped.
@@ -1046,6 +1053,7 @@
@NonNull File codePath, @Nullable String legacyNativeLibraryPath,
@Nullable String primaryCpuAbi, @Nullable String secondaryCpuAbi, int pkgFlags,
int pkgPrivateFlags, @NonNull UserManagerService userManager,
+ @Nullable String[] usesSdkLibraries, @Nullable long[] usesSdkLibrariesVersions,
@Nullable String[] usesStaticLibraries, @Nullable long[] usesStaticLibrariesVersions,
@Nullable Set<String> mimeGroupNames, @NonNull UUID domainSetId)
throws PackageManagerException {
@@ -1095,7 +1103,17 @@
.setSecondaryCpuAbi(secondaryCpuAbi)
.updateMimeGroups(mimeGroupNames)
.setDomainSetId(domainSetId);
- // Update static shared library dependencies if needed
+ // Update SDK library dependencies if needed.
+ if (usesSdkLibraries != null && usesSdkLibrariesVersions != null
+ && usesSdkLibraries.length == usesSdkLibrariesVersions.length) {
+ pkgSetting.setUsesSdkLibraries(usesSdkLibraries)
+ .setUsesSdkLibrariesVersionsMajor(usesSdkLibrariesVersions);
+ } else {
+ pkgSetting.setUsesSdkLibraries(null)
+ .setUsesSdkLibrariesVersionsMajor(null);
+ }
+
+ // Update static shared library dependencies if needed.
if (usesStaticLibraries != null && usesStaticLibrariesVersions != null
&& usesStaticLibraries.length == usesStaticLibrariesVersions.length) {
pkgSetting.setUsesStaticLibraries(usesStaticLibraries)
@@ -2167,6 +2185,21 @@
}
}
+ void readUsesSdkLibLPw(TypedXmlPullParser parser, PackageSetting outPs)
+ throws IOException, XmlPullParserException {
+ String libName = parser.getAttributeValue(null, ATTR_NAME);
+ long libVersion = parser.getAttributeLong(null, ATTR_VERSION, -1);
+
+ if (libName != null && libVersion >= 0) {
+ outPs.setUsesSdkLibraries(ArrayUtils.appendElement(String.class,
+ outPs.getUsesSdkLibraries(), libName));
+ outPs.setUsesSdkLibrariesVersionsMajor(ArrayUtils.appendLong(
+ outPs.getUsesSdkLibrariesVersionsMajor(), libVersion));
+ }
+
+ XmlUtils.skipCurrentTag(parser);
+ }
+
void readUsesStaticLibLPw(TypedXmlPullParser parser, PackageSetting outPs)
throws IOException, XmlPullParserException {
String libName = parser.getAttributeValue(null, ATTR_NAME);
@@ -2182,6 +2215,23 @@
XmlUtils.skipCurrentTag(parser);
}
+ void writeUsesSdkLibLPw(TypedXmlSerializer serializer, String[] usesSdkLibraries,
+ long[] usesSdkLibraryVersions) throws IOException {
+ if (ArrayUtils.isEmpty(usesSdkLibraries) || ArrayUtils.isEmpty(usesSdkLibraryVersions)
+ || usesSdkLibraries.length != usesSdkLibraryVersions.length) {
+ return;
+ }
+ final int libCount = usesSdkLibraries.length;
+ for (int i = 0; i < libCount; i++) {
+ final String libName = usesSdkLibraries[i];
+ final long libVersion = usesSdkLibraryVersions[i];
+ serializer.startTag(null, TAG_USES_SDK_LIB);
+ serializer.attribute(null, ATTR_NAME, libName);
+ serializer.attributeLong(null, ATTR_VERSION, libVersion);
+ serializer.endTag(null, TAG_USES_SDK_LIB);
+ }
+ }
+
void writeUsesStaticLibLPw(TypedXmlSerializer serializer, String[] usesStaticLibraries,
long[] usesStaticLibraryVersions) throws IOException {
if (ArrayUtils.isEmpty(usesStaticLibraries) || ArrayUtils.isEmpty(usesStaticLibraryVersions)
@@ -2707,6 +2757,9 @@
}
serializer.attributeFloat(null, "loadingProgress", pkg.getLoadingProgress());
+ writeUsesSdkLibLPw(serializer, pkg.getUsesSdkLibraries(),
+ pkg.getUsesSdkLibrariesVersionsMajor());
+
writeUsesStaticLibLPw(serializer, pkg.getUsesStaticLibraries(),
pkg.getUsesStaticLibrariesVersions());
@@ -2785,6 +2838,9 @@
serializer.attribute(null, "domainSetId", pkg.getDomainSetId().toString());
+ writeUsesSdkLibLPw(serializer, pkg.getUsesSdkLibraries(),
+ pkg.getUsesSdkLibrariesVersionsMajor());
+
writeUsesStaticLibLPw(serializer, pkg.getUsesStaticLibraries(),
pkg.getUsesStaticLibrariesVersions());
@@ -3455,8 +3511,8 @@
UUID domainSetId = DomainVerificationManagerInternal.DISABLED_ID;
PackageSetting ps = new PackageSetting(name, realName, new File(codePathStr),
legacyNativeLibraryPathStr, primaryCpuAbiStr, secondaryCpuAbiStr, cpuAbiOverrideStr,
- versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, null, null, null,
- domainSetId);
+ versionCode, pkgFlags, pkgPrivateFlags, 0 /*sharedUserId*/, null, null, null, null,
+ null, domainSetId);
long timeStamp = parser.getAttributeLongHex(null, "ft", 0);
if (timeStamp == 0) {
timeStamp = parser.getAttributeLong(null, "ts", 0);
@@ -3484,6 +3540,8 @@
readInstallPermissionsLPr(parser, ps.getLegacyPermissionState(), users);
} else if (parser.getName().equals(TAG_USES_STATIC_LIB)) {
readUsesStaticLibLPw(parser, ps);
+ } else if (parser.getName().equals(TAG_USES_SDK_LIB)) {
+ readUsesSdkLibLPw(parser, ps);
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unknown element under <updated-package>: " + parser.getName());
@@ -3642,8 +3700,9 @@
packageSetting = addPackageLPw(name.intern(), realName, new File(codePathStr),
legacyNativeLibraryPathStr, primaryCpuAbiString, secondaryCpuAbiString,
cpuAbiOverrideString, userId, versionCode, pkgFlags, pkgPrivateFlags,
- null /*usesStaticLibraries*/, null /*usesStaticLibraryVersions*/,
- null /*mimeGroups*/, domainSetId);
+ null /* usesSdkLibraries */, null /* usesSdkLibraryVersions */,
+ null /* usesStaticLibraries */, null /* usesStaticLibraryVersions */,
+ null /* mimeGroups */, domainSetId);
if (PackageManagerService.DEBUG_SETTINGS)
Log.i(PackageManagerService.TAG, "Reading package " + name + ": userId="
+ userId + " pkg=" + packageSetting);
@@ -3662,9 +3721,11 @@
new File(codePathStr), legacyNativeLibraryPathStr,
primaryCpuAbiString, secondaryCpuAbiString, cpuAbiOverrideString,
versionCode, pkgFlags, pkgPrivateFlags, sharedUserId,
- null /*usesStaticLibraries*/,
- null /*usesStaticLibraryVersions*/,
- null /*mimeGroups*/, domainSetId);
+ null /* usesSdkLibraries */,
+ null /* usesSdkLibrariesVersions */,
+ null /* usesStaticLibraries */,
+ null /* usesStaticLibraryVersions */,
+ null /* mimeGroups */, domainSetId);
packageSetting.setLastModifiedTime(timeStamp);
packageSetting.setFirstInstallTime(firstInstallTime);
packageSetting.setLastUpdateTime(lastUpdateTime);
@@ -3793,6 +3854,8 @@
}
} else if (tagName.equals(TAG_USES_STATIC_LIB)) {
readUsesStaticLibLPw(parser, packageSetting);
+ } else if (tagName.equals(TAG_USES_SDK_LIB)) {
+ readUsesSdkLibLPw(parser, packageSetting);
} else {
PackageManagerService.reportSettingsProblem(Log.WARN,
"Unknown element under <package>: " + parser.getName());
@@ -4581,6 +4644,13 @@
pw.print(" version:"); pw.println(pkg.getStaticSharedLibVersion());
}
+ if (pkg.getSdkLibName() != null) {
+ pw.print(prefix); pw.println(" SDK library:");
+ pw.print(prefix); pw.print(" ");
+ pw.print("name:"); pw.print(pkg.getSdkLibName());
+ pw.print(" versionMajor:"); pw.println(pkg.getSdkLibVersionMajor());
+ }
+
List<String> usesLibraries = pkg.getUsesLibraries();
if (usesLibraries.size() > 0) {
pw.print(prefix); pw.println(" usesLibraries:");
@@ -4600,6 +4670,17 @@
}
}
+ List<String> usesSdkLibraries = pkg.getUsesSdkLibraries();
+ long[] usesSdkLibrariesVersionsMajor = pkg.getUsesSdkLibrariesVersionsMajor();
+ if (usesSdkLibraries.size() > 0) {
+ pw.print(prefix); pw.println(" usesSdkLibraries:");
+ for (int i = 0, size = usesSdkLibraries.size(); i < size; ++i) {
+ pw.print(prefix); pw.print(" ");
+ pw.print(usesSdkLibraries.get(i)); pw.print(" version:");
+ pw.println(usesSdkLibrariesVersionsMajor[i]);
+ }
+ }
+
List<String> usesOptionalLibraries = pkg.getUsesOptionalLibraries();
if (usesOptionalLibraries.size() > 0) {
pw.print(prefix); pw.println(" usesOptionalLibraries:");
diff --git a/services/core/java/com/android/server/pm/SharedLibraryHelper.java b/services/core/java/com/android/server/pm/SharedLibraryHelper.java
index 461fca1..dd8fad0 100644
--- a/services/core/java/com/android/server/pm/SharedLibraryHelper.java
+++ b/services/core/java/com/android/server/pm/SharedLibraryHelper.java
@@ -76,12 +76,15 @@
Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingSharedLibraries) {
// Let's used the parsed package as scanResult.pkgSetting may be null
final ParsedPackage parsedPackage = scanResult.mRequest.mParsedPackage;
- if (scanResult.mStaticSharedLibraryInfo == null
+ if (scanResult.mSdkSharedLibraryInfo == null && scanResult.mStaticSharedLibraryInfo == null
&& scanResult.mDynamicSharedLibraryInfos == null) {
return null;
}
- // Any app can add new static shared libraries
+ // Any app can add new SDKs and static shared libraries.
+ if (scanResult.mSdkSharedLibraryInfo != null) {
+ return Collections.singletonList(scanResult.mSdkSharedLibraryInfo);
+ }
if (scanResult.mStaticSharedLibraryInfo != null) {
return Collections.singletonList(scanResult.mStaticSharedLibraryInfo);
}
@@ -181,41 +184,49 @@
ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
if (!pkg.getUsesLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null,
- pkg.getPackageName(), true, pkg.getTargetSdkVersion(), null,
+ pkg.getPackageName(), "shared", true, pkg.getTargetSdkVersion(), null,
availablePackages, existingLibraries, newLibraries);
}
if (!pkg.getUsesStaticLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(),
pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(),
- pkg.getPackageName(), true, pkg.getTargetSdkVersion(), usesLibraryInfos,
- availablePackages, existingLibraries, newLibraries);
+ pkg.getPackageName(), "static shared", true, pkg.getTargetSdkVersion(),
+ usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
}
if (!pkg.getUsesOptionalLibraries().isEmpty()) {
- usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(),
- null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(), null, null,
+ pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(),
usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
}
if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES,
pkg.getPackageName(), pkg.getTargetSdkVersion())) {
if (!pkg.getUsesNativeLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null,
- null, pkg.getPackageName(), true, pkg.getTargetSdkVersion(),
- usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
+ null, pkg.getPackageName(), "native shared", true,
+ pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
+ existingLibraries, newLibraries);
}
if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalNativeLibraries(),
- null, null, pkg.getPackageName(), false, pkg.getTargetSdkVersion(),
- usesLibraryInfos, availablePackages, existingLibraries, newLibraries);
+ null, null, pkg.getPackageName(), "native shared", false,
+ pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
+ existingLibraries, newLibraries);
}
}
+ if (!pkg.getUsesSdkLibraries().isEmpty()) {
+ usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesSdkLibraries(),
+ pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(),
+ pkg.getPackageName(), "sdk", true, pkg.getTargetSdkVersion(), usesLibraryInfos,
+ availablePackages, existingLibraries, newLibraries);
+ }
return usesLibraryInfos;
}
public static ArrayList<SharedLibraryInfo> collectSharedLibraryInfos(
@NonNull List<String> requestedLibraries,
@Nullable long[] requiredVersions, @Nullable String[][] requiredCertDigests,
- @NonNull String packageName, boolean required, int targetSdk,
- @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
+ @NonNull String packageName, @NonNull String libraryType, boolean required,
+ int targetSdk, @Nullable ArrayList<SharedLibraryInfo> outUsedLibraries,
@NonNull final Map<String, AndroidPackage> availablePackages,
@NonNull final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> existingLibraries,
@Nullable final Map<String, WatchedLongSparseArray<SharedLibraryInfo>> newLibraries)
@@ -230,18 +241,17 @@
if (libraryInfo == null) {
if (required) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires unavailable shared library "
- + libName + "; failing!");
+ "Package " + packageName + " requires unavailable " + libraryType
+ + " library " + libName + "; failing!");
} else if (DEBUG_SHARED_LIBRARIES) {
- Slog.i(TAG, "Package " + packageName
- + " desires unavailable shared library "
- + libName + "; ignoring!");
+ Slog.i(TAG, "Package " + packageName + " desires unavailable " + libraryType
+ + " library " + libName + "; ignoring!");
}
} else {
if (requiredVersions != null && requiredCertDigests != null) {
if (libraryInfo.getLongVersion() != requiredVersions[i]) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires unavailable static shared"
+ "Package " + packageName + " requires unavailable " + libraryType
+ " library " + libName + " version "
+ libraryInfo.getLongVersion() + "; failing!");
}
@@ -249,7 +259,7 @@
SigningDetails libPkg = pkg == null ? null : pkg.getSigningDetails();
if (libPkg == null) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires unavailable static shared"
+ "Package " + packageName + " requires unavailable " + libraryType
+ " library; failing!");
}
final String[] expectedCertDigests = requiredCertDigests[i];
@@ -267,8 +277,8 @@
// Therefore, the size check is safe to make.
if (expectedCertDigests.length != libCertDigests.length) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires differently signed"
- + " static shared library; failing!");
+ "Package " + packageName + " requires differently signed "
+ + libraryType + " library; failing!");
}
// Use a predictable order as signature order may vary
@@ -280,8 +290,8 @@
if (!libCertDigests[j].equalsIgnoreCase(expectedCertDigests[j])) {
throw new PackageManagerException(
INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires differently signed"
- + " static shared library; failing!");
+ "Package " + packageName + " requires differently signed "
+ + libraryType + " library; failing!");
}
}
} else {
@@ -290,10 +300,9 @@
byte[] digestBytes = HexEncoding.decode(
expectedCertDigests[0], false /* allowSingleChar */);
if (!libPkg.hasSha256Certificate(digestBytes)) {
- throw new PackageManagerException(
- INSTALL_FAILED_MISSING_SHARED_LIBRARY,
- "Package " + packageName + " requires differently signed"
- + " static shared library; failing!");
+ throw new PackageManagerException(INSTALL_FAILED_MISSING_SHARED_LIBRARY,
+ "Package " + packageName + " requires differently signed "
+ + libraryType + " library; failing!");
}
}
}
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index 32b1e5d..8b2c3a12 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -87,6 +87,17 @@
return paths;
}
+ public static SharedLibraryInfo createSharedLibraryForSdk(AndroidPackage pkg) {
+ return new SharedLibraryInfo(null, pkg.getPackageName(),
+ AndroidPackageUtils.getAllCodePaths(pkg),
+ pkg.getSdkLibName(),
+ pkg.getSdkLibVersionMajor(),
+ SharedLibraryInfo.TYPE_SDK,
+ new VersionedPackage(pkg.getManifestPackageName(),
+ pkg.getLongVersionCode()),
+ null, null, false /* isNative */);
+ }
+
public static SharedLibraryInfo createSharedLibraryForStatic(AndroidPackage pkg) {
return new SharedLibraryInfo(null, pkg.getPackageName(),
AndroidPackageUtils.getAllCodePaths(pkg),
@@ -218,7 +229,8 @@
public static boolean isLibrary(AndroidPackage pkg) {
// TODO(b/135203078): Can parsing just enforce these always match?
- return pkg.getStaticSharedLibName() != null || !pkg.getLibraryNames().isEmpty();
+ return pkg.getSdkLibName() != null || pkg.getStaticSharedLibName() != null
+ || !pkg.getLibraryNames().isEmpty();
}
public static int getHiddenApiEnforcementPolicy(AndroidPackage pkg,
diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java
index 82edce6..34575e08 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageState.java
@@ -27,7 +27,6 @@
import android.content.pm.SigningInfo;
import android.util.SparseArray;
-import com.android.internal.R;
import com.android.server.pm.PackageSetting;
import com.android.server.pm.Settings;
@@ -206,6 +205,18 @@
List<SharedLibraryInfo> getUsesLibraryInfos();
/**
+ * @see R.styleable#AndroidManifestUsesSdkLibrary
+ */
+ @NonNull
+ String[] getUsesSdkLibraries();
+
+ /**
+ * @see R.styleable#AndroidManifestUsesSdkLibrary_versionMajor
+ */
+ @NonNull
+ long[] getUsesSdkLibrariesVersionsMajor();
+
+ /**
* @see R.styleable#AndroidManifestUsesStaticLibrary
*/
@NonNull
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
index 46d32b9..f5e498d 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
@@ -130,6 +130,10 @@
@Nullable
private final Integer mSharedUserId;
@NonNull
+ private final String[] mUsesSdkLibraries;
+ @NonNull
+ private final long[] mUsesSdkLibrariesVersionsMajor;
+ @NonNull
private final String[] mUsesStaticLibraries;
@NonNull
private final long[] mUsesStaticLibrariesVersions;
@@ -171,6 +175,8 @@
mPrimaryCpuAbi = pkgState.getPrimaryCpuAbi();
mSecondaryCpuAbi = pkgState.getSecondaryCpuAbi();
mSharedUserId = pkgState.getSharedUserId();
+ mUsesSdkLibraries = pkgState.getUsesSdkLibraries();
+ mUsesSdkLibrariesVersionsMajor = pkgState.getUsesSdkLibrariesVersionsMajor();
mUsesStaticLibraries = pkgState.getUsesStaticLibraries();
mUsesStaticLibrariesVersions = pkgState.getUsesStaticLibrariesVersions();
mUsesLibraryInfos = pkgState.getUsesLibraryInfos();
@@ -262,6 +268,11 @@
return getBoolean(Booleans.VENDOR);
}
+ @Override
+ public long getVersionCode() {
+ return mLongVersionCode;
+ }
+
/**
* @hide
*/
@@ -500,10 +511,10 @@
}
@DataClass.Generated(
- time = 1633375703010L,
+ time = 1637977288540L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
- inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\npublic static android.content.pm.pkg.PackageUserState copy(android.content.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [android.content.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
+ inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\npublic static com.android.server.pm.pkg.PackageUserState copy(com.android.server.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@Deprecated
private void __metadata() {}
@@ -579,7 +590,7 @@
}
@DataClass.Generated.Member
- public long getVersionCode() {
+ public long getLongVersionCode() {
return mLongVersionCode;
}
@@ -609,6 +620,16 @@
}
@DataClass.Generated.Member
+ public @NonNull String[] getUsesSdkLibraries() {
+ return mUsesSdkLibraries;
+ }
+
+ @DataClass.Generated.Member
+ public @NonNull long[] getUsesSdkLibrariesVersionsMajor() {
+ return mUsesSdkLibrariesVersionsMajor;
+ }
+
+ @DataClass.Generated.Member
public @NonNull String[] getUsesStaticLibraries() {
return mUsesStaticLibraries;
}
@@ -650,10 +671,10 @@
}
@DataClass.Generated(
- time = 1633375703038L,
+ time = 1637977288579L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
- inputSignatures = "private int mBooleans\nprivate final @android.annotation.Nullable com.android.server.pm.pkg.AndroidPackageApi mAndroidPackage\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mVolumeUuid\nprivate final int mAppId\nprivate final int mCategoryOverride\nprivate final @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate final long mFirstInstallTime\nprivate final long mLastModifiedTime\nprivate final long mLastUpdateTime\nprivate final long mLongVersionCode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mMimeGroups\nprivate final @android.annotation.NonNull java.io.File mPath\nprivate final @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.Integer mSharedUserId\nprivate final @android.annotation.NonNull java.lang.String[] mUsesStaticLibraries\nprivate final @android.annotation.NonNull long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mUsesLibraryInfos\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesLibraryFiles\nprivate final @android.annotation.NonNull long[] mLastPackageUsageTime\nprivate final @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate final @android.annotation.NonNull android.util.SparseArray<android.content.pm.pkg.PackageUserState> mUserStates\npublic static com.android.server.pm.pkg.PackageState copy(com.android.server.pm.PackageSetting)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isExternalStorage()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isOdm()\npublic @java.lang.Override boolean isOem()\npublic @java.lang.Override boolean isPrivileged()\npublic @java.lang.Override boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic @java.lang.Override boolean isSystem()\npublic @java.lang.Override boolean isSystemExt()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isVendor()\nclass PackageStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageState]\nprivate static final int SYSTEM\nprivate static final int EXTERNAL_STORAGE\nprivate static final int PRIVILEGED\nprivate static final int OEM\nprivate static final int VENDOR\nprivate static final int PRODUCT\nprivate static final int SYSTEM_EXT\nprivate static final int REQUIRED_FOR_SYSTEM_USER\nprivate static final int ODM\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int HIDDEN_UNTIL_INSTALLED\nprivate static final int INSTALL_PERMISSIONS_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int UPDATED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
+ inputSignatures = "private int mBooleans\nprivate final @android.annotation.Nullable com.android.server.pm.pkg.AndroidPackageApi mAndroidPackage\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mVolumeUuid\nprivate final int mAppId\nprivate final int mCategoryOverride\nprivate final @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate final long mFirstInstallTime\nprivate final long mLastModifiedTime\nprivate final long mLastUpdateTime\nprivate final long mLongVersionCode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mMimeGroups\nprivate final @android.annotation.NonNull java.io.File mPath\nprivate final @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.Integer mSharedUserId\nprivate final @android.annotation.NonNull java.lang.String[] mUsesSdkLibraries\nprivate final @android.annotation.NonNull long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.NonNull java.lang.String[] mUsesStaticLibraries\nprivate final @android.annotation.NonNull long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mUsesLibraryInfos\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesLibraryFiles\nprivate final @android.annotation.NonNull long[] mLastPackageUsageTime\nprivate final @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserState> mUserStates\npublic static com.android.server.pm.pkg.PackageState copy(com.android.server.pm.pkg.PackageStateInternal)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isExternalStorage()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isOdm()\npublic @java.lang.Override boolean isOem()\npublic @java.lang.Override boolean isPrivileged()\npublic @java.lang.Override boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic @java.lang.Override boolean isSystem()\npublic @java.lang.Override boolean isSystemExt()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isVendor()\npublic @java.lang.Override long getVersionCode()\nclass PackageStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageState]\nprivate static final int SYSTEM\nprivate static final int EXTERNAL_STORAGE\nprivate static final int PRIVILEGED\nprivate static final int OEM\nprivate static final int VENDOR\nprivate static final int PRODUCT\nprivate static final int SYSTEM_EXT\nprivate static final int REQUIRED_FOR_SYSTEM_USER\nprivate static final int ODM\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int HIDDEN_UNTIL_INSTALLED\nprivate static final int INSTALL_PERMISSIONS_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int UPDATED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@Deprecated
private void __metadata() {}
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/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 9d8f1fc..2e318bc 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -808,7 +808,7 @@
};
private final Consumer<WindowState> mPerformLayout = w -> {
- if (w.mLayoutAttached || w.skipLayout()) {
+ if (w.mLayoutAttached) {
return;
}
@@ -866,7 +866,7 @@
};
private final Consumer<WindowState> mPerformLayoutAttached = w -> {
- if (!w.mLayoutAttached || w.skipLayout()) {
+ if (!w.mLayoutAttached) {
return;
}
if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + w + " mHaveFrame=" + w.mHaveFrame
@@ -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/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index cbb9d5d..7a2a311 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1494,6 +1494,9 @@
* @param displayFrames The display frames.
*/
public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
+ if (win.skipLayout()) {
+ return;
+ }
// This window might be in the simulated environment.
// We invoke this to get the proper DisplayFrames.
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/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
index 9d86081..4b12fd4 100644
--- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
+++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt
@@ -274,7 +274,7 @@
private fun makePkgSetting(pkgName: String) = spy(
PackageSetting(
pkgName, null, File("/test"),
- null, null, null, null, 0, 0, 0, 0, null, null, null,
+ null, null, null, null, 0, 0, 0, 0, null, null, null, null, null,
UUID.fromString("3f9d52b7-d7b4-406a-a1da-d9f19984c72c")
)
) {
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
index dc93e53..4a35fdf 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt
@@ -106,6 +106,16 @@
"setSplitCodePaths",
"setSplitClassLoaderName",
"setSplitHasCode",
+ // Tested through addUsesSdkLibrary
+ "addUsesSdkLibrary",
+ "getUsesSdkLibraries",
+ "getUsesSdkLibrariesVersionsMajor",
+ "getUsesSdkLibrariesCertDigests",
+ // Tested through addUsesStaticLibrary
+ "addUsesStaticLibrary",
+ "getUsesStaticLibraries",
+ "getUsesStaticLibrariesVersions",
+ "getUsesStaticLibrariesCertDigests"
)
override val baseParams = listOf(
@@ -157,6 +167,8 @@
AndroidPackage::getSecondaryNativeLibraryDir,
AndroidPackage::getSharedUserId,
AndroidPackage::getSharedUserLabel,
+ AndroidPackage::getSdkLibName,
+ AndroidPackage::getSdkLibVersionMajor,
AndroidPackage::getStaticSharedLibName,
AndroidPackage::getStaticSharedLibVersion,
AndroidPackage::getTargetSandboxVersion,
@@ -210,6 +222,7 @@
AndroidPackage::isResizeableActivityViaSdkVersion,
AndroidPackage::isRestoreAnyVersion,
AndroidPackage::isSignedWithPlatformKey,
+ AndroidPackage::isSdkLibrary,
AndroidPackage::isStaticSharedLibrary,
AndroidPackage::isStub,
AndroidPackage::isSupportsRtl,
@@ -228,7 +241,7 @@
AndroidPackage::getMinAspectRatio,
AndroidPackage::hasPreserveLegacyExternalStorage,
AndroidPackage::hasRequestForegroundServiceExemption,
- AndroidPackage::hasRequestRawExternalStorageAccess,
+ AndroidPackage::hasRequestRawExternalStorageAccess
)
override fun extraParams() = listOf(
@@ -254,13 +267,6 @@
adder(AndroidPackage::getUsesNativeLibraries, "testUsesNativeLibrary"),
adder(AndroidPackage::getUsesOptionalLibraries, "testUsesOptionalLibrary"),
adder(AndroidPackage::getUsesOptionalNativeLibraries, "testUsesOptionalNativeLibrary"),
- adder(AndroidPackage::getUsesStaticLibraries, "testUsesStaticLibrary"),
- getSetByValue(
- AndroidPackage::getUsesStaticLibrariesVersions,
- PackageImpl::addUsesStaticLibraryVersion,
- (testCounter++).toLong(),
- transformGet = { it?.singleOrNull() }
- ),
getSetByValue(
AndroidPackage::areAttributionsUserVisible,
ParsingPackage::setAttributionsAreUserVisible,
@@ -290,7 +296,7 @@
AndroidPackage::getKeySetMapping,
PackageImpl::addKeySet,
"testKeySetName" to testKey(),
- transformGet = { "testKeySetName" to it["testKeySetName"]?.singleOrNull() },
+ transformGet = { "testKeySetName" to it["testKeySetName"]?.singleOrNull() }
),
getSetByValue(
AndroidPackage::getPermissionGroups,
@@ -315,7 +321,7 @@
{ it.first },
{ it.second.intentFilter.schemesIterator().asSequence().singleOrNull() },
{ it.second.intentFilter.authoritiesIterator().asSequence()
- .singleOrNull()?.host },
+ .singleOrNull()?.host }
)
}
),
@@ -324,7 +330,7 @@
PackageImpl::addQueriesIntent,
Intent(Intent.ACTION_VIEW, Uri.parse("https://test.pm.server.android.com")),
transformGet = { it.singleOrNull() },
- compare = { first, second -> first?.filterEquals(second) },
+ compare = { first, second -> first?.filterEquals(second) }
),
getSetByValue(
AndroidPackage::getRestrictUpdateHash,
@@ -347,13 +353,6 @@
}
),
getSetByValue(
- AndroidPackage::getUsesStaticLibrariesCertDigests,
- PackageImpl::addUsesStaticLibraryCertDigests,
- arrayOf("testCertDigest"),
- transformGet = { it?.singleOrNull() },
- compare = Array<String?>?::contentEquals
- ),
- getSetByValue(
AndroidPackage::getActivities,
PackageImpl::addActivity,
"TestActivityName",
@@ -440,7 +439,7 @@
first, second,
{ it.size() },
{ it.keyAt(0) },
- { it.valueAt(0) },
+ { it.valueAt(0) }
)
}
),
@@ -451,7 +450,7 @@
compare = { first, second ->
equalBy(
first, second,
- { it["testProcess"]?.name },
+ { it["testProcess"]?.name }
)
}
),
@@ -471,10 +470,10 @@
PackageManager.Property::getName,
PackageManager.Property::getClassName,
PackageManager.Property::getPackageName,
- PackageManager.Property::getString,
+ PackageManager.Property::getString
)
}
- ),
+ )
)
override fun initialObject() = PackageImpl.forParsing(
@@ -518,6 +517,9 @@
.setSplitClassLoaderName(0, "testSplitClassLoaderNameZero")
.setSplitClassLoaderName(1, "testSplitClassLoaderNameOne")
+ .addUsesSdkLibrary("testSdk", 2L, arrayOf("testCertDigest1"))
+ .addUsesStaticLibrary("testStatic", 3L, arrayOf("testCertDigest2"))
+
override fun extraAssertions(before: Parcelable, after: Parcelable) {
super.extraAssertions(before, after)
after as PackageImpl
@@ -558,6 +560,18 @@
expect.that(it.get(0)).asList().containsExactly(-1)
expect.that(it.get(1)).asList().containsExactly(0)
}
+
+ expect.that(after.usesSdkLibraries).containsExactly("testSdk")
+ expect.that(after.usesSdkLibrariesVersionsMajor).asList().containsExactly(2L)
+ expect.that(after.usesSdkLibrariesCertDigests!!.size).isEqualTo(1)
+ expect.that(after.usesSdkLibrariesCertDigests!![0]).asList()
+ .containsExactly("testCertDigest1")
+
+ expect.that(after.usesStaticLibraries).containsExactly("testStatic")
+ expect.that(after.usesStaticLibrariesVersions).asList().containsExactly(3L)
+ expect.that(after.usesStaticLibrariesCertDigests!!.size).isEqualTo(1)
+ expect.that(after.usesStaticLibrariesCertDigests!![0]).asList()
+ .containsExactly("testCertDigest2")
}
private fun testKey() = KeyPairGenerator.getInstance("RSA")
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/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 0e5640a..ae5984a41 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -150,7 +150,7 @@
}
whenever(mocks.settings.addPackageLPw(nullable(), nullable(), nullable(), nullable(),
nullable(), nullable(), nullable(), nullable(), nullable(), nullable(), nullable(),
- nullable(), nullable(), nullable(), nullable())) {
+ nullable(), nullable(), nullable(), nullable(), nullable(), nullable())) {
val name: String = getArgument(0)
val pendingAdd = mPendingPackageAdds.firstOrNull { it.first == name }
?: return@whenever null
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/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index a9a3469..6c9f8fe 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -498,6 +498,71 @@
}
@Test
+ public void testWriteReadUsesSdkLibraries() {
+ final Settings settingsUnderTest = makeSettings();
+ final PackageSetting ps1 = createPackageSetting(PACKAGE_NAME_1);
+ ps1.setAppId(Process.FIRST_APPLICATION_UID);
+ ps1.setPkg(((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_1).hideAsParsed())
+ .setUid(ps1.getAppId())
+ .setSystem(true)
+ .hideAsFinal());
+ final PackageSetting ps2 = createPackageSetting(PACKAGE_NAME_2);
+ ps2.setAppId(Process.FIRST_APPLICATION_UID + 1);
+ ps2.setPkg(((ParsedPackage) PackageImpl.forTesting(PACKAGE_NAME_2).hideAsParsed())
+ .setUid(ps2.getAppId())
+ .hideAsFinal());
+
+ ps1.setUsesSdkLibraries(new String[] { "com.example.sdk.one" });
+ ps1.setUsesSdkLibrariesVersionsMajor(new long[] { 12 });
+ ps1.setFlags(ps1.getFlags() | ApplicationInfo.FLAG_SYSTEM);
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
+ assertThat(settingsUnderTest.disableSystemPackageLPw(PACKAGE_NAME_1, false), is(true));
+
+ ps2.setUsesSdkLibraries(new String[] { "com.example.sdk.two" });
+ ps2.setUsesSdkLibrariesVersionsMajor(new long[] { 34 });
+ settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
+
+ settingsUnderTest.writeLPr();
+
+ settingsUnderTest.mPackages.clear();
+ settingsUnderTest.mDisabledSysPackages.clear();
+
+ assertThat(settingsUnderTest.readLPw(createFakeUsers()), is(true));
+
+ PackageSetting readPs1 = settingsUnderTest.getPackageLPr(PACKAGE_NAME_1);
+ PackageSetting readPs2 = settingsUnderTest.getPackageLPr(PACKAGE_NAME_2);
+
+ Truth.assertThat(readPs1).isNotNull();
+ Truth.assertThat(readPs1.getUsesSdkLibraries()).isNotNull();
+ Truth.assertThat(readPs1.getUsesSdkLibrariesVersionsMajor()).isNotNull();
+ Truth.assertThat(readPs2).isNotNull();
+ Truth.assertThat(readPs2.getUsesSdkLibraries()).isNotNull();
+ Truth.assertThat(readPs2.getUsesSdkLibrariesVersionsMajor()).isNotNull();
+
+ List<Long> ps1VersionsAsList = new ArrayList<>();
+ for (long version : ps1.getUsesSdkLibrariesVersionsMajor()) {
+ ps1VersionsAsList.add(version);
+ }
+
+ List<Long> ps2VersionsAsList = new ArrayList<>();
+ for (long version : ps2.getUsesSdkLibrariesVersionsMajor()) {
+ ps2VersionsAsList.add(version);
+ }
+
+ Truth.assertThat(readPs1.getUsesSdkLibraries()).asList()
+ .containsExactlyElementsIn(ps1.getUsesSdkLibraries()).inOrder();
+
+ Truth.assertThat(readPs1.getUsesSdkLibrariesVersionsMajor()).asList()
+ .containsExactlyElementsIn(ps1VersionsAsList).inOrder();
+
+ Truth.assertThat(readPs2.getUsesSdkLibraries()).asList()
+ .containsExactlyElementsIn(ps2.getUsesSdkLibraries()).inOrder();
+
+ Truth.assertThat(readPs2.getUsesSdkLibrariesVersionsMajor()).asList()
+ .containsExactlyElementsIn(ps2VersionsAsList).inOrder();
+ }
+
+ @Test
public void testPackageRestrictionsDistractionFlagsDefault() {
final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1);
assertThat(defaultSetting.getDistractionFlags(0), is(PackageManager.RESTRICTION_NONE));
@@ -571,6 +636,8 @@
ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_HAS_CODE,
ApplicationInfo.PRIVATE_FLAG_PRIVILEGED|ApplicationInfo.PRIVATE_FLAG_HIDDEN,
0,
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -593,6 +660,8 @@
ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_HAS_CODE,
ApplicationInfo.PRIVATE_FLAG_PRIVILEGED|ApplicationInfo.PRIVATE_FLAG_HIDDEN,
0,
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -609,6 +678,8 @@
0 /*pkgFlags*/,
0 /*pkgPrivateFlags*/,
0,
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -637,6 +708,8 @@
0 /*pkgFlags*/,
0 /*pkgPrivateFlags*/,
UserManagerService.getInstance(),
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -671,6 +744,8 @@
ApplicationInfo.FLAG_SYSTEM /*pkgFlags*/,
ApplicationInfo.PRIVATE_FLAG_PRIVILEGED /*pkgPrivateFlags*/,
UserManagerService.getInstance(),
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -708,6 +783,8 @@
0 /*pkgFlags*/,
0 /*pkgPrivateFlags*/,
UserManagerService.getInstance(),
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -741,6 +818,8 @@
false /*instantApp*/,
false /*virtualPreload*/,
UserManagerService.getInstance(),
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -780,6 +859,8 @@
false /*instantApp*/,
false /*virtualPreload*/,
UserManagerService.getInstance(),
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -822,6 +903,8 @@
false /*instantApp*/,
false /*virtualPreload*/,
UserManagerService.getInstance(),
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -864,6 +947,8 @@
false /*instantApp*/,
false /*virtualPreload*/,
UserManagerService.getInstance(),
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -971,6 +1056,25 @@
assertThat(countDownLatch.getCount(), is(1L));
}
+ @Test
+ public void testSetPkgStateLibraryFiles_addNewSdks() {
+ final PackageSetting packageSetting = createPackageSetting("com.foo");
+ final CountDownLatch countDownLatch = new CountDownLatch(1);
+ packageSetting.registerObserver(new Watcher() {
+ @Override
+ public void onChange(Watchable what) {
+ countDownLatch.countDown();
+ }
+ });
+
+ final List<String> files = new ArrayList<>();
+ files.add("com.sdk1_123");
+ files.add("com.sdk9_876");
+ packageSetting.setUsesSdkLibraries(files.toArray(new String[files.size()]));
+
+ assertThat(countDownLatch.getCount(), is(0L));
+ }
+
private <T> void assertArrayEquals(T[] a, T[] b) {
assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b),
Arrays.equals(a, b));
@@ -1090,6 +1194,8 @@
pkgFlags,
0 /*privateFlags*/,
sharedUserId,
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
@@ -1109,6 +1215,8 @@
0,
0 /*privateFlags*/,
0,
+ null /*usesSdkLibraries*/,
+ null /*usesSdkLibrariesVersions*/,
null /*usesStaticLibraries*/,
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index fb092d2..11bac45 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -932,11 +932,12 @@
.addUsesPermission(new ParsedUsesPermissionImpl("foo7", 0))
.addImplicitPermission("foo25")
.addProtectedBroadcast("foo8")
+ .setSdkLibName("sdk12")
+ .setSdkLibVersionMajor(42)
+ .addUsesSdkLibrary("sdk23", 200, new String[]{"digest2"})
.setStaticSharedLibName("foo23")
.setStaticSharedLibVersion(100)
- .addUsesStaticLibrary("foo23")
- .addUsesStaticLibraryCertDigests(new String[]{"digest"})
- .addUsesStaticLibraryVersion(100)
+ .addUsesStaticLibrary("foo23", 100, new String[]{"digest"})
.addLibraryName("foo10")
.addUsesLibrary("foo11")
.addUsesOptionalLibrary("foo12")
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
index 2146070..94d8358 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSettingBuilder.java
@@ -44,8 +44,6 @@
private SparseArray<PackageUserStateImpl> mUserStates = new SparseArray<>();
private AndroidPackage mPkg;
private InstallSource mInstallSource;
- private String[] mUsesStaticLibraries;
- private long[] mUsesStaticLibrariesVersions;
private Map<String, Set<String>> mMimeGroups;
private SigningDetails mSigningDetails;
private UUID mDomainSetId = UUID.randomUUID();
@@ -116,17 +114,6 @@
return this;
}
- public PackageSettingBuilder setUsesStaticLibraries(String[] usesStaticLibraries) {
- this.mUsesStaticLibraries = usesStaticLibraries;
- return this;
- }
-
- public PackageSettingBuilder setUsesStaticLibrariesVersions(
- long[] usesStaticLibrariesVersions) {
- this.mUsesStaticLibrariesVersions = usesStaticLibrariesVersions;
- return this;
- }
-
public PackageSettingBuilder setMimeGroups(Map<String, Set<String>> mimeGroups) {
this.mMimeGroups = mimeGroups;
return this;
@@ -173,8 +160,9 @@
final PackageSetting packageSetting = new PackageSetting(mName, mRealName,
new File(mCodePath), mLegacyNativeLibraryPathString, mPrimaryCpuAbiString,
mSecondaryCpuAbiString, mCpuAbiOverrideString, mPVersionCode, mPkgFlags,
- mPrivateFlags, mSharedUserId, mUsesStaticLibraries, mUsesStaticLibrariesVersions,
- mMimeGroups, mDomainSetId);
+ mPrivateFlags, mSharedUserId, null /* usesSdkLibraries */,
+ null /* usesSdkLibrariesVersions */, null /* usesStaticLibraries */,
+ null /* usesStaticLibrariesVersions */, mMimeGroups, mDomainSetId);
packageSetting.setSignatures(mSigningDetails != null
? new PackageSignatures(mSigningDetails)
: new PackageSignatures());
diff --git a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
index cfdbb5b7..71d5b77 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import static android.content.pm.SharedLibraryInfo.TYPE_DYNAMIC;
+import static android.content.pm.SharedLibraryInfo.TYPE_SDK;
import static android.content.pm.SharedLibraryInfo.TYPE_STATIC;
import static android.content.pm.SharedLibraryInfo.VERSION_UNDEFINED;
@@ -238,6 +239,37 @@
}
@Test
+ public void installSdkLibrary() throws Exception {
+ final ParsedPackage pkg = ((ParsedPackage) createBasicPackage("ogl.sdk_123")
+ .setSdkLibName("ogl.sdk")
+ .setSdkLibVersionMajor(123)
+ .hideAsParsed())
+ .setPackageName("ogl.sdk_123")
+ .setVersionCodeMajor(5)
+ .setVersionCode(678)
+ .setBaseApkPath("/some/path.apk")
+ .setSplitCodePaths(new String[] {"/some/other/path.apk"});
+
+ final ScanRequest scanRequest = new ScanRequestBuilder(pkg)
+ .setUser(UserHandle.of(0)).build();
+
+ final ScanResult scanResult = executeScan(scanRequest);
+
+ assertThat(scanResult.mSdkSharedLibraryInfo.getPackageName(), is("ogl.sdk_123"));
+ assertThat(scanResult.mSdkSharedLibraryInfo.getName(), is("ogl.sdk"));
+ assertThat(scanResult.mSdkSharedLibraryInfo.getLongVersion(), is(123L));
+ assertThat(scanResult.mSdkSharedLibraryInfo.getType(), is(TYPE_SDK));
+ assertThat(scanResult.mSdkSharedLibraryInfo.getDeclaringPackage().getPackageName(),
+ is("ogl.sdk_123"));
+ assertThat(scanResult.mSdkSharedLibraryInfo.getDeclaringPackage().getLongVersionCode(),
+ is(pkg.getLongVersionCode()));
+ assertThat(scanResult.mSdkSharedLibraryInfo.getAllCodePaths(),
+ hasItems("/some/path.apk", "/some/other/path.apk"));
+ assertThat(scanResult.mSdkSharedLibraryInfo.getDependencies(), nullValue());
+ assertThat(scanResult.mSdkSharedLibraryInfo.getDependentPackages(), empty());
+ }
+
+ @Test
public void installStaticSharedLibrary() throws Exception {
final ParsedPackage pkg = ((ParsedPackage) createBasicPackage("static.lib.pkg")
.setStaticSharedLibName("static.lib")
@@ -528,10 +560,10 @@
"/data/tmp/randompath/base.apk", createCodePath(packageName),
mock(TypedArray.class), false)
.setVolumeUuid(UUID_ONE.toString())
- .addUsesStaticLibrary("some.static.library")
- .addUsesStaticLibraryVersion(234L)
- .addUsesStaticLibrary("some.other.static.library")
- .addUsesStaticLibraryVersion(456L)
+ .addUsesStaticLibrary("some.static.library", 234L, new String[]{"testCert1"})
+ .addUsesStaticLibrary("some.other.static.library", 456L, new String[]{"testCert2"})
+ .addUsesSdkLibrary("some.sdk.library", 123L, new String[]{"testCert3"})
+ .addUsesSdkLibrary("some.other.sdk.library", 789L, new String[]{"testCert4"})
.hideAsParsed())
.setNativeLibraryRootDir("/data/tmp/randompath/base.apk:/lib")
.setVersionCodeMajor(1)
@@ -557,6 +589,9 @@
assertThat(pkgSetting.getUsesStaticLibraries(),
arrayContaining("some.static.library", "some.other.static.library"));
assertThat(pkgSetting.getUsesStaticLibrariesVersions(), is(new long[]{234L, 456L}));
+ assertThat(pkgSetting.getUsesSdkLibraries(),
+ arrayContaining("some.sdk.library", "some.other.sdk.library"));
+ assertThat(pkgSetting.getUsesSdkLibrariesVersionsMajor(), is(new long[]{123L, 789L}));
assertThat(pkgSetting.getPkg(), is(scanResult.mRequest.mParsedPackage));
assertThat(pkgSetting.getPath(), is(new File(createCodePath(packageName))));
assertThat(pkgSetting.getVersionCode(),
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..a7a374be 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()));
+ }
+}
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 40bbb36..9828b97 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -1461,6 +1461,64 @@
}
};
+/** Represents <sdk-library> elements. **/
+class SdkLibrary : public ManifestExtractor::Element {
+ public:
+ SdkLibrary() = default;
+ std::string name;
+ int versionMajor;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ versionMajor = GetAttributeIntegerDefault(FindAttribute(element, VERSION_MAJOR_ATTR), 0);
+ }
+ }
+
+ void Print(text::Printer* printer) override {
+ printer->Print(
+ StringPrintf("sdk-library: name='%s' versionMajor='%d'\n", name.data(), versionMajor));
+ }
+};
+
+/** Represents <uses-sdk-library> elements. **/
+class UsesSdkLibrary : public ManifestExtractor::Element {
+ public:
+ UsesSdkLibrary() = default;
+ std::string name;
+ int versionMajor;
+ std::vector<std::string> certDigests;
+
+ void Extract(xml::Element* element) override {
+ auto parent_stack = extractor()->parent_stack();
+ if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+ name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+ versionMajor = GetAttributeIntegerDefault(FindAttribute(element, VERSION_MAJOR_ATTR), 0);
+ AddCertDigest(element);
+ }
+ }
+
+ void AddCertDigest(xml::Element* element) {
+ std::string digest = GetAttributeStringDefault(FindAttribute(element, CERT_DIGEST_ATTR), "");
+ // We allow ":" delimiters in the SHA declaration as this is the format
+ // emitted by the certtool making it easy for developers to copy/paste.
+ digest.erase(std::remove(digest.begin(), digest.end(), ':'), digest.end());
+ if (!digest.empty()) {
+ certDigests.push_back(digest);
+ }
+ }
+
+ void Print(text::Printer* printer) override {
+ printer->Print(
+ StringPrintf("uses-sdk-library: name='%s' versionMajor='%d'", name.data(), versionMajor));
+ for (size_t i = 0; i < certDigests.size(); i++) {
+ printer->Print(StringPrintf(" certDigest='%s'", certDigests[i].data()));
+ }
+ printer->Print("\n");
+ }
+};
+
/** Represents <uses-native-library> elements. **/
class UsesNativeLibrary : public ManifestExtractor::Element {
public:
@@ -2367,6 +2425,7 @@
{"required-not-feature", std::is_base_of<RequiredNotFeature, T>::value},
{"screen", std::is_base_of<Screen, T>::value},
{"service", std::is_base_of<Service, T>::value},
+ {"sdk-library", std::is_base_of<SdkLibrary, T>::value},
{"static-library", std::is_base_of<StaticLibrary, T>::value},
{"supports-gl-texture", std::is_base_of<SupportsGlTexture, T>::value},
{"supports-input", std::is_base_of<SupportsInput, T>::value},
@@ -2379,6 +2438,7 @@
{"uses-permission", std::is_base_of<UsesPermission, T>::value},
{"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value},
{"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
+ {"uses-sdk-library", std::is_base_of<UsesSdkLibrary, T>::value},
{"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
};
@@ -2421,6 +2481,7 @@
{"required-not-feature", &CreateType<RequiredNotFeature>},
{"screen", &CreateType<Screen>},
{"service", &CreateType<Service>},
+ {"sdk-library", &CreateType<SdkLibrary>},
{"static-library", &CreateType<StaticLibrary>},
{"supports-gl-texture", &CreateType<SupportsGlTexture>},
{"supports-input", &CreateType<SupportsInput>},
@@ -2433,6 +2494,7 @@
{"uses-permission", &CreateType<UsesPermission>},
{"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>},
{"uses-sdk", &CreateType<UsesSdkBadging>},
+ {"uses-sdk-library", &CreateType<UsesSdkLibrary>},
{"uses-static-library", &CreateType<UsesStaticLibrary>},
};
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 63b2fcd..b46a125 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -508,6 +508,16 @@
uses_static_library_action.Action(RequiredAndroidAttribute("certDigest"));
uses_static_library_action["additional-certificate"];
+ xml::XmlNodeAction& sdk_library_action = application_action["sdk-library"];
+ sdk_library_action.Action(RequiredNameIsJavaPackage);
+ sdk_library_action.Action(RequiredAndroidAttribute("versionMajor"));
+
+ xml::XmlNodeAction& uses_sdk_library_action = application_action["uses-sdk-library"];
+ uses_sdk_library_action.Action(RequiredNameIsJavaPackage);
+ uses_sdk_library_action.Action(RequiredAndroidAttribute("versionMajor"));
+ uses_sdk_library_action.Action(RequiredAndroidAttribute("certDigest"));
+ uses_sdk_library_action["additional-certificate"];
+
xml::XmlNodeAction& uses_package_action = application_action["uses-package"];
uses_package_action.Action(RequiredNameIsJavaPackage);
uses_package_action["additional-certificate"];