Merge "Add support for brightness as a float"
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index 8cea645..821305e 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -142,6 +142,9 @@
/** @hide */
public static final int COMMIT_RESULT_ERROR = 1;
+ /** @hide */
+ public static final int INVALID_RES_ID = -1;
+
private final Context mContext;
private final IBlobStoreManager mService;
@@ -285,11 +288,65 @@
* caller is trying to acquire too many leases.
*
* @see {@link #acquireLease(BlobHandle, int)}
+ * @see {@link #acquireLease(BlobHandle, CharSequence)}
*/
public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId,
@CurrentTimeMillisLong long leaseExpiryTimeMillis) throws IOException {
try {
- mService.acquireLease(blobHandle, descriptionResId, leaseExpiryTimeMillis,
+ mService.acquireLease(blobHandle, descriptionResId, null, leaseExpiryTimeMillis,
+ mContext.getOpPackageName());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Acquire a lease to the blob represented by {@code blobHandle}. This lease indicates to the
+ * system that the caller wants the blob to be kept around.
+ *
+ * <p> This is variant of {@link #acquireLease(BlobHandle, int, long)} taking a
+ * {@link CharSequence} for {@code description}. It is highly recommended that callers only
+ * use this when a valid resource ID for {@code description} could not be provided. Otherwise,
+ * apps should prefer using {@link #acquireLease(BlobHandle, int)} which will allow
+ * {@code description} to be localized.
+ *
+ * <p> Any active leases will be automatically released when the blob's expiry time
+ * ({@link BlobHandle#getExpiryTimeMillis()}) is elapsed.
+ *
+ * <p> This lease information is persisted and calling this more than once will result in
+ * latest lease overriding any previous lease.
+ *
+ * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
+ * acquire a lease for.
+ * @param description a short description string that can be surfaced
+ * to the user explaining what the blob is used for.
+ * @param leaseExpiryTimeMillis the time in milliseconds after which the lease can be
+ * automatically released, in {@link System#currentTimeMillis()}
+ * timebase. If its value is {@code 0}, then the behavior of this
+ * API is identical to {@link #acquireLease(BlobHandle, int)}
+ * where clients have to explicitly call
+ * {@link #releaseLease(BlobHandle)} when they don't
+ * need the blob anymore.
+ *
+ * @throws IOException when there is an I/O error while acquiring a lease to the blob.
+ * @throws SecurityException when the blob represented by the {@code blobHandle} does not
+ * exist or the caller does not have access to it.
+ * @throws IllegalArgumentException when {@code blobHandle} is invalid or
+ * if the {@code leaseExpiryTimeMillis} is greater than the
+ * {@link BlobHandle#getExpiryTimeMillis()}.
+ * @throws IllegalStateException when a lease could not be acquired, such as when the
+ * caller is trying to acquire too many leases.
+ *
+ * @see {@link #acquireLease(BlobHandle, int, long)}
+ * @see {@link #acquireLease(BlobHandle, CharSequence)}
+ */
+ public void acquireLease(@NonNull BlobHandle blobHandle, @NonNull CharSequence description,
+ @CurrentTimeMillisLong long leaseExpiryTimeMillis) throws IOException {
+ try {
+ mService.acquireLease(blobHandle, INVALID_RES_ID, description, leaseExpiryTimeMillis,
mContext.getOpPackageName());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
@@ -327,6 +384,7 @@
* caller is trying to acquire too many leases.
*
* @see {@link #acquireLease(BlobHandle, int, long)}
+ * @see {@link #acquireLease(BlobHandle, CharSequence, long)}
*/
public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId)
throws IOException {
@@ -334,6 +392,47 @@
}
/**
+ * Acquire a lease to the blob represented by {@code blobHandle}. This lease indicates to the
+ * system that the caller wants the blob to be kept around.
+ *
+ * <p> This is variant of {@link #acquireLease(BlobHandle, int)} taking a {@link CharSequence}
+ * for {@code description}. It is highly recommended that callers only use this when a valid
+ * resource ID for {@code description} could not be provided. Otherwise, apps should prefer
+ * using {@link #acquireLease(BlobHandle, int)} which will allow {@code description} to be
+ * localized.
+ *
+ * <p> This is similar to {@link #acquireLease(BlobHandle, CharSequence, long)} except clients
+ * don't have to specify the lease expiry time upfront using this API and need to explicitly
+ * release the lease using {@link #releaseLease(BlobHandle)} when they no longer like to keep
+ * a blob around.
+ *
+ * <p> Any active leases will be automatically released when the blob's expiry time
+ * ({@link BlobHandle#getExpiryTimeMillis()}) is elapsed.
+ *
+ * <p> This lease information is persisted and calling this more than once will result in
+ * latest lease overriding any previous lease.
+ *
+ * @param blobHandle the {@link BlobHandle} representing the blob that the caller wants to
+ * acquire a lease for.
+ * @param description a short description string that can be surfaced
+ * to the user explaining what the blob is used for.
+ *
+ * @throws IOException when there is an I/O error while acquiring a lease to the blob.
+ * @throws SecurityException when the blob represented by the {@code blobHandle} does not
+ * exist or the caller does not have access to it.
+ * @throws IllegalArgumentException when {@code blobHandle} is invalid.
+ * @throws IllegalStateException when a lease could not be acquired, such as when the
+ * caller is trying to acquire too many leases.
+ *
+ * @see {@link #acquireLease(BlobHandle, int)}
+ * @see {@link #acquireLease(BlobHandle, CharSequence, long)}
+ */
+ public void acquireLease(@NonNull BlobHandle blobHandle, @NonNull CharSequence description)
+ throws IOException {
+ acquireLease(blobHandle, description, 0);
+ }
+
+ /**
* Release all active leases to the blob represented by {@code blobHandle} which are
* currently held by the caller.
*
diff --git a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
index e2128b4..a85a25c 100644
--- a/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
+++ b/apex/blobstore/framework/java/android/app/blob/IBlobStoreManager.aidl
@@ -26,8 +26,8 @@
ParcelFileDescriptor openBlob(in BlobHandle handle, in String packageName);
void deleteSession(long sessionId, in String packageName);
- void acquireLease(in BlobHandle handle, int descriptionResId, long leaseTimeout,
- in String packageName);
+ void acquireLease(in BlobHandle handle, int descriptionResId, in CharSequence description,
+ long leaseTimeoutMillis, in String packageName);
void releaseLease(in BlobHandle handle, in String packageName);
void waitForIdle(in RemoteCallback callback);
diff --git a/apex/blobstore/framework/java/android/app/blob/XmlTags.java b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
index 803c9a4..9834d74 100644
--- a/apex/blobstore/framework/java/android/app/blob/XmlTags.java
+++ b/apex/blobstore/framework/java/android/app/blob/XmlTags.java
@@ -52,4 +52,5 @@
// For leasee
public static final String TAG_LEASEE = "l";
public static final String ATTR_DESCRIPTION_RES_ID = "rid";
+ public static final String ATTR_DESCRIPTION = "d";
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index c12e0ec..c7d803c 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -15,6 +15,7 @@
*/
package com.android.server.blob;
+import static android.app.blob.XmlTags.ATTR_DESCRIPTION;
import static android.app.blob.XmlTags.ATTR_DESCRIPTION_RES_ID;
import static android.app.blob.XmlTags.ATTR_EXPIRY_TIME;
import static android.app.blob.XmlTags.ATTR_ID;
@@ -28,12 +29,14 @@
import static android.system.OsConstants.O_RDONLY;
import static com.android.server.blob.BlobStoreConfig.TAG;
+import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_STRING_DESC;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.blob.BlobHandle;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.res.ResourceId;
import android.content.res.Resources;
import android.os.ParcelFileDescriptor;
import android.os.RevocableFileDescriptor;
@@ -141,11 +144,11 @@
}
}
- void addLeasee(String callingPackage, int callingUid,
- int descriptionResId, long leaseExpiryTimeMillis) {
+ void addLeasee(String callingPackage, int callingUid, int descriptionResId,
+ CharSequence description, long leaseExpiryTimeMillis) {
synchronized (mMetadataLock) {
mLeasees.add(new Leasee(callingPackage, callingUid,
- descriptionResId, leaseExpiryTimeMillis));
+ descriptionResId, description, leaseExpiryTimeMillis));
}
}
@@ -308,7 +311,7 @@
}
@Nullable
- static BlobMetadata createFromXml(Context context, XmlPullParser in)
+ static BlobMetadata createFromXml(XmlPullParser in, int version, Context context)
throws XmlPullParserException, IOException {
final long blobId = XmlUtils.readLongAttribute(in, ATTR_ID);
final int userId = XmlUtils.readIntAttribute(in, ATTR_USER_ID);
@@ -321,12 +324,12 @@
if (TAG_BLOB_HANDLE.equals(in.getName())) {
blobHandle = BlobHandle.createFromXml(in);
} else if (TAG_COMMITTER.equals(in.getName())) {
- final Committer committer = Committer.createFromXml(in);
+ final Committer committer = Committer.createFromXml(in, version);
if (committer != null) {
committers.add(committer);
}
} else if (TAG_LEASEE.equals(in.getName())) {
- leasees.add(Leasee.createFromXml(in));
+ leasees.add(Leasee.createFromXml(in, version));
}
}
@@ -366,7 +369,7 @@
}
@Nullable
- static Committer createFromXml(@NonNull XmlPullParser in)
+ static Committer createFromXml(@NonNull XmlPullParser in, int version)
throws XmlPullParserException, IOException {
final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
final int uid = XmlUtils.readIntAttribute(in, ATTR_UID);
@@ -388,12 +391,15 @@
static final class Leasee extends Accessor {
public final int descriptionResId;
+ public final CharSequence description;
public final long expiryTimeMillis;
- Leasee(String packageName, int uid, int descriptionResId, long expiryTimeMillis) {
+ Leasee(String packageName, int uid, int descriptionResId, CharSequence description,
+ long expiryTimeMillis) {
super(packageName, uid);
this.descriptionResId = descriptionResId;
this.expiryTimeMillis = expiryTimeMillis;
+ this.description = description;
}
boolean isStillValid() {
@@ -401,35 +407,51 @@
}
void dump(Context context, IndentingPrintWriter fout) {
- String desc = null;
- try {
- final Resources leaseeRes = context.getPackageManager()
- .getResourcesForApplicationAsUser(packageName, UserHandle.getUserId(uid));
- desc = leaseeRes.getString(descriptionResId);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.d(TAG, "Unknown package in user " + UserHandle.getUserId(uid) + ": "
- + packageName, e);
- desc = "<none>";
- }
- fout.println("desc: " + desc);
+ fout.println("desc: " + getDescriptionToDump(context));
fout.println("expiryMs: " + expiryTimeMillis);
}
+ private String getDescriptionToDump(Context context) {
+ String desc = null;
+ if (ResourceId.isValid(descriptionResId)) {
+ try {
+ final Resources leaseeRes = context.getPackageManager()
+ .getResourcesForApplicationAsUser(
+ packageName, UserHandle.getUserId(uid));
+ desc = leaseeRes.getString(descriptionResId);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.d(TAG, "Unknown package in user " + UserHandle.getUserId(uid) + ": "
+ + packageName, e);
+ desc = "<none>";
+ }
+ } else {
+ desc = description.toString();
+ }
+ return desc;
+ }
+
void writeToXml(@NonNull XmlSerializer out) throws IOException {
XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, packageName);
XmlUtils.writeIntAttribute(out, ATTR_UID, uid);
XmlUtils.writeIntAttribute(out, ATTR_DESCRIPTION_RES_ID, descriptionResId);
XmlUtils.writeLongAttribute(out, ATTR_EXPIRY_TIME, expiryTimeMillis);
+ XmlUtils.writeStringAttribute(out, ATTR_DESCRIPTION, description);
}
@NonNull
- static Leasee createFromXml(@NonNull XmlPullParser in) throws IOException {
+ static Leasee createFromXml(@NonNull XmlPullParser in, int version) throws IOException {
final String packageName = XmlUtils.readStringAttribute(in, ATTR_PACKAGE);
final int uid = XmlUtils.readIntAttribute(in, ATTR_UID);
final int descriptionResId = XmlUtils.readIntAttribute(in, ATTR_DESCRIPTION_RES_ID);
final long expiryTimeMillis = XmlUtils.readLongAttribute(in, ATTR_EXPIRY_TIME);
+ final CharSequence description;
+ if (version >= XML_VERSION_ADD_STRING_DESC) {
+ description = XmlUtils.readStringAttribute(in, ATTR_DESCRIPTION);
+ } else {
+ description = null;
+ }
- return new Leasee(packageName, uid, descriptionResId, expiryTimeMillis);
+ return new Leasee(packageName, uid, descriptionResId, description, expiryTimeMillis);
}
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
index ba2e559..bcc1610 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
@@ -28,7 +28,12 @@
public static final String TAG = "BlobStore";
public static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE);
- public static final int CURRENT_XML_VERSION = 1;
+ // Initial version.
+ public static final int XML_VERSION_INIT = 1;
+ // Added a string variant of lease description.
+ public static final int XML_VERSION_ADD_STRING_DESC = 2;
+
+ public static final int XML_VERSION_CURRENT = XML_VERSION_ADD_STRING_DESC;
private static final String ROOT_DIR_NAME = "blobstore";
private static final String BLOBS_DIR_NAME = "blobs";
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index 0ba34ca..1efdbda 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -27,10 +27,10 @@
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.os.UserHandle.USER_NULL;
-import static com.android.server.blob.BlobStoreConfig.CURRENT_XML_VERSION;
import static com.android.server.blob.BlobStoreConfig.LOGV;
import static com.android.server.blob.BlobStoreConfig.SESSION_EXPIRY_TIMEOUT_MILLIS;
import static com.android.server.blob.BlobStoreConfig.TAG;
+import static com.android.server.blob.BlobStoreConfig.XML_VERSION_CURRENT;
import static com.android.server.blob.BlobStoreSession.STATE_ABANDONED;
import static com.android.server.blob.BlobStoreSession.STATE_COMMITTED;
import static com.android.server.blob.BlobStoreSession.STATE_VERIFIED_INVALID;
@@ -324,7 +324,8 @@
}
private void acquireLeaseInternal(BlobHandle blobHandle, int descriptionResId,
- long leaseExpiryTimeMillis, int callingUid, String callingPackage) {
+ CharSequence description, long leaseExpiryTimeMillis,
+ int callingUid, String callingPackage) {
synchronized (mBlobsLock) {
final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
.get(blobHandle);
@@ -338,7 +339,7 @@
"Lease expiry cannot be later than blobs expiry time");
}
blobMetadata.addLeasee(callingPackage, callingUid,
- descriptionResId, leaseExpiryTimeMillis);
+ descriptionResId, description, leaseExpiryTimeMillis);
if (LOGV) {
Slog.v(TAG, "Acquired lease on " + blobHandle
+ "; callingUid=" + callingUid + ", callingPackage=" + callingPackage);
@@ -450,7 +451,7 @@
out.setOutput(fos, StandardCharsets.UTF_8.name());
out.startDocument(null, true);
out.startTag(null, TAG_SESSIONS);
- XmlUtils.writeIntAttribute(out, ATTR_VERSION, CURRENT_XML_VERSION);
+ XmlUtils.writeIntAttribute(out, ATTR_VERSION, XML_VERSION_CURRENT);
for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
final LongSparseArray<BlobStoreSession> userSessions =
@@ -491,6 +492,7 @@
final XmlPullParser in = Xml.newPullParser();
in.setInput(fis, StandardCharsets.UTF_8.name());
XmlUtils.beginDocument(in, TAG_SESSIONS);
+ final int version = XmlUtils.readIntAttribute(in, ATTR_VERSION);
while (true) {
XmlUtils.nextElement(in);
if (in.getEventType() == XmlPullParser.END_DOCUMENT) {
@@ -499,7 +501,7 @@
if (TAG_SESSION.equals(in.getName())) {
final BlobStoreSession session = BlobStoreSession.createFromXml(
- in, mContext, mSessionStateChangeListener);
+ in, version, mContext, mSessionStateChangeListener);
if (session == null) {
continue;
}
@@ -539,7 +541,7 @@
out.setOutput(fos, StandardCharsets.UTF_8.name());
out.startDocument(null, true);
out.startTag(null, TAG_BLOBS);
- XmlUtils.writeIntAttribute(out, ATTR_VERSION, CURRENT_XML_VERSION);
+ XmlUtils.writeIntAttribute(out, ATTR_VERSION, XML_VERSION_CURRENT);
for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
@@ -579,6 +581,7 @@
final XmlPullParser in = Xml.newPullParser();
in.setInput(fis, StandardCharsets.UTF_8.name());
XmlUtils.beginDocument(in, TAG_BLOBS);
+ final int version = XmlUtils.readIntAttribute(in, ATTR_VERSION);
while (true) {
XmlUtils.nextElement(in);
if (in.getEventType() == XmlPullParser.END_DOCUMENT) {
@@ -586,7 +589,8 @@
}
if (TAG_BLOB.equals(in.getName())) {
- final BlobMetadata blobMetadata = BlobMetadata.createFromXml(mContext, in);
+ final BlobMetadata blobMetadata = BlobMetadata.createFromXml(
+ in, version, mContext);
final SparseArray<String> userPackages = allPackages.get(
blobMetadata.getUserId());
if (userPackages == null) {
@@ -1032,11 +1036,14 @@
@Override
public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId,
+ @Nullable CharSequence description,
@CurrentTimeSecondsLong long leaseExpiryTimeMillis, @NonNull String packageName) {
Objects.requireNonNull(blobHandle, "blobHandle must not be null");
blobHandle.assertIsValid();
- Preconditions.checkArgument(ResourceId.isValid(descriptionResId),
- "descriptionResId is not valid");
+ Preconditions.checkArgument(
+ ResourceId.isValid(descriptionResId) || description != null,
+ "Description must be valid; descriptionId=" + descriptionResId
+ + ", description=" + description);
Preconditions.checkArgumentNonnegative(leaseExpiryTimeMillis,
"leaseExpiryTimeMillis must not be negative");
Objects.requireNonNull(packageName, "packageName must not be null");
@@ -1044,7 +1051,7 @@
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
- acquireLeaseInternal(blobHandle, descriptionResId, leaseExpiryTimeMillis,
+ acquireLeaseInternal(blobHandle, descriptionResId, description, leaseExpiryTimeMillis,
callingUid, packageName);
}
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index bd35b86..80b4235 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -511,7 +511,7 @@
}
@Nullable
- static BlobStoreSession createFromXml(@NonNull XmlPullParser in,
+ static BlobStoreSession createFromXml(@NonNull XmlPullParser in, int version,
@NonNull Context context, @NonNull SessionStateChangeListener stateChangeListener)
throws IOException, XmlPullParserException {
final int sessionId = XmlUtils.readIntAttribute(in, ATTR_ID);
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index b1b8fba..6e05bc0 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -136,25 +136,59 @@
private static final long ONE_HOUR = ONE_MINUTE * 60;
private static final long ONE_DAY = ONE_HOUR * 24;
- static final long[] SCREEN_TIME_THRESHOLDS = {
+ /**
+ * The minimum amount of time the screen must have been on before an app can time out from its
+ * current bucket to the next bucket.
+ */
+ private static final long[] SCREEN_TIME_THRESHOLDS = {
0,
0,
- COMPRESS_TIME ? 120 * 1000 : 1 * ONE_HOUR,
- COMPRESS_TIME ? 240 * 1000 : 2 * ONE_HOUR
+ COMPRESS_TIME ? 2 * ONE_MINUTE : 1 * ONE_HOUR,
+ COMPRESS_TIME ? 4 * ONE_MINUTE : 2 * ONE_HOUR,
+ COMPRESS_TIME ? 8 * ONE_MINUTE : 6 * ONE_HOUR
};
- static final long[] ELAPSED_TIME_THRESHOLDS = {
+ /** The minimum allowed values for each index in {@link #SCREEN_TIME_THRESHOLDS}. */
+ private static final long[] MINIMUM_SCREEN_TIME_THRESHOLDS = COMPRESS_TIME
+ ? new long[SCREEN_TIME_THRESHOLDS.length]
+ : new long[]{
+ 0,
+ 0,
+ 0,
+ 30 * ONE_MINUTE,
+ ONE_HOUR
+ };
+
+ /**
+ * The minimum amount of elapsed time that must have passed before an app can time out from its
+ * current bucket to the next bucket.
+ */
+ private static final long[] ELAPSED_TIME_THRESHOLDS = {
0,
COMPRESS_TIME ? 1 * ONE_MINUTE : 12 * ONE_HOUR,
COMPRESS_TIME ? 4 * ONE_MINUTE : 24 * ONE_HOUR,
- COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR
+ COMPRESS_TIME ? 16 * ONE_MINUTE : 48 * ONE_HOUR,
+ // TODO(149050681): increase timeout to 30+ days
+ COMPRESS_TIME ? 32 * ONE_MINUTE : 4 * ONE_DAY
};
- static final int[] THRESHOLD_BUCKETS = {
+ /** The minimum allowed values for each index in {@link #ELAPSED_TIME_THRESHOLDS}. */
+ private static final long[] MINIMUM_ELAPSED_TIME_THRESHOLDS = COMPRESS_TIME
+ ? new long[ELAPSED_TIME_THRESHOLDS.length]
+ : new long[]{
+ 0,
+ ONE_HOUR,
+ ONE_HOUR,
+ 2 * ONE_HOUR,
+ 4 * ONE_DAY
+ };
+
+ private static final int[] THRESHOLD_BUCKETS = {
STANDBY_BUCKET_ACTIVE,
STANDBY_BUCKET_WORKING_SET,
STANDBY_BUCKET_FREQUENT,
- STANDBY_BUCKET_RARE
+ STANDBY_BUCKET_RARE,
+ STANDBY_BUCKET_RESTRICTED
};
/** Default expiration time for bucket prediction. After this, use thresholds to downgrade. */
@@ -204,7 +238,15 @@
static final int MSG_REPORT_EXEMPTED_SYNC_START = 13;
long mCheckIdleIntervalMillis;
+ /**
+ * The minimum amount of time the screen must have been on before an app can time out from its
+ * current bucket to the next bucket.
+ */
long[] mAppStandbyScreenThresholds = SCREEN_TIME_THRESHOLDS;
+ /**
+ * The minimum amount of elapsed time that must have passed before an app can time out from its
+ * current bucket to the next bucket.
+ */
long[] mAppStandbyElapsedThresholds = ELAPSED_TIME_THRESHOLDS;
/** Minimum time a strong usage event should keep the bucket elevated. */
long mStrongUsageTimeoutMillis;
@@ -1147,9 +1189,11 @@
final boolean isForcedByUser =
(reason & REASON_MAIN_MASK) == REASON_MAIN_FORCED_BY_USER;
- // If the current bucket is RESTRICTED, only user force or usage should bring it out.
+ // If the current bucket is RESTRICTED, only user force or usage should bring it out,
+ // unless the app was put into the bucket due to timing out.
if (app.currentBucket == STANDBY_BUCKET_RESTRICTED && !isUserUsage(reason)
- && !isForcedByUser) {
+ && !isForcedByUser
+ && (app.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_TIMEOUT) {
return;
}
@@ -1829,12 +1873,12 @@
String screenThresholdsValue = mParser.getString(KEY_SCREEN_TIME_THRESHOLDS, null);
mAppStandbyScreenThresholds = parseLongArray(screenThresholdsValue,
- SCREEN_TIME_THRESHOLDS);
+ SCREEN_TIME_THRESHOLDS, MINIMUM_SCREEN_TIME_THRESHOLDS);
String elapsedThresholdsValue = mParser.getString(KEY_ELAPSED_TIME_THRESHOLDS,
null);
mAppStandbyElapsedThresholds = parseLongArray(elapsedThresholdsValue,
- ELAPSED_TIME_THRESHOLDS);
+ ELAPSED_TIME_THRESHOLDS, MINIMUM_ELAPSED_TIME_THRESHOLDS);
mCheckIdleIntervalMillis = Math.min(mAppStandbyElapsedThresholds[1] / 4,
COMPRESS_TIME ? ONE_MINUTE : 4 * 60 * ONE_MINUTE); // 4 hours
mStrongUsageTimeoutMillis = mParser.getDurationMillis(
@@ -1870,8 +1914,8 @@
mUnexemptedSyncScheduledTimeoutMillis = mParser.getDurationMillis(
KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION,
- COMPRESS_TIME ? ONE_MINUTE
- : DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT); // TODO
+ COMPRESS_TIME
+ ? ONE_MINUTE : DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT);
mSystemInteractionTimeoutMillis = mParser.getDurationMillis(
KEY_SYSTEM_INTERACTION_HOLD_DURATION,
@@ -1888,7 +1932,7 @@
setAppIdleEnabled(mInjector.isAppIdleEnabled());
}
- long[] parseLongArray(String values, long[] defaults) {
+ long[] parseLongArray(String values, long[] defaults, long[] minValues) {
if (values == null) return defaults;
if (values.isEmpty()) {
// Reset to defaults
@@ -1896,13 +1940,19 @@
} else {
String[] thresholds = values.split("/");
if (thresholds.length == THRESHOLD_BUCKETS.length) {
+ if (minValues.length != THRESHOLD_BUCKETS.length) {
+ Slog.wtf(TAG, "minValues array is the wrong size");
+ // Use zeroes as the minimums.
+ minValues = new long[THRESHOLD_BUCKETS.length];
+ }
long[] array = new long[THRESHOLD_BUCKETS.length];
for (int i = 0; i < THRESHOLD_BUCKETS.length; i++) {
try {
if (thresholds[i].startsWith("P") || thresholds[i].startsWith("p")) {
- array[i] = Duration.parse(thresholds[i]).toMillis();
+ array[i] = Math.max(minValues[i],
+ Duration.parse(thresholds[i]).toMillis());
} else {
- array[i] = Long.parseLong(thresholds[i]);
+ array[i] = Math.max(minValues[i], Long.parseLong(thresholds[i]));
}
} catch (NumberFormatException|DateTimeParseException e) {
return defaults;
diff --git a/api/current.txt b/api/current.txt
index a7367e8..475506a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7574,7 +7574,9 @@
public class BlobStoreManager {
method public void acquireLease(@NonNull android.app.blob.BlobHandle, @IdRes int, long) throws java.io.IOException;
+ method public void acquireLease(@NonNull android.app.blob.BlobHandle, @NonNull CharSequence, long) throws java.io.IOException;
method public void acquireLease(@NonNull android.app.blob.BlobHandle, @IdRes int) throws java.io.IOException;
+ method public void acquireLease(@NonNull android.app.blob.BlobHandle, @NonNull CharSequence) throws java.io.IOException;
method @IntRange(from=1) public long createSession(@NonNull android.app.blob.BlobHandle) throws java.io.IOException;
method public void deleteSession(@IntRange(from=1) long) throws java.io.IOException;
method @NonNull public android.os.ParcelFileDescriptor openBlob(@NonNull android.app.blob.BlobHandle) throws java.io.IOException;
@@ -17033,6 +17035,7 @@
field public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1; // 0x1
field public static final int BIOMETRIC_ERROR_NONE_ENROLLED = 11; // 0xb
field public static final int BIOMETRIC_ERROR_NO_HARDWARE = 12; // 0xc
+ field public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; // 0xf
field public static final int BIOMETRIC_SUCCESS = 0; // 0x0
}
@@ -17067,6 +17070,7 @@
field public static final int BIOMETRIC_ERROR_NO_BIOMETRICS = 11; // 0xb
field public static final int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14; // 0xe
field public static final int BIOMETRIC_ERROR_NO_SPACE = 4; // 0x4
+ field public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15; // 0xf
field public static final int BIOMETRIC_ERROR_TIMEOUT = 3; // 0x3
field public static final int BIOMETRIC_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
field public static final int BIOMETRIC_ERROR_USER_CANCELED = 10; // 0xa
@@ -31281,6 +31285,7 @@
method public int getWifiState();
method public boolean is5GHzBandSupported();
method public boolean is6GHzBandSupported();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isAutoWakeupEnabled();
method @Deprecated public boolean isDeviceToApRttSupported();
method public boolean isEasyConnectSupported();
method public boolean isEnhancedOpenSupported();
@@ -42600,6 +42605,7 @@
method @NonNull public String getKeystoreAlias();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
+ method public int getUserAuthenticationType();
method public int getUserAuthenticationValidityDurationSeconds();
method @NonNull public boolean isDigestsSpecified();
method public boolean isInvalidatedByBiometricEnrollment();
@@ -42634,9 +42640,10 @@
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
+ method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationParameters(@IntRange(from=0xffffffff) int, int);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean);
- method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(@IntRange(from=0xffffffff) int);
+ method @Deprecated @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(@IntRange(from=0xffffffff) int);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserConfirmationRequired(boolean);
method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUserPresenceRequired(boolean);
}
@@ -42653,6 +42660,7 @@
method public int getOrigin();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
+ method public int getUserAuthenticationType();
method public int getUserAuthenticationValidityDurationSeconds();
method public boolean isInsideSecureHardware();
method public boolean isInvalidatedByBiometricEnrollment();
@@ -42676,6 +42684,8 @@
}
public abstract class KeyProperties {
+ field public static final int AUTH_BIOMETRIC_STRONG = 2; // 0x2
+ field public static final int AUTH_DEVICE_CREDENTIAL = 1; // 0x1
field public static final String BLOCK_MODE_CBC = "CBC";
field public static final String BLOCK_MODE_CTR = "CTR";
field public static final String BLOCK_MODE_ECB = "ECB";
@@ -42722,6 +42732,7 @@
method @Nullable public java.util.Date getKeyValidityStart();
method public int getPurposes();
method @NonNull public String[] getSignaturePaddings();
+ method public int getUserAuthenticationType();
method public int getUserAuthenticationValidityDurationSeconds();
method public boolean isDigestsSpecified();
method public boolean isInvalidatedByBiometricEnrollment();
@@ -42747,9 +42758,10 @@
method @NonNull public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
method @NonNull public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
method @NonNull public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
+ method @NonNull public android.security.keystore.KeyProtection.Builder setUserAuthenticationParameters(@IntRange(from=0xffffffff) int, int);
method @NonNull public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean);
method @NonNull public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean);
- method @NonNull public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(@IntRange(from=0xffffffff) int);
+ method @Deprecated @NonNull public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(@IntRange(from=0xffffffff) int);
method @NonNull public android.security.keystore.KeyProtection.Builder setUserConfirmationRequired(boolean);
method @NonNull public android.security.keystore.KeyProtection.Builder setUserPresenceRequired(boolean);
}
@@ -45427,7 +45439,6 @@
field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
- field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 8192; // 0x2000
field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 2048; // 0x800
field public static final int PROPERTY_RTT = 1024; // 0x400
@@ -45505,7 +45516,6 @@
public abstract class Conference extends android.telecom.Conferenceable {
ctor public Conference(android.telecom.PhoneAccountHandle);
method public final boolean addConnection(android.telecom.Connection);
- method @NonNull public static android.telecom.Conference createFailedConference(@NonNull android.telecom.DisconnectCause, @NonNull android.telecom.PhoneAccountHandle);
method public final void destroy();
method public final android.telecom.CallAudioState getCallAudioState();
method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
@@ -45520,8 +45530,6 @@
method public final android.telecom.StatusHints getStatusHints();
method public android.telecom.Connection.VideoProvider getVideoProvider();
method public int getVideoState();
- method public final boolean isRingbackRequested();
- method public void onAnswer(int);
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onConnectionAdded(android.telecom.Connection);
method public void onDisconnect();
@@ -45530,7 +45538,6 @@
method public void onMerge(android.telecom.Connection);
method public void onMerge();
method public void onPlayDtmfTone(char);
- method public void onReject();
method public void onSeparate(android.telecom.Connection);
method public void onStopDtmfTone();
method public void onSwap();
@@ -45550,8 +45557,6 @@
method public final void setDisconnected(android.telecom.DisconnectCause);
method public final void setExtras(@Nullable android.os.Bundle);
method public final void setOnHold();
- method public final void setRingbackRequested(boolean);
- method public final void setRinging();
method public final void setStatusHints(android.telecom.StatusHints);
method public final void setVideoProvider(android.telecom.Connection, android.telecom.Connection.VideoProvider);
method public final void setVideoState(android.telecom.Connection, int);
@@ -45710,7 +45715,6 @@
field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
field public static final int PROPERTY_HIGH_DEF_AUDIO = 4; // 0x4
- field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 4096; // 0x1000
field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
field public static final int PROPERTY_IS_RTT = 256; // 0x100
field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 1024; // 0x400
@@ -46126,7 +46130,6 @@
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall(int);
method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
- method public void addNewIncomingConference(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void cancelMissedCallsNotification();
method public android.content.Intent createManageBlockedNumbersIntent();
method @Deprecated @RequiresPermission(android.Manifest.permission.ANSWER_PHONE_CALLS) public boolean endCall();
@@ -46154,7 +46157,6 @@
method public void registerPhoneAccount(android.telecom.PhoneAccount);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void showInCallScreen(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void silenceRinger();
- method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void startConference(@NonNull java.util.List<android.net.Uri>, @NonNull android.os.Bundle);
method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
field public static final String ACTION_CHANGE_DEFAULT_DIALER = "android.telecom.action.CHANGE_DEFAULT_DIALER";
field public static final String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
@@ -46681,7 +46683,6 @@
field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
- field public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL = "support_adhoc_conference_calls_bool";
field public static final String KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL = "support_clir_network_default_bool";
field public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
field public static final String KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool";
diff --git a/api/system-current.txt b/api/system-current.txt
index 3a7bd00..4a3474d 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1451,16 +1451,16 @@
package android.bluetooth {
public final class BluetoothA2dp implements android.bluetooth.BluetoothProfile {
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void disableOptionalCodecs(@Nullable android.bluetooth.BluetoothDevice);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void enableOptionalCodecs(@Nullable android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void disableOptionalCodecs(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void enableOptionalCodecs(@NonNull android.bluetooth.BluetoothDevice);
method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothDevice getActiveDevice();
- method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothCodecStatus getCodecStatus(@Nullable android.bluetooth.BluetoothDevice);
+ method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothCodecStatus getCodecStatus(@NonNull android.bluetooth.BluetoothDevice);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public int getOptionalCodecsEnabled(@Nullable android.bluetooth.BluetoothDevice);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setCodecConfigPreference(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothCodecConfig);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public int isOptionalCodecsEnabled(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public int isOptionalCodecsSupported(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setCodecConfigPreference(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothCodecConfig);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setOptionalCodecsEnabled(@Nullable android.bluetooth.BluetoothDevice, int);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public int supportsOptionalCodecs(@Nullable android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void setOptionalCodecsEnabled(@NonNull android.bluetooth.BluetoothDevice, int);
field public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0; // 0x0
field public static final int OPTIONAL_CODECS_PREF_DISABLED = 0; // 0x0
field public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; // 0x1
@@ -1471,10 +1471,10 @@
public final class BluetoothA2dpSink implements android.bluetooth.BluetoothProfile {
method public void finalize();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isAudioPlaying(@Nullable android.bluetooth.BluetoothDevice);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
- field @RequiresPermission(android.Manifest.permission.BLUETOOTH) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isAudioPlaying(@Nullable android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
+ field @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
}
public final class BluetoothAdapter {
@@ -1922,18 +1922,22 @@
public abstract class IntegrityFormula {
method @NonNull public static android.content.integrity.IntegrityFormula all(@NonNull android.content.integrity.IntegrityFormula...);
method @NonNull public static android.content.integrity.IntegrityFormula any(@NonNull android.content.integrity.IntegrityFormula...);
- method @NonNull public android.content.integrity.IntegrityFormula equalTo(@NonNull String);
- method @NonNull public android.content.integrity.IntegrityFormula equalTo(boolean);
- method @NonNull public android.content.integrity.IntegrityFormula equalTo(long);
- method @NonNull public android.content.integrity.IntegrityFormula greaterThan(long);
- method @NonNull public android.content.integrity.IntegrityFormula greaterThanOrEquals(long);
method @NonNull public static android.content.integrity.IntegrityFormula not(@NonNull android.content.integrity.IntegrityFormula);
- field @NonNull public static final android.content.integrity.IntegrityFormula APP_CERTIFICATE;
- field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_CERTIFICATE;
- field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_NAME;
- field @NonNull public static final android.content.integrity.IntegrityFormula PACKAGE_NAME;
- field @NonNull public static final android.content.integrity.IntegrityFormula PRE_INSTALLED;
- field @NonNull public static final android.content.integrity.IntegrityFormula VERSION_CODE;
+ }
+
+ public static final class IntegrityFormula.Application {
+ method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String);
+ method @NonNull public static android.content.integrity.IntegrityFormula isPreInstalled();
+ method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String);
+ method @NonNull public static android.content.integrity.IntegrityFormula versionCodeEquals(@NonNull long);
+ method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThan(@NonNull long);
+ method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long);
+ }
+
+ public static final class IntegrityFormula.Installer {
+ method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String);
+ method @NonNull public static android.content.integrity.IntegrityFormula notAllowedByManifest();
+ method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String);
}
public final class Rule implements android.os.Parcelable {
@@ -7714,6 +7718,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveBackupData();
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public byte[] retrieveSoftApBackupData();
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setAutoWakeupEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE) public void setDeviceMobilityState(int);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMacRandomizationSettingPasspointEnabled(@NonNull String, boolean);
method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void setMeteredOverridePasspoint(@NonNull String, int);
@@ -9693,7 +9698,6 @@
field public static final String EUICC_UNSUPPORTED_COUNTRIES = "euicc_unsupported_countries";
field public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = "install_carrier_app_notification_persistent";
field public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis";
- field public static final String NETWORK_RECOMMENDATIONS_ENABLED = "network_recommendations_enabled";
field public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
field public static final String REQUIRE_PASSWORD_TO_DECRYPT = "require_password_to_decrypt";
field public static final String SOFT_AP_TIMEOUT_ENABLED = "soft_ap_timeout_enabled";
@@ -9708,7 +9712,7 @@
field public static final String WIFI_SCAN_THROTTLE_ENABLED = "wifi_scan_throttle_enabled";
field public static final String WIFI_SCORE_PARAMS = "wifi_score_params";
field public static final String WIFI_VERBOSE_LOGGING_ENABLED = "wifi_verbose_logging_enabled";
- field public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled";
+ field @Deprecated public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled";
}
public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
@@ -13196,19 +13200,7 @@
}
public class ImsRcsManager implements android.telephony.ims.RegistrationManager {
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(int) throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(int, int) throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerRcsAvailabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterRcsAvailabilityCallback(@NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException;
- }
-
- public static class ImsRcsManager.AvailabilityCallback {
- ctor public ImsRcsManager.AvailabilityCallback();
- method public void onAvailabilityChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
+ method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter();
}
public final class ImsReasonInfo implements android.os.Parcelable {
@@ -13587,34 +13579,8 @@
}
public class RcsUceAdapter {
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void requestCapabilities(@NonNull java.util.concurrent.Executor, @NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
- field public static final int ERROR_ALREADY_IN_QUEUE = 13; // 0xd
- field public static final int ERROR_FORBIDDEN = 6; // 0x6
- field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1
- field public static final int ERROR_INSUFFICIENT_MEMORY = 11; // 0xb
- field public static final int ERROR_LOST_NETWORK = 12; // 0xc
- field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5
- field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3
- field public static final int ERROR_NOT_ENABLED = 2; // 0x2
- field public static final int ERROR_NOT_FOUND = 7; // 0x7
- field public static final int ERROR_NOT_REGISTERED = 4; // 0x4
- field public static final int ERROR_REQUEST_TIMEOUT = 10; // 0xa
- field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8
- field public static final int PUBLISH_STATE_200_OK = 1; // 0x1
- field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2
- field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6
- field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4
- field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5
- field public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; // 0x3
- }
-
- public static class RcsUceAdapter.CapabilitiesCallback {
- ctor public RcsUceAdapter.CapabilitiesCallback();
- method public void onCapabilitiesReceived(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>);
- method public void onError(int);
}
}
@@ -13700,23 +13666,8 @@
public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
ctor public RcsFeature();
method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
- method @NonNull public android.telephony.ims.stub.RcsSipOptionsImplBase getOptionsExchangeImpl();
- method @NonNull public android.telephony.ims.stub.RcsPresenceExchangeImplBase getPresenceExchangeImpl();
- method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
method public void onFeatureReady();
method public void onFeatureRemoved();
- method public boolean queryCapabilityConfiguration(int, int);
- method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus();
- }
-
- public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
- ctor public RcsFeature.RcsImsCapabilities(int);
- method public void addCapabilities(int);
- method public boolean isCapable(int);
- method public void removeCapabilities(int);
- field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
- field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
- field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
}
}
@@ -13894,71 +13845,6 @@
field public static final int INVALID_RESULT = -1; // 0xffffffff
}
- public class RcsCapabilityExchange {
- ctor public RcsCapabilityExchange();
- method public final void onCommandUpdate(int, int) throws android.telephony.ims.ImsException;
- field public static final int COMMAND_CODE_FETCH_ERROR = 4; // 0x4
- field public static final int COMMAND_CODE_GENERIC_FAILURE = 2; // 0x2
- field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 6; // 0x6
- field public static final int COMMAND_CODE_INVALID_PARAM = 3; // 0x3
- field public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 7; // 0x7
- field public static final int COMMAND_CODE_NOT_FOUND = 9; // 0x9
- field public static final int COMMAND_CODE_NOT_SUPPORTED = 8; // 0x8
- field public static final int COMMAND_CODE_NO_CHANGE_IN_CAP = 11; // 0xb
- field public static final int COMMAND_CODE_REQUEST_TIMEOUT = 5; // 0x5
- field public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 10; // 0xa
- field public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; // 0x0
- field public static final int COMMAND_CODE_SUCCESS = 1; // 0x1
- }
-
- public class RcsPresenceExchangeImplBase extends android.telephony.ims.stub.RcsCapabilityExchange {
- ctor public RcsPresenceExchangeImplBase();
- method public final void onCapabilityRequestResponse(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>, int) throws android.telephony.ims.ImsException;
- method public final void onNetworkResponse(int, @NonNull String, int) throws android.telephony.ims.ImsException;
- method public final void onNotifyUpdateCapabilites(int) throws android.telephony.ims.ImsException;
- method public final void onUnpublish() throws android.telephony.ims.ImsException;
- method public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, int);
- method public void updateCapabilities(@NonNull android.telephony.ims.RcsContactUceCapability, int);
- field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 0; // 0x0
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 6; // 0x6
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 5; // 0x5
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 3; // 0x3
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 4; // 0x4
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 8; // 0x8
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1; // 0x1
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2; // 0x2
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; // 0xa
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 7; // 0x7
- field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 9; // 0x9
- field public static final int RESPONSE_FORBIDDEN = 3; // 0x3
- field public static final int RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE = 2; // 0x2
- field public static final int RESPONSE_NOT_FOUND = 4; // 0x4
- field public static final int RESPONSE_NOT_REGISTERED = 1; // 0x1
- field public static final int RESPONSE_SIP_INTERVAL_TOO_SHORT = 7; // 0x7
- field public static final int RESPONSE_SIP_REQUEST_TIMEOUT = 5; // 0x5
- field public static final int RESPONSE_SIP_SERVICE_UNAVAILABLE = 8; // 0x8
- field public static final int RESPONSE_SUBSCRIBE_GENERIC_FAILURE = -1; // 0xffffffff
- field public static final int RESPONSE_SUBSCRIBE_TOO_LARGE = 6; // 0x6
- field public static final int RESPONSE_SUCCESS = 0; // 0x0
- }
-
- public class RcsSipOptionsImplBase extends android.telephony.ims.stub.RcsCapabilityExchange {
- ctor public RcsSipOptionsImplBase();
- method public final void onCapabilityRequestResponse(int, @NonNull String, @Nullable android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException;
- method public final void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException;
- method public void respondToCapabilityRequest(@NonNull String, @NonNull android.telephony.ims.RcsContactUceCapability, int);
- method public void respondToCapabilityRequestWithError(@NonNull android.net.Uri, int, @NonNull String, int);
- method public void sendCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int);
- field public static final int RESPONSE_BAD_REQUEST = 5; // 0x5
- field public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4; // 0x4
- field public static final int RESPONSE_GENERIC_FAILURE = -1; // 0xffffffff
- field public static final int RESPONSE_NOT_FOUND = 3; // 0x3
- field public static final int RESPONSE_REQUEST_TIMEOUT = 2; // 0x2
- field public static final int RESPONSE_SUCCESS = 0; // 0x0
- field public static final int RESPONSE_TEMPORARILY_UNAVAILABLE = 1; // 0x1
- }
-
}
package android.telephony.mbms {
diff --git a/api/test-current.txt b/api/test-current.txt
index e352cb6..10cecb76 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -812,18 +812,22 @@
public abstract class IntegrityFormula {
method @NonNull public static android.content.integrity.IntegrityFormula all(@NonNull android.content.integrity.IntegrityFormula...);
method @NonNull public static android.content.integrity.IntegrityFormula any(@NonNull android.content.integrity.IntegrityFormula...);
- method @NonNull public android.content.integrity.IntegrityFormula equalTo(@NonNull String);
- method @NonNull public android.content.integrity.IntegrityFormula equalTo(boolean);
- method @NonNull public android.content.integrity.IntegrityFormula equalTo(long);
- method @NonNull public android.content.integrity.IntegrityFormula greaterThan(long);
- method @NonNull public android.content.integrity.IntegrityFormula greaterThanOrEquals(long);
method @NonNull public static android.content.integrity.IntegrityFormula not(@NonNull android.content.integrity.IntegrityFormula);
- field @NonNull public static final android.content.integrity.IntegrityFormula APP_CERTIFICATE;
- field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_CERTIFICATE;
- field @NonNull public static final android.content.integrity.IntegrityFormula INSTALLER_NAME;
- field @NonNull public static final android.content.integrity.IntegrityFormula PACKAGE_NAME;
- field @NonNull public static final android.content.integrity.IntegrityFormula PRE_INSTALLED;
- field @NonNull public static final android.content.integrity.IntegrityFormula VERSION_CODE;
+ }
+
+ public static final class IntegrityFormula.Application {
+ method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String);
+ method @NonNull public static android.content.integrity.IntegrityFormula isPreInstalled();
+ method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String);
+ method @NonNull public static android.content.integrity.IntegrityFormula versionCodeEquals(@NonNull long);
+ method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThan(@NonNull long);
+ method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long);
+ }
+
+ public static final class IntegrityFormula.Installer {
+ method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String);
+ method @NonNull public static android.content.integrity.IntegrityFormula notAllowedByManifest();
+ method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String);
}
public final class Rule implements android.os.Parcelable {
@@ -3961,19 +3965,7 @@
}
public class ImsRcsManager implements android.telephony.ims.RegistrationManager {
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isAvailable(int) throws android.telephony.ims.ImsException;
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isCapable(int, int) throws android.telephony.ims.ImsException;
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException;
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerRcsAvailabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException;
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback);
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterRcsAvailabilityCallback(@NonNull android.telephony.ims.ImsRcsManager.AvailabilityCallback) throws android.telephony.ims.ImsException;
- }
-
- public static class ImsRcsManager.AvailabilityCallback {
- ctor public ImsRcsManager.AvailabilityCallback();
- method public void onAvailabilityChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
+ method @NonNull public android.telephony.ims.RcsUceAdapter getUceAdapter();
}
public class ImsService extends android.app.Service {
@@ -4348,34 +4340,8 @@
}
public class RcsUceAdapter {
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getUcePublishState() throws android.telephony.ims.ImsException;
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException;
- method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void requestCapabilities(@NonNull java.util.concurrent.Executor, @NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
- field public static final int ERROR_ALREADY_IN_QUEUE = 13; // 0xd
- field public static final int ERROR_FORBIDDEN = 6; // 0x6
- field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1
- field public static final int ERROR_INSUFFICIENT_MEMORY = 11; // 0xb
- field public static final int ERROR_LOST_NETWORK = 12; // 0xc
- field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5
- field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3
- field public static final int ERROR_NOT_ENABLED = 2; // 0x2
- field public static final int ERROR_NOT_FOUND = 7; // 0x7
- field public static final int ERROR_NOT_REGISTERED = 4; // 0x4
- field public static final int ERROR_REQUEST_TIMEOUT = 10; // 0xa
- field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8
- field public static final int PUBLISH_STATE_200_OK = 1; // 0x1
- field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2
- field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6
- field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4
- field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5
- field public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; // 0x3
- }
-
- public static class RcsUceAdapter.CapabilitiesCallback {
- ctor public RcsUceAdapter.CapabilitiesCallback();
- method public void onCapabilitiesReceived(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>);
- method public void onError(int);
}
}
@@ -4461,23 +4427,8 @@
public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
ctor public RcsFeature();
method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
- method @NonNull public android.telephony.ims.stub.RcsSipOptionsImplBase getOptionsExchangeImpl();
- method @NonNull public android.telephony.ims.stub.RcsPresenceExchangeImplBase getPresenceExchangeImpl();
- method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities);
method public void onFeatureReady();
method public void onFeatureRemoved();
- method public boolean queryCapabilityConfiguration(int, int);
- method @NonNull public final android.telephony.ims.feature.RcsFeature.RcsImsCapabilities queryCapabilityStatus();
- }
-
- public static class RcsFeature.RcsImsCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities {
- ctor public RcsFeature.RcsImsCapabilities(int);
- method public void addCapabilities(int);
- method public boolean isCapable(int);
- method public void removeCapabilities(int);
- field public static final int CAPABILITY_TYPE_NONE = 0; // 0x0
- field public static final int CAPABILITY_TYPE_OPTIONS_UCE = 1; // 0x1
- field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
}
}
@@ -4655,71 +4606,6 @@
field public static final int INVALID_RESULT = -1; // 0xffffffff
}
- public class RcsCapabilityExchange {
- ctor public RcsCapabilityExchange();
- method public final void onCommandUpdate(int, int) throws android.telephony.ims.ImsException;
- field public static final int COMMAND_CODE_FETCH_ERROR = 4; // 0x4
- field public static final int COMMAND_CODE_GENERIC_FAILURE = 2; // 0x2
- field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 6; // 0x6
- field public static final int COMMAND_CODE_INVALID_PARAM = 3; // 0x3
- field public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 7; // 0x7
- field public static final int COMMAND_CODE_NOT_FOUND = 9; // 0x9
- field public static final int COMMAND_CODE_NOT_SUPPORTED = 8; // 0x8
- field public static final int COMMAND_CODE_NO_CHANGE_IN_CAP = 11; // 0xb
- field public static final int COMMAND_CODE_REQUEST_TIMEOUT = 5; // 0x5
- field public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 10; // 0xa
- field public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; // 0x0
- field public static final int COMMAND_CODE_SUCCESS = 1; // 0x1
- }
-
- public class RcsPresenceExchangeImplBase extends android.telephony.ims.stub.RcsCapabilityExchange {
- ctor public RcsPresenceExchangeImplBase();
- method public final void onCapabilityRequestResponse(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>, int) throws android.telephony.ims.ImsException;
- method public final void onNetworkResponse(int, @NonNull String, int) throws android.telephony.ims.ImsException;
- method public final void onNotifyUpdateCapabilites(int) throws android.telephony.ims.ImsException;
- method public final void onUnpublish() throws android.telephony.ims.ImsException;
- method public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, int);
- method public void updateCapabilities(@NonNull android.telephony.ims.RcsContactUceCapability, int);
- field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 0; // 0x0
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 6; // 0x6
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 5; // 0x5
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 3; // 0x3
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 4; // 0x4
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 8; // 0x8
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1; // 0x1
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2; // 0x2
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; // 0xa
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb
- field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 7; // 0x7
- field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 9; // 0x9
- field public static final int RESPONSE_FORBIDDEN = 3; // 0x3
- field public static final int RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE = 2; // 0x2
- field public static final int RESPONSE_NOT_FOUND = 4; // 0x4
- field public static final int RESPONSE_NOT_REGISTERED = 1; // 0x1
- field public static final int RESPONSE_SIP_INTERVAL_TOO_SHORT = 7; // 0x7
- field public static final int RESPONSE_SIP_REQUEST_TIMEOUT = 5; // 0x5
- field public static final int RESPONSE_SIP_SERVICE_UNAVAILABLE = 8; // 0x8
- field public static final int RESPONSE_SUBSCRIBE_GENERIC_FAILURE = -1; // 0xffffffff
- field public static final int RESPONSE_SUBSCRIBE_TOO_LARGE = 6; // 0x6
- field public static final int RESPONSE_SUCCESS = 0; // 0x0
- }
-
- public class RcsSipOptionsImplBase extends android.telephony.ims.stub.RcsCapabilityExchange {
- ctor public RcsSipOptionsImplBase();
- method public final void onCapabilityRequestResponse(int, @NonNull String, @Nullable android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException;
- method public final void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException;
- method public void respondToCapabilityRequest(@NonNull String, @NonNull android.telephony.ims.RcsContactUceCapability, int);
- method public void respondToCapabilityRequestWithError(@NonNull android.net.Uri, int, @NonNull String, int);
- method public void sendCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int);
- field public static final int RESPONSE_BAD_REQUEST = 5; // 0x5
- field public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4; // 0x4
- field public static final int RESPONSE_GENERIC_FAILURE = -1; // 0xffffffff
- field public static final int RESPONSE_NOT_FOUND = 3; // 0x3
- field public static final int RESPONSE_REQUEST_TIMEOUT = 2; // 0x2
- field public static final int RESPONSE_SUCCESS = 0; // 0x0
- field public static final int RESPONSE_TEMPORARILY_UNAVAILABLE = 1; // 0x1
- }
-
}
package android.telephony.mbms {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 89b1798..23f9f22 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -7960,6 +7960,10 @@
// presentation, until the buffer was ready to be presented.
optional FrameTimingHistogram post_to_acquire = 9
[(android.os.statsd.log_mode) = MODE_BYTES];
+ // Frames missed latch because the acquire fence didn't fire
+ optional int64 late_acquire_frames = 10;
+ // Frames latched early because the desired present time was bad
+ optional int64 bad_desired_present_frames = 11;
}
/**
diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
index d79740b..9912d2b 100644
--- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java
@@ -76,6 +76,16 @@
private final int mDescriptionResId;
/**
+ * Resource id of the animated image of the accessibility shortcut target.
+ */
+ private final int mAnimatedImageRes;
+
+ /**
+ * Resource id of the html description of the accessibility shortcut target.
+ */
+ private final int mHtmlDescriptionRes;
+
+ /**
* Creates a new instance.
*
* @param context Context for accessing resources.
@@ -119,6 +129,14 @@
// Gets summary
mSummaryResId = asAttributes.getResourceId(
com.android.internal.R.styleable.AccessibilityShortcutTarget_summary, 0);
+ // Gets animated image
+ mAnimatedImageRes = asAttributes.getResourceId(
+ com.android.internal.R.styleable
+ .AccessibilityShortcutTarget_animatedImageDrawable, 0);
+ // Gets html description
+ mHtmlDescriptionRes = asAttributes.getResourceId(
+ com.android.internal.R.styleable.AccessibilityShortcutTarget_htmlDescription,
+ 0);
asAttributes.recycle();
if (mDescriptionResId == 0 || mSummaryResId == 0) {
@@ -172,6 +190,25 @@
}
/**
+ * The animated image resource id of the accessibility shortcut target.
+ *
+ * @return The animated image resource id.
+ */
+ public int getAnimatedImageRes() {
+ return mAnimatedImageRes;
+ }
+
+ /**
+ * The localized html description of the accessibility shortcut target.
+ *
+ * @return The localized html description.
+ */
+ @Nullable
+ public String loadHtmlDescription(@NonNull PackageManager packageManager) {
+ return loadResourceString(packageManager, mActivityInfo, mHtmlDescriptionRes);
+ }
+
+ /**
* Gets string resource by the given activity and resource id.
*/
@Nullable
diff --git a/core/java/android/app/prediction/AppPredictionSessionId.java b/core/java/android/app/prediction/AppPredictionSessionId.java
index e5e06f8..876bafd 100644
--- a/core/java/android/app/prediction/AppPredictionSessionId.java
+++ b/core/java/android/app/prediction/AppPredictionSessionId.java
@@ -22,6 +22,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* The id for an app prediction session. See {@link AppPredictor}.
*
@@ -32,18 +34,28 @@
public final class AppPredictionSessionId implements Parcelable {
private final String mId;
+ private final int mUserId;
/**
* Creates a new id for a prediction session.
*
* @hide
*/
- public AppPredictionSessionId(@NonNull String id) {
+ public AppPredictionSessionId(@NonNull final String id, final int userId) {
mId = id;
+ mUserId = userId;
}
private AppPredictionSessionId(Parcel p) {
mId = p.readString();
+ mUserId = p.readInt();
+ }
+
+ /**
+ * @hide
+ */
+ public int getUserId() {
+ return mUserId;
}
@Override
@@ -51,17 +63,17 @@
if (!getClass().equals(o != null ? o.getClass() : null)) return false;
AppPredictionSessionId other = (AppPredictionSessionId) o;
- return mId.equals(other.mId);
+ return mId.equals(other.mId) && mUserId == other.mUserId;
}
@Override
public @NonNull String toString() {
- return mId;
+ return mId + "," + mUserId;
}
@Override
public int hashCode() {
- return mId.hashCode();
+ return Objects.hash(mId, mUserId);
}
@Override
@@ -72,6 +84,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mId);
+ dest.writeInt(mUserId);
}
public static final @android.annotation.NonNull Parcelable.Creator<AppPredictionSessionId> CREATOR =
diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java
index cd635d6..f0eedf3 100644
--- a/core/java/android/app/prediction/AppPredictor.java
+++ b/core/java/android/app/prediction/AppPredictor.java
@@ -96,7 +96,7 @@
IBinder b = ServiceManager.getService(Context.APP_PREDICTION_SERVICE);
mPredictionManager = IPredictionManager.Stub.asInterface(b);
mSessionId = new AppPredictionSessionId(
- context.getPackageName() + ":" + UUID.randomUUID().toString());
+ context.getPackageName() + ":" + UUID.randomUUID().toString(), context.getUserId());
try {
mPredictionManager.createPredictionSession(predictionContext, mSessionId);
} catch (RemoteException e) {
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index d8c653c6..b672a08 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -643,8 +643,9 @@
@SystemApi
@Nullable
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public BluetoothCodecStatus getCodecStatus(@Nullable BluetoothDevice device) {
+ public BluetoothCodecStatus getCodecStatus(@NonNull BluetoothDevice device) {
if (DBG) Log.d(TAG, "getCodecStatus(" + device + ")");
+ verifyDeviceNotNull(device, "getCodecStatus");
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()) {
@@ -670,9 +671,14 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void setCodecConfigPreference(@Nullable BluetoothDevice device,
- @Nullable BluetoothCodecConfig codecConfig) {
+ public void setCodecConfigPreference(@NonNull BluetoothDevice device,
+ @NonNull BluetoothCodecConfig codecConfig) {
if (DBG) Log.d(TAG, "setCodecConfigPreference(" + device + ")");
+ verifyDeviceNotNull(device, "setCodecConfigPreference");
+ if (codecConfig == null) {
+ Log.e(TAG, "setCodecConfigPreference: Codec config can't be null");
+ throw new IllegalArgumentException("codecConfig cannot be null");
+ }
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled()) {
@@ -695,8 +701,9 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void enableOptionalCodecs(@Nullable BluetoothDevice device) {
+ public void enableOptionalCodecs(@NonNull BluetoothDevice device) {
if (DBG) Log.d(TAG, "enableOptionalCodecs(" + device + ")");
+ verifyDeviceNotNull(device, "enableOptionalCodecs");
enableDisableOptionalCodecs(device, true);
}
@@ -709,8 +716,9 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void disableOptionalCodecs(@Nullable BluetoothDevice device) {
+ public void disableOptionalCodecs(@NonNull BluetoothDevice device) {
if (DBG) Log.d(TAG, "disableOptionalCodecs(" + device + ")");
+ verifyDeviceNotNull(device, "disableOptionalCodecs");
enableDisableOptionalCodecs(device, false);
}
@@ -750,7 +758,8 @@
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
@OptionalCodecsSupportStatus
- public int supportsOptionalCodecs(@Nullable BluetoothDevice device) {
+ public int isOptionalCodecsSupported(@NonNull BluetoothDevice device) {
+ verifyDeviceNotNull(device, "isOptionalCodecsSupported");
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
@@ -775,7 +784,8 @@
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
@OptionalCodecsPreferenceStatus
- public int getOptionalCodecsEnabled(@Nullable BluetoothDevice device) {
+ public int isOptionalCodecsEnabled(@NonNull BluetoothDevice device) {
+ verifyDeviceNotNull(device, "isOptionalCodecsEnabled");
try {
final IBluetoothA2dp service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
@@ -800,8 +810,9 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
- public void setOptionalCodecsEnabled(@Nullable BluetoothDevice device,
+ public void setOptionalCodecsEnabled(@NonNull BluetoothDevice device,
@OptionalCodecsPreferenceStatus int value) {
+ verifyDeviceNotNull(device, "setOptionalCodecsEnabled");
try {
if (value != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN
&& value != BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED
@@ -854,6 +865,13 @@
return false;
}
+ private void verifyDeviceNotNull(BluetoothDevice device, String methodName) {
+ if (device == null) {
+ Log.e(TAG, methodName + ": device param is null");
+ throw new IllegalArgumentException("Device cannot be null");
+ }
+ }
+
private boolean isValidDevice(BluetoothDevice device) {
if (device == null) return false;
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index ee2cc6d..ab49230 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -66,7 +66,7 @@
*/
@SystemApi
@SuppressLint("ActionValue")
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED";
@@ -296,7 +296,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean setConnectionPolicy(@Nullable BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
@@ -345,7 +345,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothA2dpSink service = getService();
@@ -370,7 +370,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean isAudioPlaying(@Nullable BluetoothDevice device) {
final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 0e0161f..f32a4ab 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -700,27 +700,6 @@
/** @hide */
public static final String REMOTE_CALLBACK_RESULT = "result";
- /**
- * How long we wait for an attached process to publish its content providers
- * before we decide it must be hung.
- * @hide
- */
- public static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS = 10 * 1000;
-
- /**
- * How long we wait for an provider to be published. Should be longer than
- * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS}.
- * @hide
- */
- public static final int CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS =
- CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS + 10 * 1000;
-
- // Should be >= {@link #CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS}, because that's how
- // long ActivityManagerService is giving a content provider to get published if a new process
- // needs to be started for that.
- private static final int GET_TYPE_TIMEOUT_MILLIS =
- CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS + 5 * 1000;
-
public ContentResolver(@Nullable Context context) {
this(context, null);
}
@@ -870,6 +849,8 @@
}
}
+ private static final int GET_TYPE_TIMEOUT_MILLIS = 3000;
+
private static class GetTypeResultListener implements RemoteCallback.OnResultListener {
@GuardedBy("this")
public boolean done;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c37c1c1..0f88c90 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4972,11 +4972,14 @@
* <pre>
* <accessibility-shortcut-target
* android:description="@string/shortcut_target_description"
- * android:summary="@string/shortcut_target_summary" />
+ * android:summary="@string/shortcut_target_summary"
+ * android:animatedImageDrawable="@drawable/shortcut_target_animated_image"
+ * android:htmlDescription="@string/shortcut_target_html_description" />
* </pre>
* <p>
* Both description and summary are necessary. The system will ignore the accessibility
- * shortcut target if they are missing.
+ * shortcut target if they are missing. The animated image and html description are supported
+ * to help users understand how to use the shortcut target.
* </p>
*/
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
diff --git a/core/java/android/content/integrity/AppInstallMetadata.java b/core/java/android/content/integrity/AppInstallMetadata.java
index cd5117b..4be7e6d 100644
--- a/core/java/android/content/integrity/AppInstallMetadata.java
+++ b/core/java/android/content/integrity/AppInstallMetadata.java
@@ -18,7 +18,9 @@
import android.annotation.NonNull;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
/**
@@ -40,6 +42,7 @@
private final List<String> mInstallerCertificates;
private final long mVersionCode;
private final boolean mIsPreInstalled;
+ private final Map<String, String> mAllowedInstallersAndCertificates;
private AppInstallMetadata(Builder builder) {
this.mPackageName = builder.mPackageName;
@@ -48,6 +51,7 @@
this.mInstallerCertificates = builder.mInstallerCertificates;
this.mVersionCode = builder.mVersionCode;
this.mIsPreInstalled = builder.mIsPreInstalled;
+ this.mAllowedInstallersAndCertificates = builder.mAllowedInstallersAndCertificates;
}
@NonNull
@@ -80,6 +84,13 @@
return mIsPreInstalled;
}
+ /**
+ * Get the allowed installers and their corresponding cert.
+ */
+ public Map<String, String> getAllowedInstallersAndCertificates() {
+ return mAllowedInstallersAndCertificates;
+ }
+
@Override
public String toString() {
return String.format(
@@ -101,6 +112,23 @@
private List<String> mInstallerCertificates;
private long mVersionCode;
private boolean mIsPreInstalled;
+ private Map<String, String> mAllowedInstallersAndCertificates;
+
+ public Builder() {
+ mAllowedInstallersAndCertificates = new HashMap<>();
+ }
+
+ /**
+ * Add allowed installers and cert.
+ *
+ * @see AppInstallMetadata#getAllowedInstallersAndCertificates()
+ */
+ @NonNull
+ public Builder setAllowedInstallersAndCert(
+ @NonNull Map<String, String> allowedInstallersAndCertificates) {
+ this.mAllowedInstallersAndCertificates = allowedInstallersAndCertificates;
+ return this;
+ }
/**
* Set package name of the app to be installed.
diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java
index 42459779..d911eab 100644
--- a/core/java/android/content/integrity/AtomicFormula.java
+++ b/core/java/android/content/integrity/AtomicFormula.java
@@ -55,14 +55,12 @@
PRE_INSTALLED,
})
@Retention(RetentionPolicy.SOURCE)
- public @interface Key {
- }
+ public @interface Key {}
/** @hide */
@IntDef(value = {EQ, GT, GTE})
@Retention(RetentionPolicy.SOURCE)
- public @interface Operator {
- }
+ public @interface Operator {}
/**
* Package name of the app.
@@ -354,7 +352,8 @@
"Key %s cannot be used with StringAtomicFormula", keyToString(key)));
mValue = hashValue(key, value);
mIsHashedValue =
- key == APP_CERTIFICATE || key == INSTALLER_CERTIFICATE
+ key == APP_CERTIFICATE
+ || key == INSTALLER_CERTIFICATE
? true
: !mValue.equals(value);
}
diff --git a/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java b/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java
new file mode 100644
index 0000000..475f019
--- /dev/null
+++ b/core/java/android/content/integrity/InstallerAllowedByManifestFormula.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.integrity;
+
+import java.util.Map;
+
+/**
+ * An atomic formula that evaluates to true if the installer of the current install is specified in
+ * the "allowed installer" field in the android manifest. Note that an empty "allowed installer" by
+ * default means containing all possible installers.
+ *
+ * @hide
+ */
+public class InstallerAllowedByManifestFormula extends IntegrityFormula {
+
+ @Override
+ public int getTag() {
+ return IntegrityFormula.INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG;
+ }
+
+ @Override
+ public boolean matches(AppInstallMetadata appInstallMetadata) {
+ Map<String, String> allowedInstallersAndCertificates =
+ appInstallMetadata.getAllowedInstallersAndCertificates();
+ return allowedInstallersAndCertificates.isEmpty()
+ || installerInAllowedInstallersFromManifest(
+ appInstallMetadata, allowedInstallersAndCertificates);
+ }
+
+ @Override
+ public boolean isAppCertificateFormula() {
+ return false;
+ }
+
+ @Override
+ public boolean isInstallerFormula() {
+ return true;
+ }
+
+ private static boolean installerInAllowedInstallersFromManifest(
+ AppInstallMetadata appInstallMetadata,
+ Map<String, String> allowedInstallersAndCertificates) {
+ return allowedInstallersAndCertificates.containsKey(appInstallMetadata.getInstallerName())
+ && appInstallMetadata.getInstallerCertificates()
+ .contains(
+ allowedInstallersAndCertificates
+ .get(appInstallMetadata.getInstallerName()));
+ }
+}
diff --git a/core/java/android/content/integrity/IntegrityFormula.java b/core/java/android/content/integrity/IntegrityFormula.java
index a2d937e..ac4c907 100644
--- a/core/java/android/content/integrity/IntegrityFormula.java
+++ b/core/java/android/content/integrity/IntegrityFormula.java
@@ -42,66 +42,88 @@
@VisibleForTesting
public abstract class IntegrityFormula {
- /**
- * A static formula base for package name formulas.
- *
- * This formulation is incomplete and should always be used with {@code equals} formulation.
- * Evaluates to false when used directly and cannot be written as a parcel.
- */
- @NonNull
- public static final IntegrityFormula PACKAGE_NAME =
- new StringAtomicFormula(AtomicFormula.PACKAGE_NAME);
+ /** Factory class for creating integrity formulas based on the app being installed. */
+ public static final class Application {
+ /** Returns an integrity formula that checks the equality to a package name. */
+ @NonNull
+ public static IntegrityFormula packageNameEquals(@NonNull String packageName) {
+ return new StringAtomicFormula(AtomicFormula.PACKAGE_NAME, packageName);
+ }
- /**
- * A static formula base for app certificate formulas.
- *
- * This formulation is incomplete and should always be used with {@code equals} formulation.
- * Evaluates to false when used directly and cannot be written as a parcel.
- */
- @NonNull
- public static final IntegrityFormula APP_CERTIFICATE =
- new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE);
+ /**
+ * Returns an integrity formula that checks if the app certificates contain {@code
+ * appCertificate}.
+ */
+ @NonNull
+ public static IntegrityFormula certificatesContain(@NonNull String appCertificate) {
+ return new StringAtomicFormula(AtomicFormula.APP_CERTIFICATE, appCertificate);
+ }
- /**
- * A static formula base for installer name formulas.
- *
- * This formulation is incomplete and should always be used with {@code equals} formulation.
- * Evaluates to false when used directly and cannot be written as a parcel.
- */
- @NonNull
- public static final IntegrityFormula INSTALLER_NAME =
- new StringAtomicFormula(AtomicFormula.INSTALLER_NAME);
+ /** Returns an integrity formula that checks the equality to a version code. */
+ @NonNull
+ public static IntegrityFormula versionCodeEquals(@NonNull long versionCode) {
+ return new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.EQ, versionCode);
+ }
- /**
- * A static formula base for installer certificate formulas.
- *
- * This formulation is incomplete and should always be used with {@code equals} formulation.
- * Evaluates to false when used directly and cannot be written as a parcel.
- */
- @NonNull
- public static final IntegrityFormula INSTALLER_CERTIFICATE =
- new StringAtomicFormula(AtomicFormula.INSTALLER_CERTIFICATE);
+ /**
+ * Returns an integrity formula that checks the app's version code is greater than the
+ * provided value.
+ */
+ @NonNull
+ public static IntegrityFormula versionCodeGreaterThan(@NonNull long versionCode) {
+ return new LongAtomicFormula(AtomicFormula.VERSION_CODE, AtomicFormula.GT, versionCode);
+ }
- /**
- * A static formula base for version code name formulas.
- *
- * This formulation is incomplete and should always be used with {@code equals},
- * {@code greaterThan} and {@code greaterThanEquals} formulation. Evaluates to false when used
- * directly and cannot be written as a parcel.
- */
- @NonNull
- public static final IntegrityFormula VERSION_CODE =
- new LongAtomicFormula(AtomicFormula.VERSION_CODE);
+ /**
+ * Returns an integrity formula that checks the app's version code is greater than or equal
+ * to the provided value.
+ */
+ @NonNull
+ public static IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long versionCode) {
+ return new LongAtomicFormula(
+ AtomicFormula.VERSION_CODE, AtomicFormula.GTE, versionCode);
+ }
- /**
- * A static formula base for pre-installed status formulas.
- *
- * This formulation is incomplete and should always be used with {@code equals} formulation.
- * Evaluates to false when used directly and cannot be written as a parcel.
- */
- @NonNull
- public static final IntegrityFormula PRE_INSTALLED =
- new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED);
+ /** Returns an integrity formula that is valid when app is pre-installed. */
+ @NonNull
+ public static IntegrityFormula isPreInstalled() {
+ return new BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true);
+ }
+
+ private Application() {
+ }
+ }
+
+ /** Factory class for creating integrity formulas based on installer. */
+ public static final class Installer {
+ /** Returns an integrity formula that checks the equality to an installer name. */
+ @NonNull
+ public static IntegrityFormula packageNameEquals(@NonNull String installerName) {
+ return new StringAtomicFormula(AtomicFormula.INSTALLER_NAME, installerName);
+ }
+
+ /**
+ * An static formula that evaluates to true if the installer is NOT allowed according to the
+ * "allowed installer" field in the android manifest.
+ */
+ @NonNull
+ public static IntegrityFormula notAllowedByManifest() {
+ return not(new InstallerAllowedByManifestFormula());
+ }
+
+ /**
+ * Returns an integrity formula that checks if the installer certificates contain {@code
+ * installerCertificate}.
+ */
+ @NonNull
+ public static IntegrityFormula certificatesContain(@NonNull String installerCertificate) {
+ return new StringAtomicFormula(AtomicFormula.INSTALLER_CERTIFICATE,
+ installerCertificate);
+ }
+
+ private Installer() {
+ }
+ }
/** @hide */
@IntDef(
@@ -109,10 +131,12 @@
COMPOUND_FORMULA_TAG,
STRING_ATOMIC_FORMULA_TAG,
LONG_ATOMIC_FORMULA_TAG,
- BOOLEAN_ATOMIC_FORMULA_TAG
+ BOOLEAN_ATOMIC_FORMULA_TAG,
+ INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG
})
@Retention(RetentionPolicy.SOURCE)
- @interface Tag {}
+ @interface Tag {
+ }
/** @hide */
public static final int COMPOUND_FORMULA_TAG = 0;
@@ -122,6 +146,8 @@
public static final int LONG_ATOMIC_FORMULA_TAG = 2;
/** @hide */
public static final int BOOLEAN_ATOMIC_FORMULA_TAG = 3;
+ /** @hide */
+ public static final int INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG = 4;
/**
* Returns the tag that identifies the current class.
@@ -135,14 +161,14 @@
*
* @hide
*/
- public abstract @Tag boolean matches(AppInstallMetadata appInstallMetadata);
+ public abstract boolean matches(AppInstallMetadata appInstallMetadata);
/**
* Returns true when the formula (or one of its atomic formulas) has app certificate as key.
*
* @hide
*/
- public abstract @Tag boolean isAppCertificateFormula();
+ public abstract boolean isAppCertificateFormula();
/**
* Returns true when the formula (or one of its atomic formulas) has installer package name
@@ -150,7 +176,7 @@
*
* @hide
*/
- public abstract @Tag boolean isInstallerFormula();
+ public abstract boolean isInstallerFormula();
/**
* Write an {@link IntegrityFormula} to {@link android.os.Parcel}.
@@ -159,7 +185,6 @@
* {@link Parcelable}.
*
* @throws IllegalArgumentException if {@link IntegrityFormula} is not a recognized subclass
- *
* @hide
*/
public static void writeToParcel(
@@ -195,70 +220,6 @@
}
/**
- * Returns an integrity formula that evaluates to true when value of the key matches to the
- * provided string value.
- *
- * <p>The value will be hashed with SHA256 and the hex digest will be computed; for
- * all cases except when the key is PACKAGE_NAME or INSTALLER_NAME and the value is less than
- * 32 characters.
- *
- * <p>Throws an {@link IllegalArgumentException} if the key is not string typed.
- */
- @NonNull
- public IntegrityFormula equalTo(@NonNull String value) {
- AtomicFormula baseFormula = (AtomicFormula) this;
- return new AtomicFormula.StringAtomicFormula(baseFormula.getKey(), value);
- }
-
- /**
- * Returns an integrity formula that evaluates to true when the boolean value of the key matches
- * the provided boolean value. It can only be used with the boolean comparison keys.
- *
- * <p>Throws an {@link IllegalArgumentException} if the key is not boolean typed.
- */
- @NonNull
- public IntegrityFormula equalTo(boolean value) {
- AtomicFormula baseFormula = (AtomicFormula) this;
- return new AtomicFormula.BooleanAtomicFormula(baseFormula.getKey(), value);
- }
-
- /**
- * Returns a formula that evaluates to true when the value of the key in the package being
- * installed is equal to {@code value}.
- *
- * <p>Throws an {@link IllegalArgumentException} if the key is not long typed.
- */
- @NonNull
- public IntegrityFormula equalTo(long value) {
- AtomicFormula baseFormula = (AtomicFormula) this;
- return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.EQ, value);
- }
-
- /**
- * Returns a formula that evaluates to true when the value of the key in the package being
- * installed is greater than {@code value}.
- *
- * <p>Throws an {@link IllegalArgumentException} if the key is not long typed.
- */
- @NonNull
- public IntegrityFormula greaterThan(long value) {
- AtomicFormula baseFormula = (AtomicFormula) this;
- return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.GT, value);
- }
-
- /**
- * Returns a formula that evaluates to true when the value of the key in the package being
- * installed is greater than or equals to the {@code value}.
- *
- * <p>Throws an {@link IllegalArgumentException} if the key is not long typed.
- */
- @NonNull
- public IntegrityFormula greaterThanOrEquals(long value) {
- AtomicFormula baseFormula = (AtomicFormula) this;
- return new AtomicFormula.LongAtomicFormula(baseFormula.getKey(), AtomicFormula.GTE, value);
- }
-
- /**
* Returns a formula that evaluates to true when any formula in {@code formulae} evaluates to
* true.
*
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 5a13651..add67aa 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -132,6 +132,14 @@
int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14;
/**
+ * A security vulnerability has been discovered and the sensor is unavailable until a
+ * security update has addressed this issue. This error can be received if for example,
+ * authentication was requested with {@link Authenticators#BIOMETRIC_STRONG}, but the
+ * sensor's strength can currently only meet {@link Authenticators#BIOMETRIC_WEAK}.
+ */
+ int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15;
+
+ /**
* This constant is only used by SystemUI. It notifies SystemUI that authentication was paused
* because the authentication attempt was unsuccessful.
* @hide
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
index bae0fd3..eafcf52 100644
--- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
@@ -17,6 +17,7 @@
package android.hardware.biometrics;
import android.app.KeyguardManager;
+import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.face.FaceManager;
/**
@@ -36,12 +37,12 @@
* authentication. Note this is to accommodate people who have limited
* vision.
*/
- public static final int FEATURE_REQUIRE_ATTENTION = 1;
+ int FEATURE_REQUIRE_ATTENTION = 1;
/**
* Require a diverse set of poses during enrollment. Note this is to
* accommodate people with limited mobility.
*/
- public static final int FEATURE_REQUIRE_REQUIRE_DIVERSITY = 2;
+ int FEATURE_REQUIRE_REQUIRE_DIVERSITY = 2;
//
// Error messages from face authentication hardware during initialization, enrollment,
@@ -50,32 +51,32 @@
/**
* The hardware is unavailable. Try again later.
*/
- public static final int FACE_ERROR_HW_UNAVAILABLE = 1;
+ int FACE_ERROR_HW_UNAVAILABLE = 1;
/**
* Error state returned when the sensor was unable to process the current image.
*/
- public static final int FACE_ERROR_UNABLE_TO_PROCESS = 2;
+ int FACE_ERROR_UNABLE_TO_PROCESS = 2;
/**
* Error state returned when the current request has been running too long. This is intended to
* prevent programs from waiting for the face authentication sensor indefinitely. The timeout is
* platform and sensor-specific, but is generally on the order of 30 seconds.
*/
- public static final int FACE_ERROR_TIMEOUT = 3;
+ int FACE_ERROR_TIMEOUT = 3;
/**
* Error state returned for operations like enrollment; the operation cannot be completed
* because there's not enough storage remaining to complete the operation.
*/
- public static final int FACE_ERROR_NO_SPACE = 4;
+ int FACE_ERROR_NO_SPACE = 4;
/**
* The operation was canceled because the face authentication sensor is unavailable. For
* example, this may happen when the user is switched, the device is locked or another pending
* operation prevents or disables it.
*/
- public static final int FACE_ERROR_CANCELED = 5;
+ int FACE_ERROR_CANCELED = 5;
/**
* The {@link FaceManager#remove} call failed. Typically this will happen when the
@@ -83,13 +84,13 @@
*
* @hide
*/
- public static final int FACE_ERROR_UNABLE_TO_REMOVE = 6;
+ int FACE_ERROR_UNABLE_TO_REMOVE = 6;
/**
* The operation was canceled because the API is locked out due to too many attempts.
* This occurs after 5 failed attempts, and lasts for 30 seconds.
*/
- public static final int FACE_ERROR_LOCKOUT = 7;
+ int FACE_ERROR_LOCKOUT = 7;
/**
* Hardware vendors may extend this list if there are conditions that do not fall under one of
@@ -99,52 +100,62 @@
* expected to show the error message string if they happen, but are advised not to rely on the
* message id since they will be device and vendor-specific
*/
- public static final int FACE_ERROR_VENDOR = 8;
+ int FACE_ERROR_VENDOR = 8;
/**
* The operation was canceled because FACE_ERROR_LOCKOUT occurred too many times.
* Face authentication is disabled until the user unlocks with strong authentication
* (PIN/Pattern/Password)
*/
- public static final int FACE_ERROR_LOCKOUT_PERMANENT = 9;
+ int FACE_ERROR_LOCKOUT_PERMANENT = 9;
/**
* The user canceled the operation. Upon receiving this, applications should use alternate
* authentication (e.g. a password). The application should also provide the means to return
* to face authentication, such as a "use face authentication" button.
*/
- public static final int FACE_ERROR_USER_CANCELED = 10;
+ int FACE_ERROR_USER_CANCELED = 10;
/**
* The user does not have a face enrolled.
*/
- public static final int FACE_ERROR_NOT_ENROLLED = 11;
+ int FACE_ERROR_NOT_ENROLLED = 11;
/**
* The device does not have a face sensor. This message will propagate if the calling app
* ignores the result from PackageManager.hasFeature(FEATURE_FACE) and calls
* this API anyway. Apps should always check for the feature before calling this API.
*/
- public static final int FACE_ERROR_HW_NOT_PRESENT = 12;
+ int FACE_ERROR_HW_NOT_PRESENT = 12;
/**
* The user pressed the negative button. This is a placeholder that is currently only used
* by the support library.
+ *
* @hide
*/
- public static final int FACE_ERROR_NEGATIVE_BUTTON = 13;
+ int FACE_ERROR_NEGATIVE_BUTTON = 13;
/**
* The device does not have pin, pattern, or password set up. See
* {@link BiometricPrompt.Builder#setDeviceCredentialAllowed(boolean)} and
* {@link KeyguardManager#isDeviceSecure()}
*/
- public static final int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14;
+ int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14;
+
+ /**
+ * A security vulnerability has been discovered and the sensor is unavailable until a
+ * security update has addressed this issue. This error can be received if for example,
+ * authentication was requested with {@link Authenticators#BIOMETRIC_STRONG}, but the
+ * sensor's strength can currently only meet {@link Authenticators#BIOMETRIC_WEAK}.
+ * @hide
+ */
+ int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15;
/**
* @hide
*/
- public static final int FACE_ERROR_VENDOR_BASE = 1000;
+ int FACE_ERROR_VENDOR_BASE = 1000;
//
// Image acquisition messages. These will not be sent to the user, since they conflict with
@@ -154,13 +165,13 @@
/**
* The image acquired was good.
*/
- public static final int FACE_ACQUIRED_GOOD = 0;
+ int FACE_ACQUIRED_GOOD = 0;
/**
* The face image was not good enough to process due to a detected condition.
* (See {@link #FACE_ACQUIRED_TOO_BRIGHT or @link #FACE_ACQUIRED_TOO_DARK}).
*/
- public static final int FACE_ACQUIRED_INSUFFICIENT = 1;
+ int FACE_ACQUIRED_INSUFFICIENT = 1;
/**
* The face image was too bright due to too much ambient light.
@@ -169,7 +180,7 @@
* The user is expected to take action to retry in better lighting conditions
* when this is returned.
*/
- public static final int FACE_ACQUIRED_TOO_BRIGHT = 2;
+ int FACE_ACQUIRED_TOO_BRIGHT = 2;
/**
* The face image was too dark due to illumination light obscured.
@@ -178,65 +189,65 @@
* The user is expected to take action to retry in better lighting conditions
* when this is returned.
*/
- public static final int FACE_ACQUIRED_TOO_DARK = 3;
+ int FACE_ACQUIRED_TOO_DARK = 3;
/**
* The detected face is too close to the sensor, and the image can't be processed.
* The user should be informed to move farther from the sensor when this is returned.
*/
- public static final int FACE_ACQUIRED_TOO_CLOSE = 4;
+ int FACE_ACQUIRED_TOO_CLOSE = 4;
/**
* The detected face is too small, as the user might be too far from the sensor.
* The user should be informed to move closer to the sensor when this is returned.
*/
- public static final int FACE_ACQUIRED_TOO_FAR = 5;
+ int FACE_ACQUIRED_TOO_FAR = 5;
/**
* Only the upper part of the face was detected. The sensor field of view is too high.
* The user should be informed to move up with respect to the sensor when this is returned.
*/
- public static final int FACE_ACQUIRED_TOO_HIGH = 6;
+ int FACE_ACQUIRED_TOO_HIGH = 6;
/**
* Only the lower part of the face was detected. The sensor field of view is too low.
* The user should be informed to move down with respect to the sensor when this is returned.
*/
- public static final int FACE_ACQUIRED_TOO_LOW = 7;
+ int FACE_ACQUIRED_TOO_LOW = 7;
/**
* Only the right part of the face was detected. The sensor field of view is too far right.
* The user should be informed to move to the right with respect to the sensor
* when this is returned.
*/
- public static final int FACE_ACQUIRED_TOO_RIGHT = 8;
+ int FACE_ACQUIRED_TOO_RIGHT = 8;
/**
* Only the left part of the face was detected. The sensor field of view is too far left.
* The user should be informed to move to the left with respect to the sensor
* when this is returned.
*/
- public static final int FACE_ACQUIRED_TOO_LEFT = 9;
+ int FACE_ACQUIRED_TOO_LEFT = 9;
/**
* The user's eyes have strayed away from the sensor. If this message is sent, the user should
* be informed to look at the device. If the user can't be found in the frame, one of the other
* acquisition messages should be sent, e.g. FACE_ACQUIRED_NOT_DETECTED.
*/
- public static final int FACE_ACQUIRED_POOR_GAZE = 10;
+ int FACE_ACQUIRED_POOR_GAZE = 10;
/**
* No face was detected in front of the sensor.
* The user should be informed to point the sensor to a face when this is returned.
*/
- public static final int FACE_ACQUIRED_NOT_DETECTED = 11;
+ int FACE_ACQUIRED_NOT_DETECTED = 11;
/**
* Too much motion was detected.
* The user should be informed to keep their face steady relative to the
* sensor.
*/
- public static final int FACE_ACQUIRED_TOO_MUCH_MOTION = 12;
+ int FACE_ACQUIRED_TOO_MUCH_MOTION = 12;
/**
* The sensor needs to be re-calibrated. This is an unexpected condition, and should only be
@@ -244,20 +255,20 @@
* requires user intervention, e.g. re-enrolling. The expected response to this message is to
* direct the user to re-enroll.
*/
- public static final int FACE_ACQUIRED_RECALIBRATE = 13;
+ int FACE_ACQUIRED_RECALIBRATE = 13;
/**
* The face is too different from a previous acquisition. This condition
* only applies to enrollment. This can happen if the user passes the
* device to someone else in the middle of enrollment.
*/
- public static final int FACE_ACQUIRED_TOO_DIFFERENT = 14;
+ int FACE_ACQUIRED_TOO_DIFFERENT = 14;
/**
* The face is too similar to a previous acquisition. This condition only
* applies to enrollment. The user should change their pose.
*/
- public static final int FACE_ACQUIRED_TOO_SIMILAR = 15;
+ int FACE_ACQUIRED_TOO_SIMILAR = 15;
/**
* The magnitude of the pan angle of the user’s face with respect to the sensor’s
@@ -269,7 +280,7 @@
*
* The user should be informed to look more directly at the camera.
*/
- public static final int FACE_ACQUIRED_PAN_TOO_EXTREME = 16;
+ int FACE_ACQUIRED_PAN_TOO_EXTREME = 16;
/**
* The magnitude of the tilt angle of the user’s face with respect to the sensor’s
@@ -280,7 +291,7 @@
*
* The user should be informed to look more directly at the camera.
*/
- public static final int FACE_ACQUIRED_TILT_TOO_EXTREME = 17;
+ int FACE_ACQUIRED_TILT_TOO_EXTREME = 17;
/**
* The magnitude of the roll angle of the user’s face with respect to the sensor’s
@@ -292,7 +303,7 @@
*
* The user should be informed to look more directly at the camera.
*/
- public static final int FACE_ACQUIRED_ROLL_TOO_EXTREME = 18;
+ int FACE_ACQUIRED_ROLL_TOO_EXTREME = 18;
/**
* The user’s face has been obscured by some object.
@@ -300,7 +311,7 @@
* The user should be informed to remove any objects from the line of sight from
* the sensor to the user’s face.
*/
- public static final int FACE_ACQUIRED_FACE_OBSCURED = 19;
+ int FACE_ACQUIRED_FACE_OBSCURED = 19;
/**
* This message represents the earliest message sent at the beginning of the authentication
@@ -310,12 +321,12 @@
* The framework will measure latency based on the time between the last START message and the
* onAuthenticated callback.
*/
- public static final int FACE_ACQUIRED_START = 20;
+ int FACE_ACQUIRED_START = 20;
/**
* The sensor is dirty. The user should be informed to clean the sensor.
*/
- public static final int FACE_ACQUIRED_SENSOR_DIRTY = 21;
+ int FACE_ACQUIRED_SENSOR_DIRTY = 21;
/**
* Hardware vendors may extend this list if there are conditions that do not fall under one of
@@ -323,10 +334,10 @@
*
* @hide
*/
- public static final int FACE_ACQUIRED_VENDOR = 22;
+ int FACE_ACQUIRED_VENDOR = 22;
/**
* @hide
*/
- public static final int FACE_ACQUIRED_VENDOR_BASE = 1000;
+ int FACE_ACQUIRED_VENDOR_BASE = 1000;
}
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index 5c74456..46e8cc0 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -18,6 +18,7 @@
import android.app.KeyguardManager;
import android.compat.annotation.UnsupportedAppUsage;
+import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.fingerprint.FingerprintManager;
/**
@@ -37,32 +38,32 @@
/**
* The hardware is unavailable. Try again later.
*/
- public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
+ int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
/**
* Error state returned when the sensor was unable to process the current image.
*/
- public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
+ int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2;
/**
* Error state returned when the current request has been running too long. This is intended to
* prevent programs from waiting for the fingerprint sensor indefinitely. The timeout is
* platform and sensor-specific, but is generally on the order of 30 seconds.
*/
- public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
+ int FINGERPRINT_ERROR_TIMEOUT = 3;
/**
* Error state returned for operations like enrollment; the operation cannot be completed
* because there's not enough storage remaining to complete the operation.
*/
- public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
+ int FINGERPRINT_ERROR_NO_SPACE = 4;
/**
* The operation was canceled because the fingerprint sensor is unavailable. For example,
* this may happen when the user is switched, the device is locked or another pending operation
* prevents or disables it.
*/
- public static final int FINGERPRINT_ERROR_CANCELED = 5;
+ int FINGERPRINT_ERROR_CANCELED = 5;
/**
* The {@link FingerprintManager#remove} call failed. Typically this will happen when the
@@ -70,13 +71,13 @@
*
* @hide
*/
- public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
+ int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
/**
* The operation was canceled because the API is locked out due to too many attempts.
* This occurs after 5 failed attempts, and lasts for 30 seconds.
*/
- public static final int FINGERPRINT_ERROR_LOCKOUT = 7;
+ int FINGERPRINT_ERROR_LOCKOUT = 7;
/**
* Hardware vendors may extend this list if there are conditions that do not fall under one of
@@ -86,52 +87,63 @@
* expected to show the error message string if they happen, but are advised not to rely on the
* message id since they will be device and vendor-specific
*/
- public static final int FINGERPRINT_ERROR_VENDOR = 8;
+ int FINGERPRINT_ERROR_VENDOR = 8;
/**
* The operation was canceled because FINGERPRINT_ERROR_LOCKOUT occurred too many times.
* Fingerprint authentication is disabled until the user unlocks with strong authentication
* (PIN/Pattern/Password)
*/
- public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9;
+ int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9;
/**
* The user canceled the operation. Upon receiving this, applications should use alternate
* authentication (e.g. a password). The application should also provide the means to return
* to fingerprint authentication, such as a "use fingerprint" button.
*/
- public static final int FINGERPRINT_ERROR_USER_CANCELED = 10;
+ int FINGERPRINT_ERROR_USER_CANCELED = 10;
/**
* The user does not have any fingerprints enrolled.
*/
- public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11;
+ int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11;
/**
* The device does not have a fingerprint sensor.
*/
- public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12;
+ int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12;
/**
* The user pressed the negative button. This is a placeholder that is currently only used
* by the support library.
+ *
* @hide
*/
- public static final int FINGERPRINT_ERROR_NEGATIVE_BUTTON = 13;
+ int FINGERPRINT_ERROR_NEGATIVE_BUTTON = 13;
/**
* The device does not have pin, pattern, or password set up. See
* {@link BiometricPrompt.Builder#setDeviceCredentialAllowed(boolean)} and
* {@link KeyguardManager#isDeviceSecure()}
+ *
* @hide
*/
- public static final int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14;
+ int BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL = 14;
+
+ /**
+ * A security vulnerability has been discovered and the sensor is unavailable until a
+ * security update has addressed this issue. This error can be received if for example,
+ * authentication was requested with {@link Authenticators#BIOMETRIC_STRONG}, but the
+ * sensor's strength can currently only meet {@link Authenticators#BIOMETRIC_WEAK}.
+ * @hide
+ */
+ public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED = 15;
/**
* @hide
*/
@UnsupportedAppUsage
- public static final int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
+ int FINGERPRINT_ERROR_VENDOR_BASE = 1000;
//
// Image acquisition messages. Must agree with those in fingerprint.h
@@ -140,19 +152,19 @@
/**
* The image acquired was good.
*/
- public static final int FINGERPRINT_ACQUIRED_GOOD = 0;
+ int FINGERPRINT_ACQUIRED_GOOD = 0;
/**
* Only a partial fingerprint image was detected. During enrollment, the user should be
* informed on what needs to happen to resolve this problem, e.g. "press firmly on sensor."
*/
- public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1;
+ int FINGERPRINT_ACQUIRED_PARTIAL = 1;
/**
* The fingerprint image was too noisy to process due to a detected condition (i.e. dry skin) or
* a possibly dirty sensor (See {@link #FINGERPRINT_ACQUIRED_IMAGER_DIRTY}).
*/
- public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
+ int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2;
/**
* The fingerprint image was too noisy due to suspected or detected dirt on the sensor.
@@ -161,13 +173,13 @@
* (stuck pixels, swaths, etc.). The user is expected to take action to clean the sensor
* when this is returned.
*/
- public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
+ int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3;
/**
* The fingerprint image was unreadable due to lack of motion. This is most appropriate for
* linear array sensors that require a swipe motion.
*/
- public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
+ int FINGERPRINT_ACQUIRED_TOO_SLOW = 4;
/**
* The fingerprint image was incomplete due to quick motion. While mostly appropriate for
@@ -175,16 +187,29 @@
* The user should be asked to move the finger slower (linear) or leave the finger on the sensor
* longer.
*/
- public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
+ int FINGERPRINT_ACQUIRED_TOO_FAST = 5;
/**
* Hardware vendors may extend this list if there are conditions that do not fall under one of
* the above categories. Vendors are responsible for providing error strings for these errors.
+ *
* @hide
*/
- public static final int FINGERPRINT_ACQUIRED_VENDOR = 6;
+ int FINGERPRINT_ACQUIRED_VENDOR = 6;
+
+ /**
+ * This message represents the earliest message sent at the beginning of the authentication
+ * pipeline. It is expected to be used to measure latency. Note this should be sent whenever
+ * authentication is restarted.
+ * The framework will measure latency based on the time between the last START message and the
+ * onAuthenticated callback.
+ *
+ * @hide
+ */
+ int FINGERPRINT_ACQUIRED_START = 7;
+
/**
* @hide
*/
- public static final int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
+ int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
}
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index 125b676..7d66cae 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -61,10 +61,20 @@
public static final int BIOMETRIC_ERROR_NO_HARDWARE =
BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
+ /**
+ * A security vulnerability has been discovered and the sensor is unavailable until a
+ * security update has addressed this issue. This error can be received if for example,
+ * authentication was requested with {@link Authenticators#BIOMETRIC_STRONG}, but the
+ * sensor's strength can currently only meet {@link Authenticators#BIOMETRIC_WEAK}.
+ */
+ public static final int BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED =
+ BiometricConstants.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED;
+
@IntDef({BIOMETRIC_SUCCESS,
BIOMETRIC_ERROR_HW_UNAVAILABLE,
BIOMETRIC_ERROR_NONE_ENROLLED,
- BIOMETRIC_ERROR_NO_HARDWARE})
+ BIOMETRIC_ERROR_NO_HARDWARE,
+ BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED})
@interface BiometricError {}
/**
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index f6717c7..ea576bc 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -918,7 +918,9 @@
if (mEnrollmentCallback != null) {
mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg);
} else if (mAuthenticationCallback != null) {
- mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
+ if (acquireInfo != BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START) {
+ mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
+ }
}
}
@@ -1050,6 +1052,9 @@
return msgArray[vendorCode];
}
}
+ break;
+ case FINGERPRINT_ACQUIRED_START:
+ return null;
}
Slog.w(TAG, "Invalid acquired message: " + acquireInfo + ", " + vendorCode);
return null;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index b202053..6d1f646 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -34,6 +34,7 @@
import android.app.Activity;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
+import android.app.PropertyInvalidatedCache;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -2150,16 +2151,38 @@
return isUserUnlocked(user.getIdentifier());
}
+ private static final String CACHE_KEY_IS_USER_UNLOCKED_PROPERTY =
+ "cache_key.is_user_unlocked";
+
+ private final PropertyInvalidatedCache<Integer, Boolean> mIsUserUnlockedCache =
+ new PropertyInvalidatedCache<Integer, Boolean>(
+ 32, CACHE_KEY_IS_USER_UNLOCKED_PROPERTY) {
+ @Override
+ protected Boolean recompute(Integer query) {
+ try {
+ return mService.isUserUnlocked(query);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+ };
+
/** {@hide} */
@UnsupportedAppUsage
@RequiresPermission(anyOf = {Manifest.permission.MANAGE_USERS,
Manifest.permission.INTERACT_ACROSS_USERS}, conditional = true)
public boolean isUserUnlocked(@UserIdInt int userId) {
- try {
- return mService.isUserUnlocked(userId);
- } catch (RemoteException re) {
- throw re.rethrowFromSystemServer();
- }
+ return mIsUserUnlockedCache.query(userId);
+ }
+
+ /** {@hide} */
+ public void disableIsUserUnlockedCache() {
+ mIsUserUnlockedCache.disableLocal();
+ }
+
+ /** {@hide} */
+ public static final void invalidateIsUserUnlockedCache() {
+ PropertyInvalidatedCache.invalidateCache(CACHE_KEY_IS_USER_UNLOCKED_PROPERTY);
}
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9944fb9..713cfc6 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10222,7 +10222,9 @@
*
* Type: int (0 for false, 1 for true)
* @hide
+ * @deprecated Use {@link WifiManager#isAutoWakeupEnabled()} instead.
*/
+ @Deprecated
@SystemApi
public static final String WIFI_WAKEUP_ENABLED = "wifi_wakeup_enabled";
@@ -10258,7 +10260,6 @@
* enabled state.
* @hide
*/
- @SystemApi
public static final String NETWORK_RECOMMENDATIONS_ENABLED =
"network_recommendations_enabled";
diff --git a/core/java/android/view/AccessibilityEmbeddedConnection.java b/core/java/android/view/AccessibilityEmbeddedConnection.java
new file mode 100644
index 0000000..cc1e501
--- /dev/null
+++ b/core/java/android/view/AccessibilityEmbeddedConnection.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 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.view;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Matrix;
+import android.os.IBinder;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityEmbeddedConnection;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * This class is an interface this ViewRootImpl provides to the host view to the latter
+ * can interact with the view hierarchy in SurfaceControlViewHost.
+ *
+ * @hide
+ */
+final class AccessibilityEmbeddedConnection extends IAccessibilityEmbeddedConnection.Stub {
+ private final WeakReference<ViewRootImpl> mViewRootImpl;
+
+ AccessibilityEmbeddedConnection(ViewRootImpl viewRootImpl) {
+ mViewRootImpl = new WeakReference<>(viewRootImpl);
+ }
+
+ @Override
+ public @Nullable IBinder associateEmbeddedHierarchy(@NonNull IBinder host, int hostViewId) {
+ final ViewRootImpl viewRootImpl = mViewRootImpl.get();
+ if (viewRootImpl != null) {
+ final AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(
+ viewRootImpl.mContext);
+ viewRootImpl.mAttachInfo.mLeashedParentToken = host;
+ viewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId = hostViewId;
+ if (accessibilityManager.isEnabled()) {
+ accessibilityManager.associateEmbeddedHierarchy(host, viewRootImpl.mLeashToken);
+ }
+ return viewRootImpl.mLeashToken;
+ }
+ return null;
+ }
+
+ @Override
+ public void disassociateEmbeddedHierarchy() {
+ final ViewRootImpl viewRootImpl = mViewRootImpl.get();
+ if (viewRootImpl != null) {
+ final AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(
+ viewRootImpl.mContext);
+ viewRootImpl.mAttachInfo.mLeashedParentToken = null;
+ viewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId = View.NO_ID;
+ viewRootImpl.mAttachInfo.mLocationInParentDisplay.set(0, 0);
+ if (accessibilityManager.isEnabled()) {
+ accessibilityManager.disassociateEmbeddedHierarchy(viewRootImpl.mLeashToken);
+ }
+ }
+ }
+
+ @Override
+ public void setScreenMatrix(float[] matrixValues) {
+ final ViewRootImpl viewRootImpl = mViewRootImpl.get();
+ if (viewRootImpl != null) {
+ // TODO(b/148821260): Implement the rest of matrix values.
+ viewRootImpl.mAttachInfo.mLocationInParentDisplay.set(
+ (int) matrixValues[Matrix.MTRANS_X], (int) matrixValues[Matrix.MTRANS_Y]);
+ }
+ }
+}
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 203b087..3ca84c1 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -855,6 +855,36 @@
return mViewRootImpl.mAttachInfo.mLocationInParentDisplay.equals(0, 0);
}
+ private void associateLeashedParentIfNeeded(List<AccessibilityNodeInfo> infos) {
+ if (infos == null || shouldBypassAssociateLeashedParent()) {
+ return;
+ }
+ final int infoCount = infos.size();
+ for (int i = 0; i < infoCount; i++) {
+ final AccessibilityNodeInfo info = infos.get(i);
+ associateLeashedParentIfNeeded(info);
+ }
+ }
+
+ private void associateLeashedParentIfNeeded(AccessibilityNodeInfo info) {
+ if (info == null || shouldBypassAssociateLeashedParent()) {
+ return;
+ }
+ // The node id of root node in embedded maybe not be ROOT_NODE_ID so we compare the id
+ // with root view.
+ if (mViewRootImpl.mView.getAccessibilityViewId()
+ != AccessibilityNodeInfo.getAccessibilityViewId(info.getSourceNodeId())) {
+ return;
+ }
+ info.setLeashedParent(mViewRootImpl.mAttachInfo.mLeashedParentToken,
+ mViewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId);
+ }
+
+ private boolean shouldBypassAssociateLeashedParent() {
+ return (mViewRootImpl.mAttachInfo.mLeashedParentToken == null
+ && mViewRootImpl.mAttachInfo.mLeashedParentAccessibilityViewId == View.NO_ID);
+ }
+
private void applyAppScaleAndMagnificationSpecIfNeeded(AccessibilityNodeInfo info,
MagnificationSpec spec) {
if (info == null) {
@@ -914,6 +944,7 @@
MagnificationSpec spec, Region interactiveRegion) {
try {
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ associateLeashedParentIfNeeded(infos);
adjustBoundsInScreenIfNeeded(infos);
// To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node,
// then impact the visibility result, we need to adjust visibility before apply scale.
@@ -935,6 +966,7 @@
MagnificationSpec spec, Region interactiveRegion) {
try {
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
+ associateLeashedParentIfNeeded(info);
adjustBoundsInScreenIfNeeded(info);
// To avoid applyAppScaleAndMagnificationSpecIfNeeded changing the bounds of node,
// then impact the visibility result, we need to adjust visibility before apply scale.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a6f8fad..f99c965 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -28989,6 +28989,18 @@
OnContentApplyWindowInsetsListener mContentOnApplyWindowInsetsListener;
/**
+ * The leash token of this view's parent when it's in an embedded hierarchy that is
+ * re-parented to another window.
+ */
+ IBinder mLeashedParentToken;
+
+ /**
+ * The accessibility view id of this view's parent when it's in an embedded
+ * hierarchy that is re-parented to another window.
+ */
+ int mLeashedParentAccessibilityViewId;
+
+ /**
* Creates a new set of attachment information with the specified
* events handler and thread.
*
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ebfe66f7..fa4fafa 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -135,6 +135,7 @@
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityWindowInfo;
+import android.view.accessibility.IAccessibilityEmbeddedConnection;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.animation.AccelerateDecelerateInterpolator;
@@ -355,6 +356,8 @@
final W mWindow;
+ final IBinder mLeashToken;
+
final int mTargetSdkVersion;
int mSeq;
@@ -652,6 +655,8 @@
private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
+ private IAccessibilityEmbeddedConnection mEmbeddedConnection;
+
static final class SystemUiVisibilityInfo {
int seq;
int globalVisibility;
@@ -685,6 +690,7 @@
mVisRect = new Rect();
mWinFrame = new Rect();
mWindow = new W(this);
+ mLeashToken = new Binder();
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
mViewVisibility = View.GONE;
mTransparentRegion = new Region();
@@ -9157,6 +9163,10 @@
focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
}
}
+ if (mAttachInfo.mLeashedParentToken != null) {
+ mAccessibilityManager.associateEmbeddedHierarchy(
+ mAttachInfo.mLeashedParentToken, mLeashToken);
+ }
} else {
ensureNoConnection();
mHandler.obtainMessage(MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST).sendToTarget();
@@ -9169,6 +9179,7 @@
if (!registered) {
mAttachInfo.mAccessibilityWindowId =
mAccessibilityManager.addAccessibilityInteractionConnection(mWindow,
+ mLeashToken,
mContext.getPackageName(),
new AccessibilityInteractionConnection(ViewRootImpl.this));
}
@@ -9355,6 +9366,17 @@
}
}
+ /**
+ * Gets an accessibility embedded connection interface for this ViewRootImpl.
+ * @hide
+ */
+ public IAccessibilityEmbeddedConnection getEmbeddedConnection() {
+ if (mEmbeddedConnection == null) {
+ mEmbeddedConnection = new AccessibilityEmbeddedConnection(ViewRootImpl.this);
+ }
+ return mEmbeddedConnection;
+ }
+
private class SendWindowContentChangedAccessibilityEvent implements Runnable {
private int mChangeTypes = 0;
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 02b098b..dc87453 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -1086,6 +1086,50 @@
}
/**
+ * Associate the connection between the host View and the embedded SurfaceControlViewHost.
+ *
+ * @hide
+ */
+ public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) {
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.associateEmbeddedHierarchy(host, embedded);
+ } catch (RemoteException e) {
+ return;
+ }
+ }
+
+ /**
+ * Disassociate the connection between the host View and the embedded SurfaceControlViewHost.
+ * The given token could be either from host side or embedded side.
+ *
+ * @hide
+ */
+ public void disassociateEmbeddedHierarchy(@NonNull IBinder token) {
+ if (token == null) {
+ return;
+ }
+ final IAccessibilityManager service;
+ synchronized (mLock) {
+ service = getServiceLocked();
+ if (service == null) {
+ return;
+ }
+ }
+ try {
+ service.disassociateEmbeddedHierarchy(token);
+ } catch (RemoteException e) {
+ return;
+ }
+ }
+
+ /**
* Sets the current state and notifies listeners, if necessary.
*
* @param stateFlags The state flags.
@@ -1147,11 +1191,12 @@
/**
* Adds an accessibility interaction connection interface for a given window.
* @param windowToken The window token to which a connection is added.
+ * @param leashToken The leash token to which a connection is added.
* @param connection The connection.
*
* @hide
*/
- public int addAccessibilityInteractionConnection(IWindow windowToken,
+ public int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken,
String packageName, IAccessibilityInteractionConnection connection) {
final IAccessibilityManager service;
final int userId;
@@ -1163,8 +1208,8 @@
userId = mUserId;
}
try {
- return service.addAccessibilityInteractionConnection(windowToken, connection,
- packageName, userId);
+ return service.addAccessibilityInteractionConnection(windowToken, leashToken,
+ connection, packageName, userId);
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
}
diff --git a/core/java/android/view/accessibility/IAccessibilityEmbeddedConnection.aidl b/core/java/android/view/accessibility/IAccessibilityEmbeddedConnection.aidl
new file mode 100644
index 0000000..707099e
--- /dev/null
+++ b/core/java/android/view/accessibility/IAccessibilityEmbeddedConnection.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 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.view.accessibility;
+
+/**
+ * Interface used by host View to talk to the view root of the embedded SurfaceControlViewHost
+ * that actually implements the functionality.
+ *
+ * @hide
+ */
+interface IAccessibilityEmbeddedConnection {
+
+ IBinder associateEmbeddedHierarchy(IBinder hostToken, int sourceId);
+
+ void disassociateEmbeddedHierarchy();
+
+ oneway void setScreenMatrix(in float[] matrixValues);
+}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 7f8fdf8..97036f3 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -47,7 +47,7 @@
@UnsupportedAppUsage
List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType, int userId);
- int addAccessibilityInteractionConnection(IWindow windowToken,
+ int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken,
in IAccessibilityInteractionConnection connection,
String packageName, int userId);
@@ -88,4 +88,8 @@
oneway void registerSystemAction(in RemoteAction action, int actionId);
oneway void unregisterSystemAction(int actionId);
oneway void setWindowMagnificationConnection(in IWindowMagnificationConnection connection);
+
+ void associateEmbeddedHierarchy(IBinder host, IBinder embedded);
+
+ void disassociateEmbeddedHierarchy(IBinder token);
}
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 8c52d1f..9e4ebfe 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -460,8 +460,19 @@
* @hide
*/
@UnsupportedAppUsage
- public WindowManager.LayoutParams getWindowParams() {
- return mTN.mParams;
+ @Nullable public WindowManager.LayoutParams getWindowParams() {
+ if (Compatibility.isChangeEnabled(CHANGE_TEXT_TOASTS_IN_THE_SYSTEM)) {
+ if (mNextView != null) {
+ // Custom toasts
+ return mTN.mParams;
+ } else {
+ // Text toasts
+ return null;
+ }
+ } else {
+ // Text and custom toasts are app-rendered
+ return mTN.mParams;
+ }
}
/**
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 3322834..086b9d8 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -377,6 +377,11 @@
public static final String NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN =
"nav_bar_handle_show_over_lockscreen";
+ /**
+ * (boolean) Whether to enable user-drag resizing for PIP.
+ */
+ public static final String PIP_USER_RESIZE = "pip_user_resize";
+
private SystemUiDeviceConfigFlags() {
}
}
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 2a56fd6d..0fd9cc7 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -27,8 +27,6 @@
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
#include <android_runtime/AndroidRuntime.h>
-#include <android_runtime/android_util_AssetManager.h>
-#include <androidfw/AssetManager2.h>
#include "Utils.h"
#include "FontUtils.h"
@@ -212,63 +210,6 @@
return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
}
-static void releaseAsset(const void* ptr, void* context) {
- delete static_cast<Asset*>(context);
-}
-
-static jboolean FontFamily_addFontFromAssetManager(JNIEnv* env, jobject, jlong builderPtr,
- jobject jassetMgr, jstring jpath, jint cookie, jboolean isAsset, jint ttcIndex,
- jint weight, jint isItalic) {
-#ifdef __ANDROID__ // Layoutlib does not support native AssetManager
- NPE_CHECK_RETURN_ZERO(env, jassetMgr);
- NPE_CHECK_RETURN_ZERO(env, jpath);
-
- NativeFamilyBuilder* builder = toNativeBuilder(builderPtr);
- Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, jassetMgr);
- if (NULL == mgr) {
- builder->axes.clear();
- return false;
- }
-
- ScopedUtfChars str(env, jpath);
- if (str.c_str() == nullptr) {
- builder->axes.clear();
- return false;
- }
-
- std::unique_ptr<Asset> asset;
- {
- ScopedLock<AssetManager2> locked_mgr(*mgr);
- if (isAsset) {
- asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
- } else if (cookie > 0) {
- // Valid java cookies are 1-based, but AssetManager cookies are 0-based.
- asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast<ApkAssetsCookie>(cookie - 1),
- Asset::ACCESS_BUFFER);
- } else {
- asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
- }
- }
-
- if (nullptr == asset) {
- builder->axes.clear();
- return false;
- }
-
- const void* buf = asset->getBuffer(false);
- if (NULL == buf) {
- builder->axes.clear();
- return false;
- }
-
- sk_sp<SkData> data(SkData::MakeWithProc(buf, asset->getLength(), releaseAsset,
- asset.release()));
- return addSkTypeface(builder, std::move(data), ttcIndex, weight, isItalic);
-#else
- return false;
-#endif
-}
-
static void FontFamily_addAxisValue(CRITICAL_JNI_PARAMS_COMMA jlong builderPtr, jint tag, jfloat value) {
NativeFamilyBuilder* builder = toNativeBuilder(builderPtr);
builder->axes.push_back({static_cast<minikin::AxisTag>(tag), value});
@@ -284,8 +225,6 @@
{ "nAddFont", "(JLjava/nio/ByteBuffer;III)Z", (void*)FontFamily_addFont },
{ "nAddFontWeightStyle", "(JLjava/nio/ByteBuffer;III)Z",
(void*)FontFamily_addFontWeightStyle },
- { "nAddFontFromAssetManager", "(JLandroid/content/res/AssetManager;Ljava/lang/String;IZIII)Z",
- (void*)FontFamily_addFontFromAssetManager },
{ "nAddAxisValue", "(JIF)V", (void*)FontFamily_addAxisValue },
};
diff --git a/core/jni/android/graphics/fonts/Font.cpp b/core/jni/android/graphics/fonts/Font.cpp
index 8d84e87..bfb9bae 100644
--- a/core/jni/android/graphics/fonts/Font.cpp
+++ b/core/jni/android/graphics/fonts/Font.cpp
@@ -26,8 +26,6 @@
#include "GraphicsJNI.h"
#include <nativehelper/ScopedUtfChars.h>
#include <android_runtime/AndroidRuntime.h>
-#include <android_runtime/android_util_AssetManager.h>
-#include <androidfw/AssetManager2.h>
#include "Utils.h"
#include "FontUtils.h"
@@ -48,14 +46,6 @@
return reinterpret_cast<NativeFontBuilder*>(ptr);
}
-static inline Asset* toAsset(jlong ptr) {
- return reinterpret_cast<Asset*>(ptr);
-}
-
-static void releaseAsset(jlong asset) {
- delete toAsset(asset);
-}
-
static void releaseFont(jlong font) {
delete reinterpret_cast<FontWrapper*>(font);
}
@@ -79,54 +69,6 @@
}
// Regular JNI
-static jlong Font_Builder_getNativeAsset(
- JNIEnv* env, jobject clazz, jobject assetMgr, jstring path, jboolean isAsset, jint cookie) {
-#ifdef __ANDROID__ // Layoutlib does not support native AssetManager
- NPE_CHECK_RETURN_ZERO(env, assetMgr);
- NPE_CHECK_RETURN_ZERO(env, path);
-
- Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, assetMgr);
- if (mgr == nullptr) {
- return 0;
- }
-
- ScopedUtfChars str(env, path);
- if (str.c_str() == nullptr) {
- return 0;
- }
-
- std::unique_ptr<Asset> asset;
- {
- ScopedLock<AssetManager2> locked_mgr(*mgr);
- if (isAsset) {
- asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
- } else if (cookie > 0) {
- // Valid java cookies are 1-based, but AssetManager cookies are 0-based.
- asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast<ApkAssetsCookie>(cookie - 1),
- Asset::ACCESS_BUFFER);
- } else {
- asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
- }
- }
-
- return reinterpret_cast<jlong>(asset.release());
-#else
- return 0;
-#endif
-}
-
-// Regular JNI
-static jobject Font_Builder_getAssetBuffer(JNIEnv* env, jobject clazz, jlong nativeAsset) {
- Asset* asset = toAsset(nativeAsset);
- return env->NewDirectByteBuffer(const_cast<void*>(asset->getBuffer(false)), asset->getLength());
-}
-
-// CriticalNative
-static jlong Font_Builder_getReleaseNativeAssetFunc(CRITICAL_JNI_PARAMS) {
- return reinterpret_cast<jlong>(&releaseAsset);
-}
-
-// Regular JNI
static jlong Font_Builder_initBuilder(JNIEnv*, jobject) {
return reinterpret_cast<jlong>(new NativeFontBuilder());
}
@@ -196,11 +138,6 @@
{ "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis },
{ "nBuild", "(JLjava/nio/ByteBuffer;Ljava/lang/String;IZI)J", (void*) Font_Builder_build },
{ "nGetReleaseNativeFont", "()J", (void*) Font_Builder_getReleaseNativeFont },
-
- { "nGetNativeAsset", "(Landroid/content/res/AssetManager;Ljava/lang/String;ZI)J",
- (void*) Font_Builder_getNativeAsset },
- { "nGetAssetBuffer", "(J)Ljava/nio/ByteBuffer;", (void*) Font_Builder_getAssetBuffer },
- { "nGetReleaseNativeAssetFunc", "()J", (void*) Font_Builder_getReleaseNativeAssetFunc },
};
int register_android_graphics_fonts_Font(JNIEnv* env) {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 92941b8..7a9a3f8 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -611,6 +611,12 @@
// Set the jemalloc decay time to 1.
mallopt(M_DECAY_TIME, 1);
+
+ // Maybe initialize GWP-ASan here. Must be called after
+ // mallopt(M_SET_ZYGOTE_CHILD).
+ bool ForceEnableGwpAsan = false;
+ android_mallopt(M_INITIALIZE_GWP_ASAN, &ForceEnableGwpAsan,
+ sizeof(ForceEnableGwpAsan));
}
static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) {
@@ -1490,6 +1496,11 @@
std::string mirrorCurPackageProfile = StringPrintf("/data_mirror/cur_profiles/%d/%s",
user_id, packageName.c_str());
+ if (access(mirrorCurPackageProfile.c_str(), F_OK) != 0) {
+ ALOGW("Can't access app profile directory: %s", mirrorCurPackageProfile.c_str());
+ continue;
+ }
+
PrepareDir(actualCurPackageProfile, DEFAULT_DATA_DIR_PERMISSION, uid, uid, fail_fn);
BindMount(mirrorCurPackageProfile, actualCurPackageProfile, fail_fn);
}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 20901e0..7d8b8db 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3790,6 +3790,12 @@
<attr name="description" />
<!-- Brief summary of the target of accessibility shortcut purpose or behavior. -->
<attr name="summary" />
+ <!-- Animated image of the target of accessibility shortcut purpose or behavior, to help
+ users understand how the target of accessibility shortcut can help them.-->
+ <attr name="animatedImageDrawable" format="reference"/>
+ <!-- Html description of the target of accessibility shortcut purpose or behavior, to help
+ users understand how the target of accessibility shortcut can help them. -->
+ <attr name="htmlDescription" format="string"/>
</declare-styleable>
<!-- Use <code>print-service</code> as the root tag of the XML resource that
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 939ccd0..8080412 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2383,7 +2383,7 @@
type. These are flags and can be freely combined.
0 - disable whitelist (install all system packages; no logging)
1 - enforce (only install system packages if they are whitelisted)
- 2 - log (log when a non-whitelisted package is run)
+ 2 - log (log non-whitelisted packages)
4 - any package not mentioned in the whitelist file is implicitly whitelisted on all users
8 - same as 4, but just for the SYSTEM user
16 - ignore OTAs (don't install system packages during OTAs)
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 718ca46..59335a5 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1330,12 +1330,6 @@
android:process=":FakeProvider">
</provider>
- <provider
- android:name="android.content.SlowProvider"
- android:authorities="android.content.SlowProvider"
- android:process=":SlowProvider">
- </provider>
-
<!-- Application components used for os tests -->
<service android:name="android.os.MessengerService"
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index f630188..21613a8 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -148,4 +148,7 @@
<!-- Summary of the accessibility shortcut [CHAR LIMIT=NONE] -->
<string name="accessibility_shortcut_summary">Accessibility shortcut summary</string>
+
+ <!-- Html description of the accessibility shortcut [CHAR LIMIT=NONE] -->
+ <string name="accessibility_shortcut_html_description">Accessibility shortcut html description</string>
</resources>
diff --git a/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml b/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml
index 60e2998..a597b71 100644
--- a/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml
+++ b/core/tests/coretests/res/xml/accessibility_shortcut_test_activity.xml
@@ -19,4 +19,6 @@
<accessibility-shortcut-target xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/accessibility_shortcut_description"
android:summary="@string/accessibility_shortcut_summary"
+ android:animatedImageDrawable="@drawable/bitmap_drawable"
+ android:htmlDescription="@string/accessibility_shortcut_html_description"
/>
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
index ae6d8df..82a7b2c 100644
--- a/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/AccessibilityShortcutInfoTest.java
@@ -84,6 +84,22 @@
}
@Test
+ public void testAnimatedImageRes() {
+ assertThat("Animated image resource id is not correct",
+ mShortcutInfo.getAnimatedImageRes(), is(R.drawable.bitmap_drawable));
+ }
+
+ @Test
+ public void testHtmlDescription() {
+ final String htmlDescription = mTargetContext.getResources()
+ .getString(R.string.accessibility_shortcut_html_description);
+
+ assertNotNull("Can't find html description string", htmlDescription);
+ assertThat("Html description is not correct",
+ mShortcutInfo.loadHtmlDescription(mPackageManager), is(htmlDescription));
+ }
+
+ @Test
public void testEquals() {
assertTrue(mShortcutInfo.equals(mShortcutInfo));
assertFalse(mShortcutInfo.equals(null));
diff --git a/core/tests/coretests/src/android/content/ContentResolverTest.java b/core/tests/coretests/src/android/content/ContentResolverTest.java
index 6dc7392..9dcce1e 100644
--- a/core/tests/coretests/src/android/content/ContentResolverTest.java
+++ b/core/tests/coretests/src/android/content/ContentResolverTest.java
@@ -209,13 +209,4 @@
String type = mResolver.getType(Uri.parse("content://android.content.FakeProviderRemote"));
assertEquals("fake/remote", type);
}
-
-
- @Test
- public void testGetType_slowProvider() {
- // This provider is running in a different process and is intentionally slow to start.
- // We are trying to confirm that it does not cause an ANR
- String type = mResolver.getType(Uri.parse("content://android.content.SlowProvider"));
- assertEquals("slow", type);
- }
}
diff --git a/core/tests/coretests/src/android/content/SlowProvider.java b/core/tests/coretests/src/android/content/SlowProvider.java
deleted file mode 100644
index aba32e8..0000000
--- a/core/tests/coretests/src/android/content/SlowProvider.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.content;
-
-import android.database.Cursor;
-import android.net.Uri;
-
-/**
- * A dummy content provider for tests. This provider runs in a different process from the test and
- * is intentionally slow.
- */
-public class SlowProvider extends ContentProvider {
-
- private static final int ON_CREATE_LATENCY_MILLIS = 3000;
-
- @Override
- public boolean onCreate() {
- try {
- Thread.sleep(ON_CREATE_LATENCY_MILLIS);
- } catch (InterruptedException e) {
- // Ignore
- }
- return true;
- }
-
- @Override
- public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
- String sortOrder) {
- return null;
- }
-
- @Override
- public String getType(Uri uri) {
- return "slow";
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- return null;
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- return 0;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- return 0;
- }
-}
diff --git a/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java
new file mode 100644
index 0000000..c897ace
--- /dev/null
+++ b/core/tests/coretests/src/android/content/integrity/InstallerAllowedByManifestFormulaTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.integrity;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.testng.annotations.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+public class InstallerAllowedByManifestFormulaTest {
+
+ private static final InstallerAllowedByManifestFormula
+ FORMULA = new InstallerAllowedByManifestFormula();
+
+ @Test
+ public void testFormulaMatches_installerAndCertBothInManifest() {
+ AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder()
+ .setInstallerName("installer1")
+ .setInstallerCertificates(Arrays.asList("installer_cert1", "random_cert"))
+ .setAllowedInstallersAndCert(ImmutableMap.of(
+ "installer1", "installer_cert1",
+ "installer2", "installer_cert2"
+ )).build();
+
+ assertThat(FORMULA.matches(appInstallMetadata)).isTrue();
+ }
+
+ @Test
+ public void testFormulaMatches_installerAndCertDoesNotMatchInManifest() {
+ AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder()
+ .setInstallerName("installer1")
+ .setInstallerCertificates(Arrays.asList("installer_cert1", "random_cert"))
+ .setAllowedInstallersAndCert(ImmutableMap.of(
+ "installer1", "installer_cert2",
+ "installer2", "installer_cert1"
+ )).build();
+
+ assertThat(FORMULA.matches(appInstallMetadata)).isFalse();
+ }
+
+ @Test
+ public void testFormulaMatches_installerNotInManifest() {
+ AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder()
+ .setInstallerName("installer3")
+ .setInstallerCertificates(Arrays.asList("installer_cert1", "random_cert"))
+ .setAllowedInstallersAndCert(ImmutableMap.of(
+ "installer1", "installer_cert2",
+ "installer2", "installer_cert1"
+ )).build();
+
+ assertThat(FORMULA.matches(appInstallMetadata)).isFalse();
+ }
+
+ @Test
+ public void testFormulaMatches_certificateNotInManifest() {
+ AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder()
+ .setInstallerName("installer1")
+ .setInstallerCertificates(Arrays.asList("installer_cert3", "random_cert"))
+ .setAllowedInstallersAndCert(ImmutableMap.of(
+ "installer1", "installer_cert2",
+ "installer2", "installer_cert1"
+ )).build();
+
+ assertThat(FORMULA.matches(appInstallMetadata)).isFalse();
+ }
+
+ @Test
+ public void testFormulaMatches_emptyManifest() {
+ AppInstallMetadata appInstallMetadata = getAppInstallMetadataBuilder()
+ .setInstallerName("installer1")
+ .setInstallerCertificates(Arrays.asList("installer_cert3", "random_cert"))
+ .setAllowedInstallersAndCert(ImmutableMap.of()).build();
+
+ assertThat(FORMULA.matches(appInstallMetadata)).isTrue();
+ }
+
+ /** Returns a builder with all fields filled with some dummy data. */
+ private AppInstallMetadata.Builder getAppInstallMetadataBuilder() {
+ return new AppInstallMetadata.Builder()
+ .setPackageName("abc")
+ .setAppCertificates(Collections.emptyList())
+ .setInstallerCertificates(Collections.emptyList())
+ .setInstallerName("abc")
+ .setVersionCode(-1)
+ .setIsPreInstalled(true);
+ }
+}
diff --git a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java
index 75ef1f2..62c9c98 100644
--- a/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java
+++ b/core/tests/coretests/src/android/content/integrity/IntegrityFormulaTest.java
@@ -20,8 +20,6 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.testng.Assert.assertThrows;
-
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -32,8 +30,7 @@
@Test
public void createEqualsFormula_packageName() {
String packageName = "com.test.app";
- IntegrityFormula formula =
- IntegrityFormula.PACKAGE_NAME.equalTo(packageName);
+ IntegrityFormula formula = IntegrityFormula.Application.packageNameEquals(packageName);
AtomicFormula.StringAtomicFormula stringAtomicFormula =
(AtomicFormula.StringAtomicFormula) formula;
@@ -46,8 +43,7 @@
@Test
public void createEqualsFormula_appCertificate() {
String appCertificate = "com.test.app";
- IntegrityFormula formula =
- IntegrityFormula.APP_CERTIFICATE.equalTo(appCertificate);
+ IntegrityFormula formula = IntegrityFormula.Application.certificatesContain(appCertificate);
AtomicFormula.StringAtomicFormula stringAtomicFormula =
(AtomicFormula.StringAtomicFormula) formula;
@@ -60,8 +56,7 @@
@Test
public void createEqualsFormula_installerName() {
String installerName = "com.test.app";
- IntegrityFormula formula =
- IntegrityFormula.INSTALLER_NAME.equalTo(installerName);
+ IntegrityFormula formula = IntegrityFormula.Installer.packageNameEquals(installerName);
AtomicFormula.StringAtomicFormula stringAtomicFormula =
(AtomicFormula.StringAtomicFormula) formula;
@@ -75,7 +70,7 @@
public void createEqualsFormula_installerCertificate() {
String installerCertificate = "com.test.app";
IntegrityFormula formula =
- IntegrityFormula.INSTALLER_CERTIFICATE.equalTo(installerCertificate);
+ IntegrityFormula.Installer.certificatesContain(installerCertificate);
AtomicFormula.StringAtomicFormula stringAtomicFormula =
(AtomicFormula.StringAtomicFormula) formula;
@@ -88,8 +83,7 @@
@Test
public void createEqualsFormula_versionCode() {
int versionCode = 12;
- IntegrityFormula formula =
- IntegrityFormula.VERSION_CODE.equalTo(versionCode);
+ IntegrityFormula formula = IntegrityFormula.Application.versionCodeEquals(versionCode);
AtomicFormula.LongAtomicFormula stringAtomicFormula =
(AtomicFormula.LongAtomicFormula) formula;
@@ -100,24 +94,9 @@
}
@Test
- public void createEqualsFormula_invalidKeyTypeForStringParameter() {
- assertThrows(
- IllegalArgumentException.class,
- () -> IntegrityFormula.PRE_INSTALLED.equalTo("wrongString"));
- }
-
- @Test
- public void createEqualsFormula_invalidKeyTypeForLongParameter() {
- assertThrows(
- IllegalArgumentException.class,
- () -> IntegrityFormula.PACKAGE_NAME.equalTo(12));
- }
-
- @Test
public void createGreaterThanFormula_versionCode() {
int versionCode = 12;
- IntegrityFormula formula =
- IntegrityFormula.VERSION_CODE.greaterThan(versionCode);
+ IntegrityFormula formula = IntegrityFormula.Application.versionCodeGreaterThan(versionCode);
AtomicFormula.LongAtomicFormula stringAtomicFormula =
(AtomicFormula.LongAtomicFormula) formula;
@@ -128,17 +107,10 @@
}
@Test
- public void createGreaterThanFormula_invalidKeyTypeForLongParameter() {
- assertThrows(
- IllegalArgumentException.class,
- () -> IntegrityFormula.PACKAGE_NAME.greaterThan(12));
- }
-
- @Test
public void createGreaterThanOrEqualsToFormula_versionCode() {
int versionCode = 12;
- IntegrityFormula formula =
- IntegrityFormula.VERSION_CODE.greaterThanOrEquals(versionCode);
+ IntegrityFormula formula = IntegrityFormula.Application.versionCodeGreaterThanOrEqualTo(
+ versionCode);
AtomicFormula.LongAtomicFormula stringAtomicFormula =
(AtomicFormula.LongAtomicFormula) formula;
@@ -149,15 +121,8 @@
}
@Test
- public void createGreaterThanOrEqualsToFormula_invalidKeyTypeForLongParameter() {
- assertThrows(
- IllegalArgumentException.class,
- () -> IntegrityFormula.PACKAGE_NAME.greaterThanOrEquals(12));
- }
-
- @Test
public void createIsTrueFormula_preInstalled() {
- IntegrityFormula formula = IntegrityFormula.PRE_INSTALLED.equalTo(true);
+ IntegrityFormula formula = IntegrityFormula.Application.isPreInstalled();
AtomicFormula.BooleanAtomicFormula stringAtomicFormula =
(AtomicFormula.BooleanAtomicFormula) formula;
@@ -167,20 +132,12 @@
}
@Test
- public void createIsTrueFormula_invalidKeyTypeForBoolParameter() {
- assertThrows(
- IllegalArgumentException.class,
- () -> IntegrityFormula.PACKAGE_NAME.equalTo(true));
- }
-
- @Test
public void createAllFormula() {
String packageName = "com.test.package";
String certificateName = "certificate";
- IntegrityFormula formula1 =
- IntegrityFormula.PACKAGE_NAME.equalTo(packageName);
- IntegrityFormula formula2 =
- IntegrityFormula.APP_CERTIFICATE.equalTo(certificateName);
+ IntegrityFormula formula1 = IntegrityFormula.Application.packageNameEquals(packageName);
+ IntegrityFormula formula2 = IntegrityFormula.Application.certificatesContain(
+ certificateName);
IntegrityFormula compoundFormula = IntegrityFormula.all(formula1, formula2);
@@ -191,10 +148,9 @@
public void createAnyFormula() {
String packageName = "com.test.package";
String certificateName = "certificate";
- IntegrityFormula formula1 =
- IntegrityFormula.PACKAGE_NAME.equalTo(packageName);
- IntegrityFormula formula2 =
- IntegrityFormula.APP_CERTIFICATE.equalTo(certificateName);
+ IntegrityFormula formula1 = IntegrityFormula.Application.packageNameEquals(packageName);
+ IntegrityFormula formula2 = IntegrityFormula.Application.certificatesContain(
+ certificateName);
IntegrityFormula compoundFormula = IntegrityFormula.any(formula1, formula2);
@@ -206,8 +162,7 @@
String packageName = "com.test.package";
IntegrityFormula compoundFormula =
- IntegrityFormula.not(
- IntegrityFormula.PACKAGE_NAME.equalTo(packageName));
+ IntegrityFormula.not(IntegrityFormula.Application.packageNameEquals(packageName));
assertThat(compoundFormula.getTag()).isEqualTo(COMPOUND_FORMULA_TAG);
}
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index 447f043..f50de16 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.res.AssetManager;
+import android.graphics.fonts.Font;
import android.graphics.fonts.FontVariationAxis;
import android.os.Build;
import android.text.TextUtils;
@@ -195,18 +196,13 @@
if (mBuilderPtr == 0) {
throw new IllegalStateException("Unable to call addFontFromAsset after freezing.");
}
- if (axes != null) {
- for (FontVariationAxis axis : axes) {
- nAddAxisValue(mBuilderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue());
- }
- }
- return nAddFontFromAssetManager(mBuilderPtr, mgr, path, cookie, isAsset, ttcIndex, weight,
- isItalic);
- }
- // TODO: Remove once internal user stop using private API.
- private static boolean nAddFont(long builderPtr, ByteBuffer font, int ttcIndex) {
- return nAddFont(builderPtr, font, ttcIndex, -1, -1);
+ try {
+ ByteBuffer buffer = Font.Builder.createBuffer(mgr, path, isAsset, cookie);
+ return addFontFromBuffer(buffer, ttcIndex, axes, weight, isItalic);
+ } catch (IOException e) {
+ return false;
+ }
}
private static native long nInitBuilder(String langs, int variant);
@@ -225,8 +221,6 @@
int weight, int isItalic);
private static native boolean nAddFontWeightStyle(long builderPtr, ByteBuffer font,
int ttcIndex, int weight, int isItalic);
- private static native boolean nAddFontFromAssetManager(long builderPtr, AssetManager mgr,
- String path, int cookie, boolean isAsset, int ttcIndex, int weight, int isItalic);
// The added axis values are only valid for the next nAddFont* method call.
@CriticalNative
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index ba96a06..4899fbe 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -19,6 +19,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.LocaleList;
@@ -35,7 +36,9 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.Objects;
@@ -54,10 +57,6 @@
* A builder class for creating new Font.
*/
public static final class Builder {
- private static final NativeAllocationRegistry sAssetByteBufferRegistry =
- NativeAllocationRegistry.createMalloced(ByteBuffer.class.getClassLoader(),
- nGetReleaseNativeAssetFunc());
-
private static final NativeAllocationRegistry sFontRegistry =
NativeAllocationRegistry.createMalloced(Font.class.getClassLoader(),
nGetReleaseNativeFont());
@@ -151,7 +150,11 @@
* @param path the file name of the font data in the asset directory
*/
public Builder(@NonNull AssetManager am, @NonNull String path) {
- this(am, path, true /* is asset */, 0 /* cookie */);
+ try {
+ mBuffer = createBuffer(am, path, true /* is asset */, 0 /* cookie */);
+ } catch (IOException e) {
+ mException = e;
+ }
}
/**
@@ -165,18 +168,11 @@
*/
public Builder(@NonNull AssetManager am, @NonNull String path, boolean isAsset,
int cookie) {
- final long nativeAsset = nGetNativeAsset(am, path, isAsset, cookie);
- if (nativeAsset == 0) {
- mException = new FileNotFoundException("Unable to open " + path);
- return;
+ try {
+ mBuffer = createBuffer(am, path, isAsset, cookie);
+ } catch (IOException e) {
+ mException = e;
}
- final ByteBuffer b = nGetAssetBuffer(nativeAsset);
- sAssetByteBufferRegistry.registerNativeAllocation(b, nativeAsset);
- if (b == null) {
- mException = new FileNotFoundException(path + " not found");
- return;
- }
- mBuffer = b;
}
/**
@@ -199,19 +195,64 @@
mException = new FileNotFoundException(resId + " must be font file.");
return;
}
- final long nativeAsset = nGetNativeAsset(res.getAssets(), str, false /* is asset */,
- value.assetCookie);
- if (nativeAsset == 0) {
- mException = new FileNotFoundException("Unable to open " + str);
- return;
+
+ try {
+ mBuffer = createBuffer(res.getAssets(), str, false, value.assetCookie);
+ } catch (IOException e) {
+ mException = e;
}
- final ByteBuffer b = nGetAssetBuffer(nativeAsset);
- sAssetByteBufferRegistry.registerNativeAllocation(b, nativeAsset);
- if (b == null) {
- mException = new FileNotFoundException(str + " not found");
- return;
+ }
+
+ /**
+ * Creates a buffer containing font data using the assetManager and other
+ * provided inputs.
+ *
+ * @param am the application's asset manager
+ * @param path the file name of the font data in the asset directory
+ * @param isAsset true if the undelying data is in asset
+ * @param cookie set asset cookie
+ * @return buffer containing the contents of the file
+ *
+ * @hide
+ */
+ public static ByteBuffer createBuffer(@NonNull AssetManager am, @NonNull String path,
+ boolean isAsset, int cookie) throws IOException {
+ Preconditions.checkNotNull(am, "assetManager can not be null");
+ Preconditions.checkNotNull(path, "path can not be null");
+
+ if (!isAsset) {
+ // Attempt to open as FD, which should work unless the asset is compressed
+ AssetFileDescriptor assetFD;
+ try {
+ if (cookie > 0) {
+ assetFD = am.openNonAssetFd(cookie, path);
+ } else {
+ assetFD = am.openNonAssetFd(path);
+ }
+
+ try (FileInputStream fis = assetFD.createInputStream()) {
+ final FileChannel fc = fis.getChannel();
+ return fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
+ }
+ } catch (IOException e) {
+ // failed to open as FD so now we will attempt to open as an input stream
+ }
}
- mBuffer = b;
+
+ try (InputStream assetStream = isAsset ? am.open(path, AssetManager.ACCESS_BUFFER)
+ : am.openNonAsset(cookie, path, AssetManager.ACCESS_BUFFER)) {
+
+ int capacity = assetStream.available();
+ ByteBuffer buffer = ByteBuffer.allocateDirect(capacity);
+ buffer.order(ByteOrder.nativeOrder());
+ assetStream.read(buffer.array(), buffer.arrayOffset(), assetStream.available());
+
+ if (assetStream.read() != -1) {
+ throw new IOException("Unable to access full contents of " + path);
+ }
+
+ return buffer;
+ }
}
/**
@@ -396,15 +437,6 @@
}
/**
- * Native methods for accessing underlying buffer in Asset
- */
- private static native long nGetNativeAsset(
- @NonNull AssetManager am, @NonNull String path, boolean isAsset, int cookie);
- private static native ByteBuffer nGetAssetBuffer(long nativeAsset);
- @CriticalNative
- private static native long nGetReleaseNativeAssetFunc();
-
- /**
* Native methods for creating Font
*/
private static native long nInitBuilder();
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index a2d2355..8dbb5f5 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -206,6 +206,7 @@
blockModes,
userAuthenticationRequired,
(int) userAuthenticationValidityDurationSeconds,
+ keymasterHwEnforcedUserAuthenticators,
userAuthenticationRequirementEnforcedBySecureHardware,
userAuthenticationValidWhileOnBody,
trustedUserPresenceRequred,
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 450dd33..d683041 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -263,6 +263,7 @@
private final boolean mRandomizedEncryptionRequired;
private final boolean mUserAuthenticationRequired;
private final int mUserAuthenticationValidityDurationSeconds;
+ private final @KeyProperties.AuthEnum int mUserAuthenticationType;
private final boolean mUserPresenceRequired;
private final byte[] mAttestationChallenge;
private final boolean mUniqueIdIncluded;
@@ -301,6 +302,7 @@
boolean randomizedEncryptionRequired,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds,
+ @KeyProperties.AuthEnum int userAuthenticationType,
boolean userPresenceRequired,
byte[] attestationChallenge,
boolean uniqueIdIncluded,
@@ -352,6 +354,7 @@
mUserAuthenticationRequired = userAuthenticationRequired;
mUserPresenceRequired = userPresenceRequired;
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+ mUserAuthenticationType = userAuthenticationType;
mAttestationChallenge = Utils.cloneIfNotNull(attestationChallenge);
mUniqueIdIncluded = uniqueIdIncluded;
mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
@@ -605,6 +608,22 @@
}
/**
+ * Gets the modes of authentication that can authorize use of this key. This has effect only if
+ * user authentication is required (see {@link #isUserAuthenticationRequired()}).
+ *
+ * <p>This authorization applies only to secret key and private key operations. Public key
+ * operations are not restricted.
+ *
+ * @return integer representing the bitwse OR of all acceptable authentication types for the
+ * key.
+ *
+ * @see #isUserAuthenticationRequired()
+ * @see Builder#setUserAuthenticationParameters(int, int)
+ */
+ public @KeyProperties.AuthEnum int getUserAuthenticationType() {
+ return mUserAuthenticationType;
+ }
+ /**
* Returns {@code true} if the key is authorized to be used only if a test of user presence has
* been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls.
* It requires that the KeyStore implementation have a direct way to validate the user presence
@@ -746,6 +765,7 @@
private boolean mRandomizedEncryptionRequired = true;
private boolean mUserAuthenticationRequired;
private int mUserAuthenticationValidityDurationSeconds = -1;
+ private @KeyProperties.AuthEnum int mUserAuthenticationType;
private boolean mUserPresenceRequired = false;
private byte[] mAttestationChallenge = null;
private boolean mUniqueIdIncluded = false;
@@ -810,6 +830,7 @@
mUserAuthenticationRequired = sourceSpec.isUserAuthenticationRequired();
mUserAuthenticationValidityDurationSeconds =
sourceSpec.getUserAuthenticationValidityDurationSeconds();
+ mUserAuthenticationType = sourceSpec.getUserAuthenticationType();
mUserPresenceRequired = sourceSpec.isUserPresenceRequired();
mAttestationChallenge = sourceSpec.getAttestationChallenge();
mUniqueIdIncluded = sourceSpec.isUniqueIdIncluded();
@@ -1207,14 +1228,62 @@
* @see BiometricPrompt
* @see BiometricPrompt.CryptoObject
* @see KeyguardManager
+ * @deprecated See {@link #setUserAuthenticationParameters(int, int)}
*/
+ @Deprecated
@NonNull
public Builder setUserAuthenticationValidityDurationSeconds(
@IntRange(from = -1) int seconds) {
if (seconds < -1) {
throw new IllegalArgumentException("seconds must be -1 or larger");
}
- mUserAuthenticationValidityDurationSeconds = seconds;
+ if (seconds == -1) {
+ return setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG);
+ }
+ return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_BIOMETRIC_STRONG);
+ }
+
+ /**
+ * Sets the duration of time (seconds) and authorization type for which this key is
+ * authorized to be used after the user is successfully authenticated. This has effect if
+ * the key requires user authentication for its use (see
+ * {@link #setUserAuthenticationRequired(boolean)}).
+ *
+ * <p>By default, if user authentication is required, it must take place for every use of
+ * the key.
+ *
+ * <p>These cryptographic operations will throw {@link UserNotAuthenticatedException} during
+ * initialization if the user needs to be authenticated to proceed. This situation can be
+ * resolved by the user authenticating with the appropriate biometric or credential as
+ * required by the key. See {@link BiometricPrompt.Builder#setAllowedAuthenticators(int)}
+ * and {@link BiometricManager.Authenticators}.
+ *
+ * <p>Once resolved, initializing a new cryptographic operation using this key (or any other
+ * key which is authorized to be used for a fixed duration of time after user
+ * authentication) should succeed provided the user authentication flow completed
+ * successfully.
+ *
+ * @param timeout duration in seconds or {@code 0} if user authentication must take place
+ * for every use of the key. {@code -1} is also accepted for legacy purposes. It is
+ * functionally the same as {@code 0}.
+ * @param type set of authentication types which can authorize use of the key. See
+ * {@link KeyProperties}.{@code AUTH} flags.
+ *
+ * @see #setUserAuthenticationRequired(boolean)
+ * @see BiometricPrompt
+ * @see BiometricPrompt.CryptoObject
+ * @see KeyguardManager
+ */
+ @NonNull
+ public Builder setUserAuthenticationParameters(@IntRange(from = -1) int timeout,
+ @KeyProperties.AuthEnum int type) {
+ if (timeout < -1) {
+ throw new IllegalArgumentException("timeout must be -1 or larger");
+ } else if (timeout == -1) {
+ timeout = 0;
+ }
+ mUserAuthenticationValidityDurationSeconds = timeout;
+ mUserAuthenticationType = type;
return this;
}
@@ -1392,6 +1461,7 @@
mRandomizedEncryptionRequired,
mUserAuthenticationRequired,
mUserAuthenticationValidityDurationSeconds,
+ mUserAuthenticationType,
mUserPresenceRequired,
mAttestationChallenge,
mUniqueIdIncluded,
diff --git a/keystore/java/android/security/keystore/KeyInfo.java b/keystore/java/android/security/keystore/KeyInfo.java
index 0a75cd5..d891a25 100644
--- a/keystore/java/android/security/keystore/KeyInfo.java
+++ b/keystore/java/android/security/keystore/KeyInfo.java
@@ -78,6 +78,7 @@
private final @KeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mUserAuthenticationRequired;
private final int mUserAuthenticationValidityDurationSeconds;
+ private final @KeyProperties.AuthEnum int mUserAuthenticationType;
private final boolean mUserAuthenticationRequirementEnforcedBySecureHardware;
private final boolean mUserAuthenticationValidWhileOnBody;
private final boolean mTrustedUserPresenceRequired;
@@ -101,6 +102,7 @@
@KeyProperties.BlockModeEnum String[] blockModes,
boolean userAuthenticationRequired,
int userAuthenticationValidityDurationSeconds,
+ @KeyProperties.AuthEnum int userAuthenticationType,
boolean userAuthenticationRequirementEnforcedBySecureHardware,
boolean userAuthenticationValidWhileOnBody,
boolean trustedUserPresenceRequired,
@@ -122,6 +124,7 @@
mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
mUserAuthenticationRequired = userAuthenticationRequired;
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+ mUserAuthenticationType = userAuthenticationType;
mUserAuthenticationRequirementEnforcedBySecureHardware =
userAuthenticationRequirementEnforcedBySecureHardware;
mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
@@ -301,6 +304,22 @@
}
/**
+ * Gets the acceptable user authentication types for which this key can be authorized to be
+ * used. This has effect only if user authentication is required (see
+ * {@link #isUserAuthenticationRequired()}).
+ *
+ * <p>This authorization applies only to secret key and private key operations. Public key
+ * operations are not restricted.
+ *
+ * @return integer representing the accepted forms of user authentication for this key
+ *
+ * @see #isUserAuthenticationRequired()
+ */
+ public @KeyProperties.AuthEnum int getUserAuthenticationType() {
+ return mUserAuthenticationType;
+ }
+
+ /**
* Returns {@code true} if the requirement that this key can only be used if the user has been
* authenticated is enforced by secure hardware (e.g., Trusted Execution Environment (TEE) or
* Secure Element (SE)).
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index f12a659..c58a123 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -39,6 +39,27 @@
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = { "AUTH_" }, value = {
+ AUTH_BIOMETRIC_STRONG,
+ AUTH_DEVICE_CREDENTIAL,
+ })
+ public @interface AuthEnum {}
+
+ /**
+ * The non-biometric credential used to secure the device (i.e., PIN, pattern, or password)
+ */
+ public static final int AUTH_DEVICE_CREDENTIAL = 1 << 0;
+
+ /**
+ * Any biometric (e.g. fingerprint, iris, or face) on the device that meets or exceeds the
+ * requirements for <strong>Strong</strong>, as defined by the Android CDD.
+ */
+ public static final int AUTH_BIOMETRIC_STRONG = 1 << 1;
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = { "PURPOSE_" }, value = {
PURPOSE_ENCRYPT,
PURPOSE_DECRYPT,
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 26181a6..e230b7c 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -225,6 +225,7 @@
private final @KeyProperties.BlockModeEnum String[] mBlockModes;
private final boolean mRandomizedEncryptionRequired;
private final boolean mUserAuthenticationRequired;
+ private final @KeyProperties.AuthEnum int mUserAuthenticationType;
private final int mUserAuthenticationValidityDurationSeconds;
private final boolean mUserPresenceRequred;
private final boolean mUserAuthenticationValidWhileOnBody;
@@ -246,6 +247,7 @@
@KeyProperties.BlockModeEnum String[] blockModes,
boolean randomizedEncryptionRequired,
boolean userAuthenticationRequired,
+ @KeyProperties.AuthEnum int userAuthenticationType,
int userAuthenticationValidityDurationSeconds,
boolean userPresenceRequred,
boolean userAuthenticationValidWhileOnBody,
@@ -267,6 +269,7 @@
mBlockModes = ArrayUtils.cloneIfNotEmpty(ArrayUtils.nullToEmpty(blockModes));
mRandomizedEncryptionRequired = randomizedEncryptionRequired;
mUserAuthenticationRequired = userAuthenticationRequired;
+ mUserAuthenticationType = userAuthenticationType;
mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
mUserPresenceRequred = userPresenceRequred;
mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
@@ -429,6 +432,10 @@
return mUserConfirmationRequired;
}
+ public @KeyProperties.AuthEnum int getUserAuthenticationType() {
+ return mUserAuthenticationType;
+ }
+
/**
* Gets the duration of time (seconds) for which this key is authorized to be used after the
* user is successfully authenticated. This has effect only if user authentication is required
@@ -555,6 +562,7 @@
private @KeyProperties.BlockModeEnum String[] mBlockModes;
private boolean mRandomizedEncryptionRequired = true;
private boolean mUserAuthenticationRequired;
+ private @KeyProperties.AuthEnum int mUserAuthenticationType;
private int mUserAuthenticationValidityDurationSeconds = -1;
private boolean mUserPresenceRequired = false;
private boolean mUserAuthenticationValidWhileOnBody;
@@ -850,14 +858,62 @@
* @see BiometricPrompt
* @see BiometricPrompt.CryptoObject
* @see KeyguardManager
+ * @deprecated See {@link #setUserAuthenticationParameters(int, int)}
*/
+ @Deprecated
@NonNull
public Builder setUserAuthenticationValidityDurationSeconds(
@IntRange(from = -1) int seconds) {
if (seconds < -1) {
throw new IllegalArgumentException("seconds must be -1 or larger");
}
- mUserAuthenticationValidityDurationSeconds = seconds;
+ if (seconds == -1) {
+ return setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG);
+ }
+ return setUserAuthenticationParameters(seconds, KeyProperties.AUTH_BIOMETRIC_STRONG);
+ }
+
+ /**
+ * Sets the duration of time (seconds) and authorization type for which this key is
+ * authorized to be used after the user is successfully authenticated. This has effect if
+ * the key requires user authentication for its use (see
+ * {@link #setUserAuthenticationRequired(boolean)}).
+ *
+ * <p>By default, if user authentication is required, it must take place for every use of
+ * the key.
+ *
+ * <p>These cryptographic operations will throw {@link UserNotAuthenticatedException} during
+ * initialization if the user needs to be authenticated to proceed. This situation can be
+ * resolved by the user authenticating with the appropriate biometric or credential as
+ * required by the key. See {@link BiometricPrompt.Builder#setAllowedAuthenticators(int)}
+ * and {@link BiometricManager.Authenticators}.
+ *
+ * <p>Once resolved, initializing a new cryptographic operation using this key (or any other
+ * key which is authorized to be used for a fixed duration of time after user
+ * authentication) should succeed provided the user authentication flow completed
+ * successfully.
+ *
+ * @param timeout duration in seconds or {@code 0} if user authentication must take place
+ * for every use of the key. {@code -1} is also accepted for legacy purposes. It is
+ * functionally the same as {@code 0}.
+ * @param type set of authentication types which can authorize use of the key. See
+ * {@link KeyProperties}.{@code AUTH} flags.
+ *
+ * @see #setUserAuthenticationRequired(boolean)
+ * @see BiometricPrompt
+ * @see BiometricPrompt.CryptoObject
+ * @see KeyguardManager
+ */
+ @NonNull
+ public Builder setUserAuthenticationParameters(@IntRange(from = -1) int timeout,
+ @KeyProperties.AuthEnum int type) {
+ if (timeout < -1) {
+ throw new IllegalArgumentException("timeout must be -1 or larger");
+ } else if (timeout == -1) {
+ timeout = 0;
+ }
+ mUserAuthenticationValidityDurationSeconds = timeout;
+ mUserAuthenticationType = type;
return this;
}
@@ -1002,6 +1058,7 @@
mBlockModes,
mRandomizedEncryptionRequired,
mUserAuthenticationRequired,
+ mUserAuthenticationType,
mUserAuthenticationValidityDurationSeconds,
mUserPresenceRequired,
mUserAuthenticationValidWhileOnBody,
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index 79e48cd..37b1f23 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -88,17 +88,9 @@
* Adds keymaster arguments to express the key's authorization policy supported by user
* authentication.
*
- * @param userAuthenticationRequired whether user authentication is required to authorize the
- * use of the key.
- * @param userAuthenticationValidityDurationSeconds duration of time (seconds) for which user
- * authentication is valid as authorization for using the key or {@code -1} if every
- * use of the key needs authorization.
- * @param boundToSpecificSecureUserId if non-zero, specify which SID the key will be bound to,
- * overriding the default logic in this method where the key is bound to either the root
- * SID of the current user, or the fingerprint SID if explicit fingerprint authorization
- * is requested.
- * @param userConfirmationRequired whether user confirmation is required to authorize the use
- * of the key.
+ * @param args The arguments sent to keymaster that need to be populated from the spec
+ * @param spec The user authentication relevant portions of the spec passed in from the caller.
+ * This spec will be translated into the relevant keymaster tags to be loaded into args.
* @throws IllegalStateException if user authentication is required but the system is in a wrong
* state (e.g., secure lock screen not set up) for generating or importing keys that
* require user authentication.
@@ -122,7 +114,7 @@
return;
}
- if (spec.getUserAuthenticationValidityDurationSeconds() == -1) {
+ if (spec.getUserAuthenticationValidityDurationSeconds() == 0) {
PackageManager pm = KeyStore.getApplicationContext().getPackageManager();
// Every use of this key needs to be authorized by the user. This currently means
// fingerprint or face auth.
@@ -168,7 +160,8 @@
args.addUnsignedLong(KeymasterDefs.KM_TAG_USER_SECURE_ID,
KeymasterArguments.toUint64(sids.get(i)));
}
- args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_BIOMETRIC);
+
+ args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, spec.getUserAuthenticationType());
if (spec.isUserAuthenticationValidWhileOnBody()) {
throw new ProviderException("Key validity extension while device is on-body is not "
diff --git a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
index 98e4589..9c9773e 100644
--- a/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/ParcelableKeyGenParameterSpec.java
@@ -97,6 +97,7 @@
out.writeBoolean(mSpec.isRandomizedEncryptionRequired());
out.writeBoolean(mSpec.isUserAuthenticationRequired());
out.writeInt(mSpec.getUserAuthenticationValidityDurationSeconds());
+ out.writeInt(mSpec.getUserAuthenticationType());
out.writeBoolean(mSpec.isUserPresenceRequired());
out.writeByteArray(mSpec.getAttestationChallenge());
out.writeBoolean(mSpec.isUniqueIdIncluded());
@@ -153,6 +154,7 @@
final boolean randomizedEncryptionRequired = in.readBoolean();
final boolean userAuthenticationRequired = in.readBoolean();
final int userAuthenticationValidityDurationSeconds = in.readInt();
+ final int userAuthenticationTypes = in.readInt();
final boolean userPresenceRequired = in.readBoolean();
final byte[] attestationChallenge = in.createByteArray();
final boolean uniqueIdIncluded = in.readBoolean();
@@ -185,6 +187,7 @@
randomizedEncryptionRequired,
userAuthenticationRequired,
userAuthenticationValidityDurationSeconds,
+ userAuthenticationTypes,
userPresenceRequired,
attestationChallenge,
uniqueIdIncluded,
diff --git a/keystore/java/android/security/keystore/UserAuthArgs.java b/keystore/java/android/security/keystore/UserAuthArgs.java
index 6952060..c9e9bf0 100644
--- a/keystore/java/android/security/keystore/UserAuthArgs.java
+++ b/keystore/java/android/security/keystore/UserAuthArgs.java
@@ -28,6 +28,7 @@
boolean isUserAuthenticationRequired();
int getUserAuthenticationValidityDurationSeconds();
+ @KeyProperties.AuthEnum int getUserAuthenticationType();
boolean isUserAuthenticationValidWhileOnBody();
boolean isInvalidatedByBiometricEnrollment();
boolean isUserConfirmationRequired();
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 030f80a..a99e68f 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -110,6 +110,7 @@
boolean isProviderEnabledForUser(String provider, int userId);
boolean isLocationEnabledForUser(int userId);
+ void setLocationEnabledForUser(boolean enabled, int userId);
void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
void removeTestProvider(String provider, String opPackageName);
void setTestProviderLocation(String provider, in Location loc, String opPackageName);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 8afdb7d..8ae967f 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -478,13 +478,11 @@
@TestApi
@RequiresPermission(WRITE_SECURE_SETTINGS)
public void setLocationEnabledForUser(boolean enabled, @NonNull UserHandle userHandle) {
- Settings.Secure.putIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.LOCATION_MODE,
- enabled
- ? Settings.Secure.LOCATION_MODE_ON
- : Settings.Secure.LOCATION_MODE_OFF,
- userHandle.getIdentifier());
+ try {
+ mService.setLocationEnabledForUser(enabled, userHandle.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 804e0cb..59881e7 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1069,17 +1069,17 @@
<string name="power_remaining_only_more_than_subtext">More than <xliff:g id="time_remaining">%1$s</xliff:g> remaining</string>
<!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
- <string name="power_remaining_duration_only_shutdown_imminent" product="default">Phone may shutdown soon</string>
+ <string name="power_remaining_duration_only_shutdown_imminent" product="default">Phone may shut down soon</string>
<!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
- <string name="power_remaining_duration_only_shutdown_imminent" product="tablet">Tablet may shutdown soon</string>
+ <string name="power_remaining_duration_only_shutdown_imminent" product="tablet">Tablet may shut down soon</string>
<!-- [CHAR_LIMIT=50] Short label for imminent shutdown warning of device -->
- <string name="power_remaining_duration_only_shutdown_imminent" product="device">Device may shutdown soon</string>
+ <string name="power_remaining_duration_only_shutdown_imminent" product="device">Device may shut down soon</string>
<!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
- <string name="power_remaining_duration_shutdown_imminent" product="default">Phone may shutdown soon (<xliff:g id="level">%1$s</xliff:g>)</string>
+ <string name="power_remaining_duration_shutdown_imminent" product="default">Phone may shut down soon (<xliff:g id="level">%1$s</xliff:g>)</string>
<!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
- <string name="power_remaining_duration_shutdown_imminent" product="tablet">Tablet may shutdown soon (<xliff:g id="level">%1$s</xliff:g>)</string>
+ <string name="power_remaining_duration_shutdown_imminent" product="tablet">Tablet may shut down soon (<xliff:g id="level">%1$s</xliff:g>)</string>
<!-- [CHAR_LIMIT=60] Label for battery level chart when shutdown is imminent-->
- <string name="power_remaining_duration_shutdown_imminent" product="device">Device may shutdown soon (<xliff:g id="level">%1$s</xliff:g>)</string>
+ <string name="power_remaining_duration_shutdown_imminent" product="device">Device may shut down soon (<xliff:g id="level">%1$s</xliff:g>)</string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging -->
<string name="power_charging"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="state">%2$s</xliff:g></string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 1ebe917..d03a747 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -216,12 +216,12 @@
}
public boolean supportsHighQualityAudio(BluetoothDevice device) {
- int support = mService.supportsOptionalCodecs(device);
+ int support = mService.isOptionalCodecsSupported(device);
return support == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED;
}
public boolean isHighQualityAudioEnabled(BluetoothDevice device) {
- int enabled = mService.getOptionalCodecsEnabled(device);
+ int enabled = mService.isOptionalCodecsEnabled(device);
if (enabled != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN) {
return enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED;
} else if (getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED &&
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 3aa35cb..5d1e4cb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -41,7 +41,6 @@
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -71,10 +70,10 @@
short mRssi;
// mProfiles and mRemovedProfiles does not do swap() between main and sub device. It is
// because current sub device is only for HearingAid and its profile is the same.
- private final List<LocalBluetoothProfile> mProfiles = new ArrayList<>();
+ private final Collection<LocalBluetoothProfile> mProfiles = new CopyOnWriteArrayList<>();
// List of profiles that were previously in mProfiles, but have been removed
- private final List<LocalBluetoothProfile> mRemovedProfiles = new ArrayList<>();
+ private final Collection<LocalBluetoothProfile> mRemovedProfiles = new CopyOnWriteArrayList<>();
// Device supports PANU but not NAP: remove PanProfile after device disconnects from NAP
private boolean mLocalNapRoleConnected;
@@ -717,7 +716,7 @@
}
public List<LocalBluetoothProfile> getProfiles() {
- return Collections.unmodifiableList(mProfiles);
+ return new ArrayList<>(mProfiles);
}
public List<LocalBluetoothProfile> getConnectableProfiles() {
@@ -734,7 +733,7 @@
}
public List<LocalBluetoothProfile> getRemovedProfiles() {
- return mRemovedProfiles;
+ return new ArrayList<>(mRemovedProfiles);
}
public void registerCallback(Callback callback) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index c72efb7..35bbbc0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -48,6 +48,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -232,7 +233,7 @@
}
private final Collection<ServiceListener> mServiceListeners =
- new ArrayList<ServiceListener>();
+ new CopyOnWriteArrayList<ServiceListener>();
private void addProfile(LocalBluetoothProfile profile,
String profileName, String stateChangedAction) {
@@ -361,14 +362,18 @@
// not synchronized: use only from UI thread! (TODO: verify)
void callServiceConnectedListeners() {
- for (ServiceListener l : mServiceListeners) {
+ final Collection<ServiceListener> listeners = new ArrayList<>(mServiceListeners);
+
+ for (ServiceListener l : listeners) {
l.onServiceConnected();
}
}
// not synchronized: use only from UI thread! (TODO: verify)
void callServiceDisconnectedListeners() {
- for (ServiceListener listener : mServiceListeners) {
+ final Collection<ServiceListener> listeners = new ArrayList<>(mServiceListeners);
+
+ for (ServiceListener listener : listeners) {
listener.onServiceDisconnected();
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
index b9081f2..b725ba5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaDevice.java
@@ -16,12 +16,9 @@
package com.android.settingslib.media;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
-import android.text.TextUtils;
-import android.util.Log;
import com.android.settingslib.R;
import com.android.settingslib.bluetooth.BluetoothUtils;
@@ -62,46 +59,6 @@
return MediaDeviceUtils.getId(mRouteInfo);
}
- @Override
- public void requestSetVolume(int volume) {
- mRouterManager.requestSetVolume(mRouteInfo, volume);
- }
-
- @Override
- public int getMaxVolume() {
- return mRouteInfo.getVolumeMax();
- }
-
- @Override
- public int getCurrentVolume() {
- return mRouteInfo.getVolume();
- }
-
- @Override
- public String getClientPackageName() {
- return mRouteInfo.getClientPackageName();
- }
-
- @Override
- public String getClientAppLabel() {
- final String packageName = mRouteInfo.getClientPackageName();
- if (TextUtils.isEmpty(packageName)) {
- Log.d(TAG, "Client package name is empty");
- return mContext.getResources().getString(R.string.unknown);
- }
- try {
- final PackageManager packageManager = mContext.getPackageManager();
- final String appLabel = packageManager.getApplicationLabel(
- packageManager.getApplicationInfo(packageName, 0)).toString();
- if (!TextUtils.isEmpty(appLabel)) {
- return appLabel;
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "unable to find " + packageName);
- }
- return mContext.getResources().getString(R.string.unknown);
- }
-
public boolean isConnected() {
return true;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index 580e086..33c3d7e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -16,14 +16,18 @@
package com.android.settingslib.media;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
import android.text.TextUtils;
+import android.util.Log;
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
+import com.android.settingslib.R;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -113,7 +117,9 @@
*
* @param volume is the new value.
*/
+
public void requestSetVolume(int volume) {
+ mRouterManager.requestSetVolume(mRouteInfo, volume);
}
/**
@@ -122,7 +128,7 @@
* @return max volume.
*/
public int getMaxVolume() {
- return 100;
+ return mRouteInfo.getVolumeMax();
}
/**
@@ -131,7 +137,7 @@
* @return current volume.
*/
public int getCurrentVolume() {
- return 0;
+ return mRouteInfo.getVolume();
}
/**
@@ -140,7 +146,7 @@
* @return package name.
*/
public String getClientPackageName() {
- return null;
+ return mRouteInfo.getClientPackageName();
}
/**
@@ -149,7 +155,22 @@
* @return application label.
*/
public String getClientAppLabel() {
- return null;
+ final String packageName = mRouteInfo.getClientPackageName();
+ if (TextUtils.isEmpty(packageName)) {
+ Log.d(TAG, "Client package name is empty");
+ return mContext.getResources().getString(R.string.unknown);
+ }
+ try {
+ final PackageManager packageManager = mContext.getPackageManager();
+ final String appLabel = packageManager.getApplicationLabel(
+ packageManager.getApplicationInfo(packageName, 0)).toString();
+ if (!TextUtils.isEmpty(appLabel)) {
+ return appLabel;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "unable to find " + packageName);
+ }
+ return mContext.getResources().getString(R.string.unknown);
}
/**
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index c555cbe..cb9092e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -73,26 +73,26 @@
@Test
public void supportsHighQualityAudio() {
- when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+ when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
assertThat(mProfile.supportsHighQualityAudio(mDevice)).isTrue();
- when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+ when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
assertThat(mProfile.supportsHighQualityAudio(mDevice)).isFalse();
- when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+ when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN);
assertThat(mProfile.supportsHighQualityAudio(mDevice)).isFalse();
}
@Test
public void isHighQualityAudioEnabled() {
- when(mBluetoothA2dp.getOptionalCodecsEnabled(any())).thenReturn(
+ when(mBluetoothA2dp.isOptionalCodecsEnabled(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED);
assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isTrue();
- when(mBluetoothA2dp.getOptionalCodecsEnabled(any())).thenReturn(
+ when(mBluetoothA2dp.isOptionalCodecsEnabled(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED);
assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isFalse();
@@ -100,16 +100,16 @@
// then isHighQualityAudioEnabled() should return true or false based on whether optional
// codecs are supported. If the device is connected then we should ask it directly, but if
// the device isn't connected then rely on the stored pref about such support.
- when(mBluetoothA2dp.getOptionalCodecsEnabled(any())).thenReturn(
+ when(mBluetoothA2dp.isOptionalCodecsEnabled(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN);
when(mBluetoothA2dp.getConnectionState(any())).thenReturn(
BluetoothProfile.STATE_DISCONNECTED);
- when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+ when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isFalse();
- when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+ when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isTrue();
@@ -151,14 +151,14 @@
// Most tests want to simulate optional codecs being supported by the device, so do that
// by default here.
- when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+ when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED);
}
@Test
public void getLableCodecsNotSupported() {
setupLabelTest();
- when(mBluetoothA2dp.supportsOptionalCodecs(any())).thenReturn(
+ when(mBluetoothA2dp.isOptionalCodecsSupported(any())).thenReturn(
BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED);
assertThat(mProfile.getHighQualityAudioOptionLabel(mDevice)).isEqualTo(UNKNOWN_CODEC_LABEL);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
index 04ceb21..77a67c2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaDeviceTest.java
@@ -21,9 +21,6 @@
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageStats;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
@@ -36,14 +33,11 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
-import org.robolectric.Shadows;
-import org.robolectric.shadows.ShadowPackageManager;
@RunWith(RobolectricTestRunner.class)
public class InfoMediaDeviceTest {
private static final String TEST_PACKAGE_NAME = "com.test.packagename";
- private static final String TEST_PACKAGE_NAME2 = "com.test.packagename2";
private static final String TEST_ID = "test_id";
private static final String TEST_NAME = "test_name";
@@ -52,27 +46,13 @@
@Mock
private MediaRoute2Info mRouteInfo;
-
private Context mContext;
private InfoMediaDevice mInfoMediaDevice;
- private ShadowPackageManager mShadowPackageManager;
- private ApplicationInfo mAppInfo;
- private PackageInfo mPackageInfo;
- private PackageStats mPackageStats;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
- mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
- mAppInfo = new ApplicationInfo();
- mAppInfo.flags = ApplicationInfo.FLAG_INSTALLED;
- mAppInfo.packageName = TEST_PACKAGE_NAME;
- mAppInfo.name = TEST_NAME;
- mPackageInfo = new PackageInfo();
- mPackageInfo.packageName = TEST_PACKAGE_NAME;
- mPackageInfo.applicationInfo = mAppInfo;
- mPackageStats = new PackageStats(TEST_PACKAGE_NAME);
mInfoMediaDevice = new InfoMediaDevice(mContext, mRouterManager, mRouteInfo,
TEST_PACKAGE_NAME);
@@ -106,32 +86,4 @@
assertThat(mInfoMediaDevice.getId()).isEqualTo(TEST_ID);
}
-
- @Test
- public void getClientPackageName_returnPackageName() {
- when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
-
- assertThat(mInfoMediaDevice.getClientPackageName()).isEqualTo(TEST_PACKAGE_NAME);
- }
-
- @Test
- public void getClientAppLabel_matchedPackageName_returnLabel() {
- when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
-
- assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo(
- mContext.getResources().getString(R.string.unknown));
-
- mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
-
- assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo(TEST_NAME);
- }
-
- @Test
- public void getClientAppLabel_noMatchedPackageName_returnDefault() {
- mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
- when(mRouteInfo.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME2);
-
- assertThat(mInfoMediaDevice.getClientAppLabel()).isEqualTo(
- mContext.getResources().getString(R.string.unknown));
- }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index fb8b78b..3f29b72 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -23,9 +23,13 @@
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageStats;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import com.android.settingslib.R;
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HearingAidProfile;
@@ -39,6 +43,8 @@
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.shadows.ShadowPackageManager;
import java.util.ArrayList;
import java.util.Collections;
@@ -58,6 +64,8 @@
private static final String ROUTER_ID_2 = "RouterId_2";
private static final String ROUTER_ID_3 = "RouterId_3";
private static final String TEST_PACKAGE_NAME = "com.test.playmusic";
+ private static final String TEST_PACKAGE_NAME2 = "com.test.playmusic2";
+ private static final String TEST_APPLICATION_LABEL = "playmusic";
private final BluetoothClass mHeadreeClass =
new BluetoothClass(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
private final BluetoothClass mCarkitClass =
@@ -111,6 +119,10 @@
private InfoMediaDevice mInfoMediaDevice3;
private List<MediaDevice> mMediaDevices = new ArrayList<>();
private PhoneMediaDevice mPhoneMediaDevice;
+ private ShadowPackageManager mShadowPackageManager;
+ private ApplicationInfo mAppInfo;
+ private PackageInfo mPackageInfo;
+ private PackageStats mPackageStats;
@Before
public void setUp() {
@@ -394,4 +406,46 @@
verify(mMediaRouter2Manager).selectRoute(TEST_PACKAGE_NAME, mRouteInfo1);
}
+
+ @Test
+ public void getClientPackageName_returnPackageName() {
+ when(mRouteInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+ assertThat(mInfoMediaDevice1.getClientPackageName()).isEqualTo(TEST_PACKAGE_NAME);
+ }
+
+ private void initPackage() {
+ mShadowPackageManager = Shadows.shadowOf(mContext.getPackageManager());
+ mAppInfo = new ApplicationInfo();
+ mAppInfo.flags = ApplicationInfo.FLAG_INSTALLED;
+ mAppInfo.packageName = TEST_PACKAGE_NAME;
+ mAppInfo.name = TEST_APPLICATION_LABEL;
+ mPackageInfo = new PackageInfo();
+ mPackageInfo.packageName = TEST_PACKAGE_NAME;
+ mPackageInfo.applicationInfo = mAppInfo;
+ mPackageStats = new PackageStats(TEST_PACKAGE_NAME);
+ }
+
+ @Test
+ public void getClientAppLabel_matchedPackageName_returnLabel() {
+ initPackage();
+ when(mRouteInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+ assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(
+ mContext.getResources().getString(R.string.unknown));
+
+ mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
+
+ assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(TEST_APPLICATION_LABEL);
+ }
+
+ @Test
+ public void getClientAppLabel_noMatchedPackageName_returnDefault() {
+ initPackage();
+ mShadowPackageManager.addPackage(mPackageInfo, mPackageStats);
+ when(mRouteInfo1.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME2);
+
+ assertThat(mInfoMediaDevice1.getClientAppLabel()).isEqualTo(
+ mContext.getResources().getString(R.string.unknown));
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
index 61fdbd5..ed308c8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -120,9 +120,9 @@
true /* basedOnUsage */);
// additional battery percentage in this string
- assertThat(info).isEqualTo("Phone may shutdown soon (10%)");
+ assertThat(info).isEqualTo("Phone may shut down soon (10%)");
// shortened string should not have percentage
- assertThat(info2).isEqualTo("Phone may shutdown soon");
+ assertThat(info2).isEqualTo("Phone may shut down soon");
}
@Test
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index c969bfd..7b518a6 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2193,7 +2193,7 @@
if (prefix == null) {
return;
}
- String callingPackage = getCallingPackage();
+ String callingPackage = resolveCallingPackage();
String namespace = prefix.replace("/", "");
if (DeviceConfig.getPublicNamespaces().contains(namespace)) {
return;
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index 01811e9..f607cc8 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -118,6 +118,7 @@
public CharSequence label;
public CharSequence secondaryLabel;
public CharSequence contentDescription;
+ public CharSequence stateDescription;
public CharSequence dualLabelContentDescription;
public boolean disabledByPolicy;
public boolean dualTarget = false;
@@ -135,6 +136,7 @@
|| !Objects.equals(other.label, label)
|| !Objects.equals(other.secondaryLabel, secondaryLabel)
|| !Objects.equals(other.contentDescription, contentDescription)
+ || !Objects.equals(other.stateDescription, stateDescription)
|| !Objects.equals(other.dualLabelContentDescription,
dualLabelContentDescription)
|| !Objects.equals(other.expandedAccessibilityClassName,
@@ -151,6 +153,7 @@
other.label = label;
other.secondaryLabel = secondaryLabel;
other.contentDescription = contentDescription;
+ other.stateDescription = stateDescription;
other.dualLabelContentDescription = dualLabelContentDescription;
other.expandedAccessibilityClassName = expandedAccessibilityClassName;
other.disabledByPolicy = disabledByPolicy;
@@ -177,6 +180,7 @@
sb.append(",label=").append(label);
sb.append(",secondaryLabel=").append(secondaryLabel);
sb.append(",contentDescription=").append(contentDescription);
+ sb.append(",stateDescription=").append(stateDescription);
sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription);
sb.append(",expandedAccessibilityClassName=").append(expandedAccessibilityClassName);
sb.append(",disabledByPolicy=").append(disabledByPolicy);
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0db7d67..f0e4e53 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -977,6 +977,9 @@
Equal to pip_action_size - pip_action_padding. -->
<dimen name="pip_expand_container_edge_margin">30dp</dimen>
+ <!-- The touchable/draggable edge size for PIP resize. -->
+ <dimen name="pip_resize_edge_size">30dp</dimen>
+
<dimen name="default_gear_space">18dp</dimen>
<dimen name="cell_overlay_padding">18dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ebaf55f..8f9e934 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2032,6 +2032,9 @@
<!-- Label for feature switch [CHAR LIMIT=30] -->
<string name="switch_bar_off">Off</string>
+ <!-- The tile in quick settings is unavailable. [CHAR LIMIT=32] -->
+ <string name="tile_unavailable">Unavailable</string>
+
<!-- SysUI Tuner: Button that leads to the navigation bar customization screen [CHAR LIMIT=60] -->
<string name="nav_bar">Navigation bar</string>
@@ -2586,6 +2589,4 @@
<string name="controls_favorite_default_title">Controls</string>
<!-- Controls management controls screen subtitle [CHAR LIMIT=NONE] -->
<string name="controls_favorite_subtitle">Choose controls for quick access</string>
-
-
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index 0ec739fe..c069708 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -26,6 +26,7 @@
import android.os.HandlerThread;
import android.os.ServiceManager;
import android.util.DisplayMetrics;
+import android.view.Choreographer;
import android.view.IWindowManager;
import android.view.LayoutInflater;
@@ -203,4 +204,11 @@
public ViewMediatorCallback providesViewMediatorCallback(KeyguardViewMediator viewMediator) {
return viewMediator.getViewMediatorCallback();
}
+
+ /** */
+ @Singleton
+ @Provides
+ public Choreographer providesChoreographer() {
+ return Choreographer.getInstance();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index f068d9c..91aeb22 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -19,14 +19,13 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
+import android.view.Choreographer;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.BootCompleteCache;
import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.DumpController;
import com.android.systemui.assist.AssistModule;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.model.SysUiState;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -34,18 +33,22 @@
import com.android.systemui.stackdivider.Divider;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.StatusBarWindowBlurController;
+import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.people.PeopleHubModule;
import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.ConcurrencyModule;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.time.SystemClock;
@@ -100,11 +103,16 @@
@Singleton
@Provides
@Nullable
- static StatusBarWindowBlurController providesBlurController(BlurUtils blurUtils,
- @Main Resources resources, SysuiStatusBarStateController statusBarStateController,
- DumpController dumpController) {
- return blurUtils.supportsBlursOnWindows() ? new StatusBarWindowBlurController(resources,
- statusBarStateController, blurUtils, dumpController) : null;
+ static NotificationShadeWindowBlurController providesBlurController(BlurUtils blurUtils,
+ SysuiStatusBarStateController statusBarStateController,
+ DumpController dumpController, BiometricUnlockController biometricUnlockController,
+ KeyguardStateController keyguardStateController,
+ NotificationShadeWindowController notificationShadeWindowController,
+ Choreographer choreographer) {
+ return blurUtils.supportsBlursOnWindows() ? new NotificationShadeWindowBlurController(
+ statusBarStateController, blurUtils, biometricUnlockController,
+ keyguardStateController, notificationShadeWindowController, choreographer,
+ dumpController) : null;
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 7e1c35d..c45063a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -167,6 +167,10 @@
// again, it will only show after the brightness sensor has stabilized,
// avoiding a potential flicker.
scrimOpacity = 255;
+ } else if (!mScreenOff && mLightSensor == null) {
+ // No light sensor but previous state turned the screen black. Make the scrim
+ // transparent and below views visible.
+ scrimOpacity = 0;
} else if (brightnessReady) {
// Only unblank scrim once brightness is ready.
scrimOpacity = computeScrimOpacity(sensorValue);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index a3cd5fd..d298b99 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -101,6 +101,7 @@
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.EmergencyDialerConstants;
@@ -1668,7 +1669,7 @@
}
if (mBackgroundDrawable == null) {
mBackgroundDrawable = new ScrimDrawable();
- mScrimAlpha = 0.8f;
+ mScrimAlpha = ScrimController.BUSY_SCRIM_ALPHA;
}
getWindow().setBackgroundDrawable(mBackgroundDrawable);
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 146f36b..280a248 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -149,7 +149,7 @@
}
if (mBlurUtils.supportsBlursOnWindows()) {
- background.setAlpha((int) (ScrimController.GRADIENT_SCRIM_ALPHA_BUSY * 255));
+ background.setAlpha((int) (ScrimController.BUSY_SCRIM_ALPHA * 255));
mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(),
mBlurUtils.radiusForRatio(1));
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
index 6f03f18..41b3130 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -345,6 +345,14 @@
}
/**
+ * Sets the current bound with the currently store aspect ratio.
+ * @param stackBounds
+ */
+ public void transformBoundsToAspectRatio(Rect stackBounds) {
+ transformBoundsToAspectRatio(stackBounds, mAspectRatio, true);
+ }
+
+ /**
* Set the current bounds (or the default bounds if there are no current bounds) with the
* specified aspect ratio.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
new file mode 100644
index 0000000..9fb6234
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipResizeGestureHandler.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.pip.phone;
+
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_USER_RESIZE;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_BOTTOM;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_LEFT;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_NONE;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_RIGHT;
+import static com.android.internal.policy.TaskResizingAlgorithm.CTRL_TOP;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.hardware.input.InputManager;
+import android.os.Looper;
+import android.provider.DeviceConfig;
+import android.util.DisplayMetrics;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
+import android.view.InputMonitor;
+import android.view.MotionEvent;
+
+import com.android.internal.policy.TaskResizingAlgorithm;
+import com.android.systemui.R;
+import com.android.systemui.pip.PipBoundsHandler;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Helper on top of PipTouchHandler that handles inputs OUTSIDE of the PIP window, which is used to
+ * trigger dynamic resize.
+ */
+public class PipResizeGestureHandler {
+
+ private static final String TAG = "PipResizeGestureHandler";
+
+ private final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+ private final PipBoundsHandler mPipBoundsHandler;
+ private final PipTouchHandler mPipTouchHandler;
+ private final PipMotionHelper mMotionHelper;
+ private final int mDisplayId;
+ private final Executor mMainExecutor;
+ private final Region mTmpRegion = new Region();
+
+ private final PointF mDownPoint = new PointF();
+ private final Point mMaxSize = new Point();
+ private final Point mMinSize = new Point();
+ private final Rect mTmpBounds = new Rect();
+ private final int mDelta;
+
+ private boolean mAllowGesture = false;
+ private boolean mIsAttached;
+ private boolean mIsEnabled;
+ private boolean mEnablePipResize;
+
+ private InputMonitor mInputMonitor;
+ private InputEventReceiver mInputEventReceiver;
+
+ private int mCtrlType;
+
+ public PipResizeGestureHandler(Context context, PipBoundsHandler pipBoundsHandler,
+ PipTouchHandler pipTouchHandler, PipMotionHelper motionHelper) {
+ final Resources res = context.getResources();
+ context.getDisplay().getMetrics(mDisplayMetrics);
+ mDisplayId = context.getDisplayId();
+ mMainExecutor = context.getMainExecutor();
+ mPipBoundsHandler = pipBoundsHandler;
+ mPipTouchHandler = pipTouchHandler;
+ mMotionHelper = motionHelper;
+
+ context.getDisplay().getRealSize(mMaxSize);
+ mDelta = res.getDimensionPixelSize(R.dimen.pip_resize_edge_size);
+
+ mEnablePipResize = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ PIP_USER_RESIZE,
+ /* defaultValue = */ false);
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor,
+ new DeviceConfig.OnPropertiesChangedListener() {
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ if (properties.getKeyset().contains(PIP_USER_RESIZE)) {
+ mEnablePipResize = properties.getBoolean(
+ PIP_USER_RESIZE, /* defaultValue = */ false);
+ }
+ }
+ });
+ }
+
+ private void disposeInputChannel() {
+ if (mInputEventReceiver != null) {
+ mInputEventReceiver.dispose();
+ mInputEventReceiver = null;
+ }
+ if (mInputMonitor != null) {
+ mInputMonitor.dispose();
+ mInputMonitor = null;
+ }
+ }
+
+ void onActivityPinned() {
+ mIsAttached = true;
+ updateIsEnabled();
+ }
+
+ void onActivityUnpinned() {
+ mIsAttached = false;
+ updateIsEnabled();
+ }
+
+ private void updateIsEnabled() {
+ boolean isEnabled = mIsAttached && mEnablePipResize;
+ if (isEnabled == mIsEnabled) {
+ return;
+ }
+ mIsEnabled = isEnabled;
+ disposeInputChannel();
+
+ if (mIsEnabled) {
+ // Register input event receiver
+ mInputMonitor = InputManager.getInstance().monitorGestureInput(
+ "pip-resize", mDisplayId);
+ mInputEventReceiver = new SysUiInputEventReceiver(
+ mInputMonitor.getInputChannel(), Looper.getMainLooper());
+ }
+ }
+
+ private void onInputEvent(InputEvent ev) {
+ if (ev instanceof MotionEvent) {
+ onMotionEvent((MotionEvent) ev);
+ }
+ }
+
+ private boolean isWithinTouchRegion(int x, int y) {
+ final Rect currentPipBounds = mMotionHelper.getBounds();
+ if (currentPipBounds == null) {
+ return false;
+ }
+
+ mTmpBounds.set(currentPipBounds);
+ mTmpBounds.inset(-mDelta, -mDelta);
+
+ mTmpRegion.set(mTmpBounds);
+ mTmpRegion.op(currentPipBounds, Region.Op.DIFFERENCE);
+
+ if (mTmpRegion.contains(x, y)) {
+ if (x < currentPipBounds.left) {
+ mCtrlType |= CTRL_LEFT;
+ }
+ if (x > currentPipBounds.right) {
+ mCtrlType |= CTRL_RIGHT;
+ }
+ if (y < currentPipBounds.top) {
+ mCtrlType |= CTRL_TOP;
+ }
+ if (y > currentPipBounds.bottom) {
+ mCtrlType |= CTRL_BOTTOM;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void onMotionEvent(MotionEvent ev) {
+ int action = ev.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ mAllowGesture = isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
+ if (mAllowGesture) {
+ mDownPoint.set(ev.getX(), ev.getY());
+ }
+
+ } else if (mAllowGesture) {
+ final Rect currentPipBounds = mMotionHelper.getBounds();
+ Rect newSize = TaskResizingAlgorithm.resizeDrag(ev.getX(), ev.getY(), mDownPoint.x,
+ mDownPoint.y, currentPipBounds, mCtrlType, mMinSize.x, mMinSize.y, mMaxSize,
+ true, true);
+ mPipBoundsHandler.transformBoundsToAspectRatio(newSize);
+ switch (action) {
+ case MotionEvent.ACTION_POINTER_DOWN:
+ // We do not support multi touch for resizing via drag
+ mAllowGesture = false;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ // Capture inputs
+ mInputMonitor.pilferPointers();
+ //TODO: Actually do resize here.
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ //TODO: Finish resize operation here.
+ mMotionHelper.synchronizePinnedStackBounds();
+ mCtrlType = CTRL_NONE;
+ mAllowGesture = false;
+ break;
+ }
+ }
+ }
+
+ void updateMaxSize(int maxX, int maxY) {
+ mMaxSize.set(maxX, maxY);
+ }
+
+ void updateMiniSize(int minX, int minY) {
+ mMinSize.set(minX, minY);
+ }
+
+ class SysUiInputEventReceiver extends InputEventReceiver {
+ SysUiInputEventReceiver(InputChannel channel, Looper looper) {
+ super(channel, looper);
+ }
+
+ public void onInputEvent(InputEvent event) {
+ PipResizeGestureHandler.this.onInputEvent(event);
+ finishInputEvent(event, true);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 65cc666..924edb6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -73,6 +73,7 @@
private final ViewConfiguration mViewConfig;
private final PipMenuListener mMenuListener = new PipMenuListener();
private final PipBoundsHandler mPipBoundsHandler;
+ private final PipResizeGestureHandler mPipResizeGestureHandler;
private IPinnedStackController mPinnedStackController;
private final PipMenuActivityController mMenuController;
@@ -188,6 +189,8 @@
mGesture = new DefaultPipTouchGesture();
mMotionHelper = new PipMotionHelper(mContext, mActivityManager, mActivityTaskManager,
mMenuController, mSnapAlgorithm, mFlingAnimationUtils);
+ mPipResizeGestureHandler =
+ new PipResizeGestureHandler(context, pipBoundsHandler, this, mMotionHelper);
mTouchState = new PipTouchState(mViewConfig, mHandler,
() -> mMenuController.showMenu(MENU_STATE_FULL, mMotionHelper.getBounds(),
mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()));
@@ -227,6 +230,7 @@
public void onActivityPinned() {
cleanUp();
mShowPipMenuOnAnimationEnd = true;
+ mPipResizeGestureHandler.onActivityPinned();
}
public void onActivityUnpinned(ComponentName topPipActivity) {
@@ -234,11 +238,14 @@
// Clean up state after the last PiP activity is removed
cleanUp();
}
+ mPipResizeGestureHandler.onActivityUnpinned();
}
public void onPinnedStackAnimationEnded() {
// Always synchronize the motion helper bounds once PiP animations finish
mMotionHelper.synchronizePinnedStackBounds();
+ mPipResizeGestureHandler.updateMiniSize(mMotionHelper.getBounds().width(),
+ mMotionHelper.getBounds().height());
if (mShowPipMenuOnAnimationEnd) {
mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
@@ -279,6 +286,7 @@
Size expandedSize = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio,
mExpandedShortestEdgeSize, displaySize.x, displaySize.y);
mExpandedBounds.set(0, 0, expandedSize.getWidth(), expandedSize.getHeight());
+ mPipResizeGestureHandler.updateMaxSize(expandedSize.getWidth(), expandedSize.getHeight());
Rect expandedMovementBounds = new Rect();
mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds, expandedMovementBounds,
bottomOffset);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 14a117c..b3d96f8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -38,7 +38,6 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
@@ -60,7 +59,6 @@
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.settings.BrightnessController;
import com.android.systemui.settings.ToggleSliderView;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener;
import com.android.systemui.tuner.TunerService;
@@ -117,9 +115,6 @@
private BrightnessMirrorController mBrightnessMirrorController;
private View mDivider;
- private FrameLayout mPluginFrame;
- private final PluginManager mPluginManager;
-
private final LocalMediaManager.DeviceCallback mDeviceCallback =
new LocalMediaManager.DeviceCallback() {
@Override
@@ -154,13 +149,12 @@
}
public QSPanel(Context context, AttributeSet attrs, DumpController dumpController) {
- this(context, attrs, dumpController, null, Dependency.get(BroadcastDispatcher.class));
+ this(context, attrs, dumpController, Dependency.get(BroadcastDispatcher.class));
}
@Inject
public QSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
- DumpController dumpController, PluginManager pluginManager,
- BroadcastDispatcher broadcastDispatcher) {
+ DumpController dumpController, BroadcastDispatcher broadcastDispatcher) {
super(context, attrs);
mContext = context;
@@ -198,7 +192,6 @@
mBrightnessController = new BrightnessController(getContext(),
findViewById(R.id.brightness_slider), broadcastDispatcher);
mDumpController = dumpController;
- mPluginManager = pluginManager;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index ae61622..c118630f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -191,6 +191,7 @@
mTile.setLabel(tile.getLabel());
mTile.setSubtitle(tile.getSubtitle());
mTile.setContentDescription(tile.getContentDescription());
+ mTile.setStateDescription(tile.getStateDescription());
mTile.setState(tile.getState());
}
@@ -345,6 +346,12 @@
state.contentDescription = state.label;
}
+ if (mTile.getStateDescription() != null) {
+ state.stateDescription = mTile.getStateDescription();
+ } else {
+ state.stateDescription = null;
+ }
+
if (state instanceof BooleanState) {
state.expandedAccessibilityClassName = Switch.class.getName();
((BooleanState) state).value = (state.state == Tile.STATE_ACTIVE);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 2fe64d2..8feee10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -63,7 +63,6 @@
private String mAccessibilityClass;
private boolean mTileState;
private boolean mCollapsedView;
- private boolean mClicked;
private boolean mShowRippleEffect = true;
private final ImageView mBg;
@@ -230,13 +229,35 @@
setLongClickable(state.handlesLongClick);
mIcon.setIcon(state, allowAnimations);
setContentDescription(state.contentDescription);
+ final StringBuilder stateDescription = new StringBuilder();
+ switch (state.state) {
+ case Tile.STATE_UNAVAILABLE:
+ stateDescription.append(mContext.getString(R.string.tile_unavailable));
+ break;
+ case Tile.STATE_INACTIVE:
+ if (state instanceof QSTile.BooleanState) {
+ stateDescription.append(mContext.getString(R.string.switch_bar_off));
+ }
+ break;
+ case Tile.STATE_ACTIVE:
+ if (state instanceof QSTile.BooleanState) {
+ stateDescription.append(mContext.getString(R.string.switch_bar_on));
+ }
+ break;
+ default:
+ break;
+ }
+ if (!TextUtils.isEmpty(state.stateDescription)) {
+ stateDescription.append(", ");
+ stateDescription.append(state.stateDescription);
+ }
+ setStateDescription(stateDescription.toString());
mAccessibilityClass =
state.state == Tile.STATE_UNAVAILABLE ? null : state.expandedAccessibilityClassName;
if (state instanceof QSTile.BooleanState) {
boolean newState = ((BooleanState) state).value;
if (mTileState != newState) {
- mClicked = false;
mTileState = newState;
}
}
@@ -288,23 +309,10 @@
}
@Override
- public boolean performClick() {
- mClicked = true;
- return super.performClick();
- }
-
- @Override
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
if (!TextUtils.isEmpty(mAccessibilityClass)) {
event.setClassName(mAccessibilityClass);
- if (Switch.class.getName().equals(mAccessibilityClass)) {
- boolean b = mClicked ? !mTileState : mTileState;
- String label = getResources()
- .getString(b ? R.string.switch_bar_on : R.string.switch_bar_off);
- event.setContentDescription(label);
- event.setChecked(b);
- }
}
}
@@ -316,11 +324,13 @@
if (!TextUtils.isEmpty(mAccessibilityClass)) {
info.setClassName(mAccessibilityClass);
if (Switch.class.getName().equals(mAccessibilityClass)) {
- boolean b = mClicked ? !mTileState : mTileState;
- String label = getResources()
- .getString(b ? R.string.switch_bar_on : R.string.switch_bar_off);
+ String label = getResources().getString(
+ mTileState ? R.string.switch_bar_on : R.string.switch_bar_off);
+ // Set the text here for tests in
+ // android.platform.test.scenario.sysui.quicksettings. Can be removed when
+ // UiObject2 has a new getStateDescription() API and tests are updated.
info.setText(label);
- info.setChecked(b);
+ info.setChecked(mTileState);
info.setCheckable(true);
if (isLongClickable()) {
info.addAction(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 9282a2e..361b6c1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -134,25 +134,27 @@
state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
state.secondaryLabel = TextUtils.emptyIfNull(
getSecondaryLabel(enabled, connecting, connected, state.isTransient));
+ state.contentDescription = state.label;
+ state.stateDescription = "";
if (enabled) {
if (connected) {
state.icon = new BluetoothConnectedTileIcon();
if (!TextUtils.isEmpty(mController.getConnectedDeviceName())) {
state.label = mController.getConnectedDeviceName();
}
- state.contentDescription =
+ state.stateDescription =
mContext.getString(R.string.accessibility_bluetooth_name, state.label)
+ ", " + state.secondaryLabel;
} else if (state.isTransient) {
state.icon = ResourceIcon.get(
com.android.internal.R.drawable.ic_bluetooth_transient_animation);
- state.contentDescription = state.secondaryLabel;
+ state.stateDescription = state.secondaryLabel;
} else {
state.icon =
ResourceIcon.get(com.android.internal.R.drawable.ic_qs_bluetooth);
state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_bluetooth) + ","
- + mContext.getString(R.string.accessibility_not_connected);
+ R.string.accessibility_quick_settings_bluetooth);
+ state.stateDescription = mContext.getString(R.string.accessibility_not_connected);
}
state.state = Tile.STATE_ACTIVE;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index 32b051e..58de057 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -183,6 +183,7 @@
protected void handleUpdateState(BooleanState state, Object arg) {
state.label = mContext.getString(R.string.quick_settings_cast_title);
state.contentDescription = state.label;
+ state.stateDescription = "";
state.value = false;
final List<CastDevice> devices = mController.getCastDevices();
boolean connecting = false;
@@ -192,8 +193,9 @@
if (device.state == CastDevice.STATE_CONNECTED) {
state.value = true;
state.secondaryLabel = getDeviceName(device);
- state.contentDescription = state.contentDescription + ","
- + mContext.getString(R.string.accessibility_cast_name, state.label);
+ state.stateDescription = state.stateDescription + ","
+ + mContext.getString(
+ R.string.accessibility_cast_name, state.label);
connecting = false;
break;
} else if (device.state == CastDevice.STATE_CONNECTING) {
@@ -217,9 +219,8 @@
state.state = Tile.STATE_UNAVAILABLE;
String noWifi = mContext.getString(R.string.quick_settings_cast_no_wifi);
state.secondaryLabel = noWifi;
- state.contentDescription = state.contentDescription + ", " + mContext.getString(
- R.string.accessibility_quick_settings_not_available, noWifi);
}
+ state.stateDescription = state.stateDescription + ", " + state.secondaryLabel;
mDetailAdapter.updateItems(devices);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 22470c7..d5f86c9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -194,17 +194,13 @@
state.secondaryLabel = r.getString(R.string.cell_data_off);
}
-
- // TODO(b/77881974): Instead of switching out the description via a string check for
- // we need to have two strings provided by the MobileIconGroup.
- final CharSequence contentDescriptionSuffix;
+ state.contentDescription = state.label;
if (state.state == Tile.STATE_INACTIVE) {
- contentDescriptionSuffix = r.getString(R.string.cell_data_off_content_description);
+ // This information is appended later by converting the Tile.STATE_INACTIVE state.
+ state.stateDescription = "";
} else {
- contentDescriptionSuffix = state.secondaryLabel;
+ state.stateDescription = state.secondaryLabel;
}
-
- state.contentDescription = state.label + ", " + contentDescriptionSuffix;
}
private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index 52d1a5b3..9215da4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -240,6 +240,8 @@
zen != Global.ZEN_MODE_OFF, mController.getConfig(), false));
state.icon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_dnd);
checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME);
+ // Keeping the secondaryLabel in contentDescription instead of stateDescription is easier
+ // to understand.
switch (zen) {
case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
state.contentDescription =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index dafdd89..792c364 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -102,14 +102,13 @@
}
state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
state.secondaryLabel = "";
+ state.stateDescription = "";
if (!mFlashlightController.isAvailable()) {
state.icon = mIcon;
state.slash.isSlashed = true;
state.secondaryLabel = mContext.getString(
R.string.quick_settings_flashlight_camera_in_use);
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_flashlight_unavailable)
- + ", " + state.secondaryLabel;
+ state.stateDescription = state.secondaryLabel;
state.state = Tile.STATE_UNAVAILABLE;
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 42f8010..91b3ae4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -148,6 +148,7 @@
state.secondaryLabel = getSecondaryLabel(
isTileActive, isTransient, isDataSaverEnabled, numConnectedDevices);
+ state.stateDescription = state.secondaryLabel;
}
@Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index fbdca3b..e617867 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -105,15 +105,8 @@
}
state.icon = mIcon;
state.slash.isSlashed = !state.value;
- if (locationEnabled) {
- state.label = mContext.getString(R.string.quick_settings_location_label);
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_location_on);
- } else {
- state.label = mContext.getString(R.string.quick_settings_location_label);
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_location_off);
- }
+ state.label = mContext.getString(R.string.quick_settings_location_label);
+ state.contentDescription = state.label;
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.expandedAccessibilityClassName = Switch.class.getName();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index b7ce101..6e8dcf3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -195,6 +195,7 @@
state.activityIn = cb.enabled && cb.activityIn;
state.activityOut = cb.enabled && cb.activityOut;
final StringBuffer minimalContentDescription = new StringBuffer();
+ final StringBuffer minimalStateDescription = new StringBuffer();
final Resources r = mContext.getResources();
if (isTransient) {
state.icon = ResourceIcon.get(
@@ -219,13 +220,14 @@
mContext.getString(R.string.quick_settings_wifi_label)).append(",");
if (state.value) {
if (wifiConnected) {
- minimalContentDescription.append(cb.wifiSignalContentDescription).append(",");
+ minimalStateDescription.append(cb.wifiSignalContentDescription);
minimalContentDescription.append(removeDoubleQuotes(cb.ssid));
if (!TextUtils.isEmpty(state.secondaryLabel)) {
minimalContentDescription.append(",").append(state.secondaryLabel);
}
}
}
+ state.stateDescription = minimalStateDescription.toString();
state.contentDescription = minimalContentDescription.toString();
state.dualLabelContentDescription = r.getString(
R.string.accessibility_quick_settings_open_settings, getTileLabel());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 7853dc3..e54ee51 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -103,14 +103,11 @@
state.icon = mIcon;
if (state.value) {
state.slash.isSlashed = false;
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_work_mode_on);
} else {
state.slash.isSlashed = true;
- state.contentDescription = mContext.getString(
- R.string.accessibility_quick_settings_work_mode_off);
}
state.label = mContext.getString(R.string.quick_settings_work_mode_label);
+ state.contentDescription = state.label;
state.expandedAccessibilityClassName = Switch.class.getName();
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 4f38a15..197fe21 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -442,27 +442,35 @@
ValueAnimator screenshotDropInAnim = screenRect != null ? createRectAnimation(screenRect)
: createScreenshotDropInAnimation();
- ValueAnimator screenshotFadeOutAnim = createScreenshotToCornerAnimation(w, h);
+ ValueAnimator screenshotToCornerAnimation = createScreenshotToCornerAnimation(w, h);
mScreenshotAnimation = new AnimatorSet();
- mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotFadeOutAnim);
- mScreenshotAnimation.addListener(new AnimatorListenerAdapter() {
+ mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotToCornerAnimation);
+
+ saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
@Override
- public void onAnimationEnd(Animator animation) {
- // Save the screenshot once we have a bit of time now
- saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
- @Override
- void onActionsReady(Uri uri, List<Notification.Action> smartActions,
- List<Notification.Action> actions) {
- if (uri == null) {
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_capture_text);
+ void onActionsReady(Uri uri, List<Notification.Action> smartActions,
+ List<Notification.Action> actions) {
+ if (uri == null) {
+ mNotificationsController.notifyScreenshotError(
+ R.string.screenshot_failed_to_capture_text);
+ } else {
+ mScreenshotHandler.post(() -> {
+ if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
+ mScreenshotAnimation.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ createScreenshotActionsShadeAnimation(
+ smartActions, actions).start();
+ }
+ });
} else {
- mScreenshotHandler.post(() ->
- createScreenshotActionsShadeAnimation(smartActions,
- actions).start());
+ createScreenshotActionsShadeAnimation(smartActions,
+ actions).start();
}
- }
- });
+ });
+ }
mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
mScreenshotHandler.sendMessageDelayed(
mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT),
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index e7e1ba8..92236ae 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -38,7 +38,6 @@
import android.os.Environment;
import android.os.Handler;
import android.os.ParcelFileDescriptor;
-import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -124,10 +123,6 @@
return null;
}
- // By default, AsyncTask sets the worker thread to have background thread priority,
- // so bump it back up so that we save a little quicker.
- Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
-
ContentResolver resolver = mContext.getContentResolver();
Bitmap image = mParams.image;
Resources r = mContext.getResources();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt
new file mode 100644
index 0000000..85215ed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowBlurController.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.view.Choreographer
+import android.view.View
+import com.android.internal.util.IndentingPrintWriter
+import com.android.systemui.DumpController
+import com.android.systemui.Dumpable
+import com.android.systemui.Interpolators
+import com.android.systemui.statusbar.phone.BiometricUnlockController
+import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController
+import com.android.systemui.statusbar.phone.PanelExpansionListener
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import javax.inject.Inject
+import javax.inject.Singleton
+import kotlin.math.max
+
+/**
+ * Controller responsible for statusbar window blur.
+ */
+@Singleton
+class NotificationShadeWindowBlurController @Inject constructor(
+ private val statusBarStateController: SysuiStatusBarStateController,
+ private val blurUtils: BlurUtils,
+ private val biometricUnlockController: BiometricUnlockController,
+ private val keyguardStateController: KeyguardStateController,
+ private val notificationShadeWindowController: NotificationShadeWindowController,
+ private val choreographer: Choreographer,
+ dumpController: DumpController
+) : PanelExpansionListener, Dumpable {
+ companion object {
+ private const val WAKE_UP_ANIMATION_ENABLED = true
+ private const val SHADE_BLUR_ENABLED = true
+ }
+
+ lateinit var root: View
+ private var keyguardAnimator: Animator? = null
+ private var notificationAnimator: Animator? = null
+ private var updateScheduled: Boolean = false
+ private var shadeExpansion = 1.0f
+ private var shadeBlurRadius = 0
+ set(value) {
+ if (field == value) return
+ field = value
+ scheduleUpdate()
+ }
+ private var wakeAndUnlockBlurRadius = 0
+ set(value) {
+ if (field == value) return
+ field = value
+ scheduleUpdate()
+ }
+ private var incomingNotificationBlurRadius = 0
+ set(value) {
+ if (field == value) return
+ field = value
+ scheduleUpdate()
+ }
+
+ /**
+ * Callback that updates the window blur value and is called only once per frame.
+ */
+ private val updateBlurCallback = Choreographer.FrameCallback {
+ updateScheduled = false
+
+ var notificationBlur = 0
+ if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+ notificationBlur = (incomingNotificationBlurRadius * shadeExpansion).toInt()
+ }
+
+ val blur = max(max(shadeBlurRadius, wakeAndUnlockBlurRadius), notificationBlur)
+ blurUtils.applyBlur(root.viewRootImpl, blur)
+ }
+
+ /**
+ * Animate blurs when unlocking.
+ */
+ private val keyguardStateCallback = object : KeyguardStateController.Callback {
+ override fun onKeyguardFadingAwayChanged() {
+ if (!keyguardStateController.isKeyguardFadingAway ||
+ biometricUnlockController.mode != MODE_WAKE_AND_UNLOCK) {
+ return
+ }
+
+ keyguardAnimator?.cancel()
+ keyguardAnimator = ValueAnimator.ofFloat(1f, 0f).apply {
+ duration = keyguardStateController.keyguardFadingAwayDuration
+ startDelay = keyguardStateController.keyguardFadingAwayDelay
+ interpolator = Interpolators.DECELERATE_QUINT
+ addUpdateListener { animation: ValueAnimator ->
+ wakeAndUnlockBlurRadius =
+ blurUtils.radiusForRatio(animation.animatedValue as Float)
+ }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator?) {
+ keyguardAnimator = null
+ scheduleUpdate()
+ }
+ })
+ start()
+ }
+ }
+
+ override fun onKeyguardShowingChanged() {
+ if (keyguardStateController.isShowing) {
+ keyguardAnimator?.cancel()
+ notificationAnimator?.cancel()
+ }
+ }
+ }
+
+ init {
+ dumpController.registerDumpable(this)
+ if (WAKE_UP_ANIMATION_ENABLED) {
+ keyguardStateController.addCallback(keyguardStateCallback)
+ }
+ }
+
+ /**
+ * Update blurs when pulling down the shade
+ */
+ override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) {
+ if (!SHADE_BLUR_ENABLED) {
+ return
+ }
+
+ var newBlur = 0
+ if (statusBarStateController.state == StatusBarState.SHADE) {
+ newBlur = blurUtils.radiusForRatio(expansion)
+ }
+
+ if (shadeBlurRadius == newBlur) {
+ return
+ }
+ shadeBlurRadius = newBlur
+ scheduleUpdate()
+ }
+
+ private fun scheduleUpdate() {
+ if (updateScheduled) {
+ return
+ }
+ updateScheduled = true
+ choreographer.postFrameCallback(updateBlurCallback)
+ }
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+ IndentingPrintWriter(pw, " ").use {
+ it.println("StatusBarWindowBlurController:")
+ it.increaseIndent()
+ it.println("shadeBlurRadius: $shadeBlurRadius")
+ it.println("wakeAndUnlockBlur: $wakeAndUnlockBlurRadius")
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWindowBlurController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWindowBlurController.kt
deleted file mode 100644
index 2e72163..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWindowBlurController.kt
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar
-
-import android.content.res.Resources
-import android.view.View
-import com.android.internal.util.IndentingPrintWriter
-import com.android.systemui.DumpController
-import com.android.systemui.Dumpable
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.statusbar.phone.PanelExpansionListener
-import java.io.FileDescriptor
-import java.io.PrintWriter
-import javax.inject.Inject
-import javax.inject.Singleton
-
-/**
- * Controller responsible for statusbar window blur.
- */
-@Singleton
-class StatusBarWindowBlurController @Inject constructor(
- @Main private val resources: Resources,
- private val statusBarStateController: SysuiStatusBarStateController,
- private val blurUtils: BlurUtils,
- dumpController: DumpController
-) : PanelExpansionListener, Dumpable {
-
- lateinit var root: View
- private var blurRadius = 0
-
- init {
- dumpController.registerDumpable(this)
- }
-
- override fun onPanelExpansionChanged(expansion: Float, tracking: Boolean) {
- val newBlur = if (statusBarStateController.state == StatusBarState.SHADE)
- blurUtils.radiusForRatio(expansion)
- else
- 0
-
- if (blurRadius == newBlur) {
- return
- }
- blurRadius = newBlur
- blurUtils.applyBlur(root.viewRootImpl, blurRadius)
- }
-
- override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
- IndentingPrintWriter(pw, " ").use {
- it.println("StatusBarWindowBlurController:")
- it.increaseIndent()
- it.println("blurRadius: $blurRadius")
- }
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index 44a3204..745843d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -375,6 +375,7 @@
mDownPoint.set(ev.getX(), ev.getY());
mThresholdCrossed = false;
}
+
} else if (mAllowGesture) {
if (!mThresholdCrossed) {
if (action == MotionEvent.ACTION_POINTER_DOWN) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 9e64748..3f5215e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -269,6 +269,17 @@
}
};
+ private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
+ new DeviceConfig.OnPropertiesChangedListener() {
+ @Override
+ public void onPropertiesChanged(DeviceConfig.Properties properties) {
+ if (properties.getKeyset().contains(NAV_BAR_HANDLE_FORCE_OPAQUE)) {
+ mForceNavBarHandleOpaque = properties.getBoolean(
+ NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true);
+ }
+ }
+ };
+
@Inject
public NavigationBarFragment(AccessibilityManagerWrapper accessibilityManagerWrapper,
DeviceProvisionedController deviceProvisionedController, MetricsLogger metricsLogger,
@@ -298,21 +309,6 @@
mDivider = divider;
mRecentsOptional = recentsOptional;
mHandler = mainHandler;
-
- mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- NAV_BAR_HANDLE_FORCE_OPAQUE,
- /* defaultValue = */ true);
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post,
- new DeviceConfig.OnPropertiesChangedListener() {
- @Override
- public void onPropertiesChanged(DeviceConfig.Properties properties) {
- if (properties.getKeyset().contains(NAV_BAR_HANDLE_FORCE_OPAQUE)) {
- mForceNavBarHandleOpaque = properties.getBoolean(
- NAV_BAR_HANDLE_FORCE_OPAQUE, /* defaultValue = */ true);
- }
- }
- });
}
// ----- Fragment Lifecycle Callbacks -----
@@ -338,6 +334,13 @@
// Respect the latest disabled-flags.
mCommandQueue.recomputeDisableFlags(mDisplayId, false);
+
+ mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_SYSTEMUI,
+ NAV_BAR_HANDLE_FORCE_OPAQUE,
+ /* defaultValue = */ true);
+ DeviceConfig.addOnPropertiesChangedListener(
+ DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
}
@Override
@@ -346,6 +349,8 @@
mNavigationModeController.removeListener(this);
mAccessibilityManagerWrapper.removeCallback(mAccessibilityListener);
mContentResolver.unregisterContentObserver(mAssistContentObserver);
+
+ DeviceConfig.removeOnPropertiesChangedListener(mOnPropertiesChangedListener);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
index 10b68b9..40b1610 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowController.java
@@ -18,8 +18,8 @@
import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
-
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
+
import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index ab1c8ad..8729e04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -45,7 +45,7 @@
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.StatusBarWindowBlurController;
+import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -81,7 +81,7 @@
private final CommandQueue mCommandQueue;
private final NotificationShadeWindowView mView;
private final ShadeController mShadeController;
- private final StatusBarWindowBlurController mBlurController;
+ private final NotificationShadeWindowBlurController mBlurController;
private GestureDetector mGestureDetector;
private View mBrightnessMirror;
@@ -123,7 +123,7 @@
CommandQueue commandQueue,
ShadeController shadeController,
DockManager dockManager,
- @Nullable StatusBarWindowBlurController blurController,
+ @Nullable NotificationShadeWindowBlurController blurController,
NotificationShadeWindowView statusBarWindowView,
NotificationPanelViewController notificationPanelViewController) {
mInjectionInflationController = injectionInflationController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 2b9fc8d..10821d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -107,7 +107,7 @@
/**
* Default alpha value for most scrims.
*/
- public static final float GRADIENT_SCRIM_ALPHA = 0.2f;
+ public static final float SCRIM_ALPHA = 0.2f;
/**
* Scrim opacity when the phone is about to wake-up.
*/
@@ -116,12 +116,12 @@
* A scrim varies its opacity based on a busyness factor, for example
* how many notifications are currently visible.
*/
- public static final float GRADIENT_SCRIM_ALPHA_BUSY = 0.7f;
+ public static final float BUSY_SCRIM_ALPHA = 0.54f;
/**
* The most common scrim, the one under the keyguard.
*/
- protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = GRADIENT_SCRIM_ALPHA;
+ protected static final float SCRIM_BEHIND_ALPHA_KEYGUARD = SCRIM_ALPHA;
static final int TAG_KEY_ANIM = R.id.scrim;
private static final int TAG_START_ALPHA = R.id.scrim_alpha_start;
@@ -481,7 +481,7 @@
// Darken scrim as you pull down the shade when unlocked
float behindFraction = getInterpolatedFraction();
behindFraction = (float) Math.pow(behindFraction, 0.8f);
- mBehindAlpha = behindFraction * GRADIENT_SCRIM_ALPHA_BUSY;
+ mBehindAlpha = behindFraction * BUSY_SCRIM_ALPHA;
mInFrontAlpha = 0;
} else if (mState == ScrimState.KEYGUARD || mState == ScrimState.PULSING) {
// Either darken of make the scrim transparent when you
@@ -489,7 +489,7 @@
float interpolatedFract = getInterpolatedFraction();
float alphaBehind = mState.getBehindAlpha();
if (mDarkenWhileDragging) {
- mBehindAlpha = MathUtils.lerp(GRADIENT_SCRIM_ALPHA_BUSY, alphaBehind,
+ mBehindAlpha = MathUtils.lerp(BUSY_SCRIM_ALPHA, alphaBehind,
interpolatedFract);
mInFrontAlpha = mState.getFrontAlpha();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 40f8d58..c23fd0a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -92,7 +92,7 @@
BOUNCER {
@Override
public void prepare(ScrimState previousState) {
- mBehindAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
+ mBehindAlpha = ScrimController.BUSY_SCRIM_ALPHA;
mFrontAlpha = 0f;
mBubbleAlpha = 0f;
}
@@ -106,7 +106,7 @@
public void prepare(ScrimState previousState) {
mBehindAlpha = 0;
mBubbleAlpha = 0f;
- mFrontAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
+ mFrontAlpha = ScrimController.BUSY_SCRIM_ALPHA;
}
},
@@ -234,8 +234,8 @@
mBubbleTint = Color.TRANSPARENT;
mFrontAlpha = ScrimController.TRANSPARENT;
- mBehindAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
- mBubbleAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
+ mBehindAlpha = ScrimController.BUSY_SCRIM_ALPHA;
+ mBubbleAlpha = ScrimController.BUSY_SCRIM_ALPHA;
mAnimationDuration = ScrimController.ANIMATION_DURATION;
mBlankScreen = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 6a046884..ef40acc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -19,6 +19,7 @@
import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
import static android.view.ViewRootImpl.sNewInsetsMode;
import static android.view.WindowInsets.Type.navigationBars;
+
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_FADING;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 399f723f..9117ea8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -173,6 +173,21 @@
}
@Test
+ public void testPulsing_withoutLightSensor_setsAoDDimmingScrimTransparent() throws Exception {
+ mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
+ null /* sensor */, mBroadcastDispatcher, mDozeHost, null /* handler */,
+ DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
+ true /* debuggable */);
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ mScreen.transitionTo(INITIALIZED, DOZE);
+ reset(mDozeHost);
+
+ mScreen.transitionTo(DOZE, DOZE_REQUEST_PULSE);
+
+ verify(mDozeHost).setAodDimmingScrim(eq(0f));
+ }
+
+ @Test
public void testDockedAod_usesLightSensor() {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD_DOCKED);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index 8936a2d..e917c93 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -38,7 +38,7 @@
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.StatusBarWindowBlurController;
+import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -80,7 +80,7 @@
@Mock private DockManager mDockManager;
@Mock private NotificationPanelViewController mNotificationPanelViewController;
@Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
- @Mock private StatusBarWindowBlurController mStatusBarWindowBlurController;
+ @Mock private NotificationShadeWindowBlurController mNotificationShadeWindowBlurController;
@Before
public void setUp() {
@@ -114,7 +114,7 @@
new CommandQueue(mContext),
mShadeController,
mDockManager,
- mStatusBarWindowBlurController,
+ mNotificationShadeWindowBlurController,
mView,
mNotificationPanelViewController);
mController.setupExpandedStatusBar();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 008a349..2e6fbe7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -521,10 +521,10 @@
Assert.assertEquals(ScrimController.TRANSPARENT,
mScrimInFront.getViewAlpha(), 0.0f);
// Back scrim should be visible
- Assert.assertEquals(ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,
+ Assert.assertEquals(ScrimController.BUSY_SCRIM_ALPHA,
mScrimBehind.getViewAlpha(), 0.0f);
// Bubble scrim should be visible
- Assert.assertEquals(ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,
+ Assert.assertEquals(ScrimController.BUSY_SCRIM_ALPHA,
mScrimBehind.getViewAlpha(), 0.0f);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 565ee63..75ec4b0 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1128,8 +1128,7 @@
@Override
public int getWindowIdForLeashToken(@NonNull IBinder token) {
synchronized (mLock) {
- // TODO: Add a method to lookup window ID by given leash token.
- return -1;
+ return mA11yWindowManager.getWindowIdLocked(token);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 7b495ce..ba29bc8 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -761,11 +761,11 @@
}
@Override
- public int addAccessibilityInteractionConnection(IWindow windowToken,
+ public int addAccessibilityInteractionConnection(IWindow windowToken, IBinder leashToken,
IAccessibilityInteractionConnection connection, String packageName,
int userId) throws RemoteException {
return mA11yWindowManager.addAccessibilityInteractionConnection(
- windowToken, connection, packageName, userId);
+ windowToken, leashToken, connection, packageName, userId);
}
@Override
@@ -2643,6 +2643,20 @@
}
@Override
+ public void associateEmbeddedHierarchy(@NonNull IBinder host, @NonNull IBinder embedded) {
+ synchronized (mLock) {
+ mA11yWindowManager.associateEmbeddedHierarchyLocked(host, embedded);
+ }
+ }
+
+ @Override
+ public void disassociateEmbeddedHierarchy(@NonNull IBinder token) {
+ synchronized (mLock) {
+ mA11yWindowManager.disassociateEmbeddedHierarchyLocked(token);
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
synchronized (mLock) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index 96e345e..d98e31e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -443,8 +443,8 @@
return false;
}
}
- // TODO: Check parent windowId if the giving windowId is from embedded view hierarchy.
- if (windowId == mAccessibilityWindowManager.getActiveWindowId(userId)) {
+ if (mAccessibilityWindowManager.resolveParentWindowIdLocked(windowId)
+ == mAccessibilityWindowManager.getActiveWindowId(userId)) {
return true;
}
return mAccessibilityWindowManager.findA11yWindowInfoByIdLocked(windowId) != null;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index a6041e0..8c00581 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -31,6 +31,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
@@ -100,6 +101,19 @@
new SparseArray<>();
/**
+ * Map of host view and embedded hierarchy, mapping from leash token of its ViewRootImpl.
+ * The key is the token from embedded hierarchy, and the value is the token from its host.
+ */
+ private final ArrayMap<IBinder, IBinder> mHostEmbeddedMap = new ArrayMap<>();
+
+ /**
+ * Map of window id and view hierarchy.
+ * The key is the window id when the ViewRootImpl register to accessibility, and the value is
+ * its leash token.
+ */
+ private final SparseArray<IBinder> mWindowIdMap = new SparseArray<>();
+
+ /**
* This class implements {@link WindowManagerInternal.WindowsForAccessibilityCallback} to
* receive {@link WindowInfo}s from window manager when there's an accessibility change in
* window and holds window lists information per display.
@@ -913,19 +927,21 @@
* Adds accessibility interaction connection according to given window token, package name and
* window token.
*
- * @param windowToken The window token of accessibility interaction connection
+ * @param window The window token of accessibility interaction connection
+ * @param leashToken The leash token of accessibility interaction connection
* @param connection The accessibility interaction connection
* @param packageName The package name
* @param userId The userId
* @return The windowId of added connection
* @throws RemoteException
*/
- public int addAccessibilityInteractionConnection(@NonNull IWindow windowToken,
- @NonNull IAccessibilityInteractionConnection connection, @NonNull String packageName,
- int userId) throws RemoteException {
+ public int addAccessibilityInteractionConnection(@NonNull IWindow window,
+ @NonNull IBinder leashToken, @NonNull IAccessibilityInteractionConnection connection,
+ @NonNull String packageName, int userId) throws RemoteException {
final int windowId;
boolean shouldComputeWindows = false;
- final int displayId = mWindowManagerInternal.getDisplayIdForWindow(windowToken.asBinder());
+ final IBinder token = window.asBinder();
+ final int displayId = mWindowManagerInternal.getDisplayIdForWindow(token);
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -947,35 +963,33 @@
windowId, connection, packageName, resolvedUid, UserHandle.USER_ALL);
wrapper.linkToDeath();
mGlobalInteractionConnections.put(windowId, wrapper);
- mGlobalWindowTokens.put(windowId, windowToken.asBinder());
+ mGlobalWindowTokens.put(windowId, token);
if (DEBUG) {
Slog.i(LOG_TAG, "Added global connection for pid:" + Binder.getCallingPid()
- + " with windowId: " + windowId + " and token: "
- + windowToken.asBinder());
+ + " with windowId: " + windowId + " and token: " + token);
}
} else {
RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection(
windowId, connection, packageName, resolvedUid, resolvedUserId);
wrapper.linkToDeath();
getInteractionConnectionsForUserLocked(resolvedUserId).put(windowId, wrapper);
- getWindowTokensForUserLocked(resolvedUserId).put(windowId, windowToken.asBinder());
+ getWindowTokensForUserLocked(resolvedUserId).put(windowId, token);
if (DEBUG) {
Slog.i(LOG_TAG, "Added user connection for pid:" + Binder.getCallingPid()
- + " with windowId: " + windowId
- + " and token: " + windowToken.asBinder());
+ + " with windowId: " + windowId + " and token: " + token);
}
}
if (isTrackingWindowsLocked(displayId)) {
shouldComputeWindows = true;
}
+ registerIdLocked(leashToken, windowId);
}
if (shouldComputeWindows) {
mWindowManagerInternal.computeWindowsForAccessibility(displayId);
}
- mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(
- windowToken.asBinder(), windowId);
+ mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(token, windowId);
return windowId;
}
@@ -1098,6 +1112,7 @@
* Invoked when accessibility interaction connection of window is removed.
*
* @param windowId Removed windowId
+ * @param binder Removed window token
*/
private void onAccessibilityInteractionConnectionRemovedLocked(
int windowId, @Nullable IBinder binder) {
@@ -1110,6 +1125,7 @@
mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(
binder, AccessibilityWindowInfo.UNDEFINED_WINDOW_ID);
}
+ unregisterIdLocked(windowId);
}
/**
@@ -1132,7 +1148,7 @@
* Returns the userId that owns the given window token, {@link UserHandle#USER_NULL}
* if not found.
*
- * @param windowToken The winodw token
+ * @param windowToken The window token
* @return The userId
*/
public int getWindowOwnerUserId(@NonNull IBinder windowToken) {
@@ -1161,6 +1177,50 @@
}
/**
+ * Establish the relationship between the host and the embedded view hierarchy.
+ *
+ * @param host The token of host hierarchy
+ * @param embedded The token of the embedded hierarchy
+ */
+ public void associateEmbeddedHierarchyLocked(@NonNull IBinder host, @NonNull IBinder embedded) {
+ // Use embedded window as key, since one host window may have multiple embedded windows.
+ associateLocked(embedded, host);
+ }
+
+ /**
+ * Clear the relationship by given token.
+ *
+ * @param token The token
+ */
+ public void disassociateEmbeddedHierarchyLocked(@NonNull IBinder token) {
+ disassociateLocked(token);
+ }
+
+ /**
+ * Gets the parent windowId of the window according to the specified windowId.
+ *
+ * @param windowId The windowId to check
+ * @return The windowId of the parent window, or self if no parent exists
+ */
+ public int resolveParentWindowIdLocked(int windowId) {
+ final IBinder token = getTokenLocked(windowId);
+ if (token == null) {
+ return windowId;
+ }
+ final IBinder resolvedToken = resolveTopParentTokenLocked(token);
+ final int resolvedWindowId = getWindowIdLocked(resolvedToken);
+ return resolvedWindowId != -1 ? resolvedWindowId : windowId;
+ }
+
+ private IBinder resolveTopParentTokenLocked(IBinder token) {
+ final IBinder hostToken = getHostTokenLocked(token);
+ if (hostToken == null) {
+ return token;
+ }
+ return resolveTopParentTokenLocked(hostToken);
+ }
+
+ /**
* Computes partial interactive region of given windowId.
*
* @param windowId The windowId
@@ -1357,6 +1417,7 @@
*/
@Nullable
public AccessibilityWindowInfo findA11yWindowInfoByIdLocked(int windowId) {
+ windowId = resolveParentWindowIdLocked(windowId);
final DisplayWindowsObserver observer = getDisplayWindowObserverByWindowIdLocked(windowId);
if (observer != null) {
return observer.findA11yWindowInfoByIdLocked(windowId);
@@ -1584,6 +1645,88 @@
}
/**
+ * Associate the token of the embedded view hierarchy to the host view hierarchy.
+ *
+ * @param embedded The leash token from the view root of embedded hierarchy
+ * @param host The leash token from the view root of host hierarchy
+ */
+ void associateLocked(IBinder embedded, IBinder host) {
+ mHostEmbeddedMap.put(embedded, host);
+ }
+
+ /**
+ * Clear the relationship of given token.
+ *
+ * @param token The leash token
+ */
+ void disassociateLocked(IBinder token) {
+ mHostEmbeddedMap.remove(token);
+ for (int i = mHostEmbeddedMap.size() - 1; i >= 0; i--) {
+ if (mHostEmbeddedMap.valueAt(i).equals(token)) {
+ mHostEmbeddedMap.removeAt(i);
+ }
+ }
+ }
+
+ /**
+ * Register the leash token with its windowId.
+ *
+ * @param token The token.
+ * @param windowId The windowID.
+ */
+ void registerIdLocked(IBinder token, int windowId) {
+ mWindowIdMap.put(windowId, token);
+ }
+
+ /**
+ * Unregister the windowId and also disassociate its token.
+ *
+ * @param windowId The windowID
+ */
+ void unregisterIdLocked(int windowId) {
+ final IBinder token = mWindowIdMap.get(windowId);
+ if (token == null) {
+ return;
+ }
+ disassociateLocked(token);
+ mWindowIdMap.remove(windowId);
+ }
+
+ /**
+ * Get the leash token by given windowID.
+ *
+ * @param windowId The windowID.
+ * @return The token, or {@code NULL} if this windowID doesn't exist
+ */
+ IBinder getTokenLocked(int windowId) {
+ return mWindowIdMap.get(windowId);
+ }
+
+ /**
+ * Get the windowId by given leash token.
+ *
+ * @param token The token
+ * @return The windowID, or -1 if the token doesn't exist
+ */
+ int getWindowIdLocked(IBinder token) {
+ final int index = mWindowIdMap.indexOfValue(token);
+ if (index == -1) {
+ return index;
+ }
+ return mWindowIdMap.keyAt(index);
+ }
+
+ /**
+ * Get the leash token of the host hierarchy by given token.
+ *
+ * @param token The token
+ * @return The token of host hierarchy, or {@code NULL} if no host exists
+ */
+ IBinder getHostTokenLocked(IBinder token) {
+ return mHostEmbeddedMap.get(token);
+ }
+
+ /**
* Dumps all {@link AccessibilityWindowInfo}s here.
*/
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
index 5844f98..1c4db12 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionManagerService.java
@@ -18,12 +18,14 @@
import static android.Manifest.permission.MANAGE_APP_PREDICTIONS;
import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.content.Context.APP_PREDICTION_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionSessionId;
import android.app.prediction.AppTargetEvent;
@@ -34,7 +36,6 @@
import android.os.Binder;
import android.os.ResultReceiver;
import android.os.ShellCallback;
-import android.os.UserHandle;
import android.util.Slog;
import com.android.server.LocalServices;
@@ -108,21 +109,21 @@
@Override
public void createPredictionSession(@NonNull AppPredictionContext context,
@NonNull AppPredictionSessionId sessionId) {
- runForUserLocked("createPredictionSession",
+ runForUserLocked("createPredictionSession", sessionId,
(service) -> service.onCreatePredictionSessionLocked(context, sessionId));
}
@Override
public void notifyAppTargetEvent(@NonNull AppPredictionSessionId sessionId,
@NonNull AppTargetEvent event) {
- runForUserLocked("notifyAppTargetEvent",
+ runForUserLocked("notifyAppTargetEvent", sessionId,
(service) -> service.notifyAppTargetEventLocked(sessionId, event));
}
@Override
public void notifyLaunchLocationShown(@NonNull AppPredictionSessionId sessionId,
@NonNull String launchLocation, @NonNull ParceledListSlice targetIds) {
- runForUserLocked("notifyLaunchLocationShown", (service) ->
+ runForUserLocked("notifyLaunchLocationShown", sessionId, (service) ->
service.notifyLaunchLocationShownLocked(sessionId, launchLocation, targetIds));
}
@@ -130,32 +131,32 @@
public void sortAppTargets(@NonNull AppPredictionSessionId sessionId,
@NonNull ParceledListSlice targets,
IPredictionCallback callback) {
- runForUserLocked("sortAppTargets",
+ runForUserLocked("sortAppTargets", sessionId,
(service) -> service.sortAppTargetsLocked(sessionId, targets, callback));
}
@Override
public void registerPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
@NonNull IPredictionCallback callback) {
- runForUserLocked("registerPredictionUpdates",
+ runForUserLocked("registerPredictionUpdates", sessionId,
(service) -> service.registerPredictionUpdatesLocked(sessionId, callback));
}
public void unregisterPredictionUpdates(@NonNull AppPredictionSessionId sessionId,
@NonNull IPredictionCallback callback) {
- runForUserLocked("unregisterPredictionUpdates",
+ runForUserLocked("unregisterPredictionUpdates", sessionId,
(service) -> service.unregisterPredictionUpdatesLocked(sessionId, callback));
}
@Override
public void requestPredictionUpdate(@NonNull AppPredictionSessionId sessionId) {
- runForUserLocked("requestPredictionUpdate",
+ runForUserLocked("requestPredictionUpdate", sessionId,
(service) -> service.requestPredictionUpdateLocked(sessionId));
}
@Override
public void onDestroyPredictionSession(@NonNull AppPredictionSessionId sessionId) {
- runForUserLocked("onDestroyPredictionSession",
+ runForUserLocked("onDestroyPredictionSession", sessionId,
(service) -> service.onDestroyPredictionSessionLocked(sessionId));
}
@@ -167,9 +168,12 @@
.exec(this, in, out, err, args, callback, resultReceiver);
}
- private void runForUserLocked(@NonNull String func,
- @NonNull Consumer<AppPredictionPerUserService> c) {
- final int userId = UserHandle.getCallingUserId();
+ private void runForUserLocked(@NonNull final String func,
+ @NonNull final AppPredictionSessionId sessionId,
+ @NonNull final Consumer<AppPredictionPerUserService> c) {
+ ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class);
+ final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ sessionId.getUserId(), false, ALLOW_NON_FULL, null, null);
Context ctx = getContext();
if (!(ctx.checkCallingPermission(PACKAGE_USAGE_STATS) == PERMISSION_GRANTED
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 0b3e3b4..2c91a11 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -2422,6 +2422,17 @@
}
@Override
+ public void setLocationEnabledForUser(boolean enabled, int userId) {
+ if (UserHandle.getCallingUserId() != userId) {
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.INTERACT_ACROSS_USERS,
+ null);
+ }
+ mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS,
+ "Requires WRITE_SECURE_SETTINGS permission");
+ mSettingsHelper.setLocationEnabled(enabled, userId);
+ }
+
+ @Override
public boolean isLocationEnabledForUser(int userId) {
userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
userId, false, false, "isLocationEnabledForUser", null);
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index a08bdb2..7bb9adf 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -342,9 +342,43 @@
*/
private final Object mPackagesLock = new Object();
+ /**
+ * mLocalUnlockedUsers affects the return value of isUserUnlocked. If
+ * any value in the array changes, then the binder cache for
+ * isUserUnlocked must be invalidated. When adding mutating methods to
+ * WatchedLockedUsers, be sure to invalidate the cache in the new
+ * methods.
+ */
+ private class WatchedLockedUsers {
+ private int[] users = EmptyArray.INT;
+ public WatchedLockedUsers() {
+ }
+ public void append(int userId) {
+ users = ArrayUtils.appendInt(users, userId);
+ invalidateIsUserUnlockedCache();
+ }
+ public void remove(int userId) {
+ users = ArrayUtils.removeInt(users, userId);
+ invalidateIsUserUnlockedCache();
+ }
+ public boolean contains(int userId) {
+ return ArrayUtils.contains(users, userId);
+ }
+ public int[] all() {
+ return users;
+ }
+ @Override
+ public String toString() {
+ return Arrays.toString(users);
+ }
+ private void invalidateIsUserUnlockedCache() {
+ UserManager.invalidateIsUserUnlockedCache();
+ }
+ }
+
/** Set of users that we know are unlocked. */
@GuardedBy("mLock")
- private int[] mLocalUnlockedUsers = EmptyArray.INT;
+ private WatchedLockedUsers mLocalUnlockedUsers = new WatchedLockedUsers();
/** Set of users that system knows are unlocked. */
@GuardedBy("mLock")
private int[] mSystemUnlockedUsers = EmptyArray.INT;
@@ -3042,7 +3076,7 @@
}
synchronized (mLock) {
- mLocalUnlockedUsers = ArrayUtils.appendInt(mLocalUnlockedUsers, userId);
+ mLocalUnlockedUsers.append(userId);
}
}
@@ -3058,14 +3092,14 @@
}
synchronized (mLock) {
- mLocalUnlockedUsers = ArrayUtils.removeInt(mLocalUnlockedUsers, userId);
+ mLocalUnlockedUsers.remove(userId);
}
}
@Override
public boolean isUserKeyUnlocked(int userId) {
synchronized (mLock) {
- return ArrayUtils.contains(mLocalUnlockedUsers, userId);
+ return mLocalUnlockedUsers.contains(userId);
}
}
@@ -4177,7 +4211,7 @@
}
pw.println();
- pw.println("Local unlocked users: " + Arrays.toString(mLocalUnlockedUsers));
+ pw.println("Local unlocked users: " + mLocalUnlockedUsers);
pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers));
final ContentResolver cr = mContext.getContentResolver();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0686e3e..b6dc3de 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -466,9 +466,18 @@
// How long we wait for a launched process to attach to the activity manager
// before we decide it's never going to come up for real.
static final int PROC_START_TIMEOUT = 10*1000;
+ // How long we wait for an attached process to publish its content providers
+ // before we decide it must be hung.
+ static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
+
// How long we wait to kill an application zygote, after the last process using
// it has gone away.
static final int KILL_APP_ZYGOTE_DELAY_MS = 5 * 1000;
+ /**
+ * How long we wait for an provider to be published. Should be longer than
+ * {@link #CONTENT_PROVIDER_PUBLISH_TIMEOUT}.
+ */
+ static final int CONTENT_PROVIDER_WAIT_TIMEOUT = 20 * 1000;
// How long we wait for a launched process to attach to the activity manager
// before we decide it's never going to come up for real, when the process was
@@ -4925,8 +4934,7 @@
if (providers != null && checkAppInLaunchingProvidersLocked(app)) {
Message msg = mHandler.obtainMessage(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
msg.obj = app;
- mHandler.sendMessageDelayed(msg,
- ContentResolver.CONTENT_PROVIDER_PUBLISH_TIMEOUT_MILLIS);
+ mHandler.sendMessageDelayed(msg, CONTENT_PROVIDER_PUBLISH_TIMEOUT);
}
checkTime(startTime, "attachApplicationLocked: before bindApplication");
@@ -7193,8 +7201,7 @@
}
// Wait for the provider to be published...
- final long timeout =
- SystemClock.uptimeMillis() + ContentResolver.CONTENT_PROVIDER_WAIT_TIMEOUT_MILLIS;
+ final long timeout = SystemClock.uptimeMillis() + CONTENT_PROVIDER_WAIT_TIMEOUT;
boolean timedOut = false;
synchronized (cpr) {
while (cpr.provider == null) {
@@ -7231,14 +7238,12 @@
}
}
if (timedOut) {
- // Note we do it after releasing the lock.
+ // Note we do it afer releasing the lock.
String callerName = "unknown";
- if (caller != null) {
- synchronized (this) {
- final ProcessRecord record = mProcessList.getLRURecordForAppLocked(caller);
- if (record != null) {
- callerName = record.processName;
- }
+ synchronized (this) {
+ final ProcessRecord record = mProcessList.getLRURecordForAppLocked(caller);
+ if (record != null) {
+ callerName = record.processName;
}
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 145f91b..789f719 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -798,7 +798,6 @@
boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
- final String packageName;
final int userId;
synchronized (mService) {
final ProcessRecord proc = data.proc;
@@ -807,7 +806,6 @@
Slog.e(TAG, "handleShowAppErrorUi: proc is null");
return;
}
- packageName = proc.info.packageName;
userId = proc.userId;
if (proc.getDialogController().hasCrashDialogs()) {
Slog.e(TAG, "App already has crash dialog: " + proc);
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index f3d8bc8c..e63da9b 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1910,8 +1910,7 @@
mWaitDialog = null;
}
- void forAllDialogs(List<? extends BaseErrorDialog> dialogs,
- Consumer<BaseErrorDialog> c) {
+ void forAllDialogs(List<? extends BaseErrorDialog> dialogs, Consumer<BaseErrorDialog> c) {
for (int i = dialogs.size() - 1; i >= 0; i--) {
c.accept(dialogs.get(i));
}
@@ -1920,42 +1919,72 @@
void showCrashDialogs(AppErrorDialog.Data data) {
List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */);
mCrashDialogs = new ArrayList<>();
-
for (int i = contexts.size() - 1; i >= 0; i--) {
final Context c = contexts.get(i);
mCrashDialogs.add(new AppErrorDialog(c, mService, data));
}
- mService.mUiHandler.post(() -> mCrashDialogs.forEach(Dialog::show));
+ mService.mUiHandler.post(() -> {
+ List<AppErrorDialog> dialogs;
+ synchronized (mService) {
+ dialogs = mCrashDialogs;
+ }
+ if (dialogs != null) {
+ forAllDialogs(dialogs, Dialog::show);
+ }
+ });
}
void showAnrDialogs(AppNotRespondingDialog.Data data) {
List<Context> contexts = getDisplayContexts(isSilentAnr() /* lastUsedOnly */);
mAnrDialogs = new ArrayList<>();
-
for (int i = contexts.size() - 1; i >= 0; i--) {
final Context c = contexts.get(i);
mAnrDialogs.add(new AppNotRespondingDialog(mService, c, data));
}
- mService.mUiHandler.post(() -> mAnrDialogs.forEach(Dialog::show));
+ mService.mUiHandler.post(() -> {
+ List<AppNotRespondingDialog> dialogs;
+ synchronized (mService) {
+ dialogs = mAnrDialogs;
+ }
+ if (dialogs != null) {
+ forAllDialogs(dialogs, Dialog::show);
+ }
+ });
}
void showViolationDialogs(AppErrorResult res) {
List<Context> contexts = getDisplayContexts(false /* lastUsedOnly */);
mViolationDialogs = new ArrayList<>();
-
for (int i = contexts.size() - 1; i >= 0; i--) {
final Context c = contexts.get(i);
mViolationDialogs.add(
new StrictModeViolationDialog(c, mService, res, ProcessRecord.this));
}
- mService.mUiHandler.post(() -> mViolationDialogs.forEach(Dialog::show));
+ mService.mUiHandler.post(() -> {
+ List<StrictModeViolationDialog> dialogs;
+ synchronized (mService) {
+ dialogs = mViolationDialogs;
+ }
+ if (dialogs != null) {
+ forAllDialogs(dialogs, Dialog::show);
+ }
+ });
}
void showDebugWaitingDialogs() {
List<Context> contexts = getDisplayContexts(true /* lastUsedOnly */);
final Context c = contexts.get(0);
mWaitDialog = new AppWaitingForDebuggerDialog(mService, c, ProcessRecord.this);
- mService.mUiHandler.post(() -> mWaitDialog.show());
+
+ mService.mUiHandler.post(() -> {
+ Dialog dialog;
+ synchronized (mService) {
+ dialog = mWaitDialog;
+ }
+ if (dialog != null) {
+ dialog.show();
+ }
+ });
}
/**
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index c75ee04..b2d0441 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -414,6 +414,7 @@
Intent intent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED, null);
intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
+ | Intent.FLAG_RECEIVER_OFFLOAD
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
mInjector.broadcastIntent(intent, null, resultTo, 0, null, null,
new String[]{android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 1f998c3..ef1bc83 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -702,6 +702,10 @@
delay = 0;
}
mDeviceBroker.postSetHearingAidConnectionState(state, device, delay);
+ if (state == BluetoothHearingAid.STATE_CONNECTED) {
+ mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, AudioSystem.FORCE_NONE,
+ "HEARING_AID set to CONNECTED");
+ }
return delay;
}
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 07fc9b7..26c94c5 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -25,6 +25,7 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import static android.hardware.biometrics.BiometricManager.Authenticators;
+import android.annotation.IntDef;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.UserSwitchObserver;
@@ -83,6 +84,25 @@
static final String TAG = "BiometricService";
private static final boolean DEBUG = true;
+ private static final int BIOMETRIC_NO_HARDWARE = 0;
+ private static final int BIOMETRIC_OK = 1;
+ private static final int BIOMETRIC_DISABLED_BY_DEVICE_POLICY = 2;
+ private static final int BIOMETRIC_INSUFFICIENT_STRENGTH = 3;
+ private static final int BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE = 4;
+ private static final int BIOMETRIC_HARDWARE_NOT_DETECTED = 5;
+ private static final int BIOMETRIC_NOT_ENROLLED = 6;
+ private static final int BIOMETRIC_NOT_ENABLED_FOR_APPS = 7;
+
+ @IntDef({BIOMETRIC_NO_HARDWARE,
+ BIOMETRIC_OK,
+ BIOMETRIC_DISABLED_BY_DEVICE_POLICY,
+ BIOMETRIC_INSUFFICIENT_STRENGTH,
+ BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE,
+ BIOMETRIC_HARDWARE_NOT_DETECTED,
+ BIOMETRIC_NOT_ENROLLED,
+ BIOMETRIC_NOT_ENABLED_FOR_APPS})
+ @interface BiometricStatus {}
+
private static final int MSG_ON_AUTHENTICATION_SUCCEEDED = 2;
private static final int MSG_ON_AUTHENTICATION_REJECTED = 3;
private static final int MSG_ON_ERROR = 4;
@@ -206,7 +226,7 @@
}
boolean isAllowDeviceCredential() {
- return Utils.isDeviceCredentialAllowed(mBundle);
+ return Utils.isCredentialRequested(mBundle);
}
}
@@ -372,16 +392,20 @@
* strength.
* @return a bitfield, see {@link Authenticators}
*/
- public int getActualStrength() {
+ int getActualStrength() {
return OEMStrength | updatedStrength;
}
+ boolean isDowngraded() {
+ return OEMStrength != updatedStrength;
+ }
+
/**
* Stores the updated strength, which takes effect whenever {@link #getActualStrength()}
* is checked.
* @param newStrength
*/
- public void updateStrength(int newStrength) {
+ void updateStrength(int newStrength) {
String log = "updateStrength: Before(" + toString() + ")";
updatedStrength = newStrength;
log += " After(" + toString() + ")";
@@ -1007,6 +1031,79 @@
return isBiometricDisabled;
}
+ private static int biometricStatusToBiometricConstant(@BiometricStatus int status) {
+ switch (status) {
+ case BIOMETRIC_NO_HARDWARE:
+ return BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
+ case BIOMETRIC_OK:
+ return BiometricConstants.BIOMETRIC_SUCCESS;
+ case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
+ return BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+ case BIOMETRIC_INSUFFICIENT_STRENGTH:
+ return BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
+ case BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE:
+ return BiometricConstants.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED;
+ case BIOMETRIC_HARDWARE_NOT_DETECTED:
+ return BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+ case BIOMETRIC_NOT_ENROLLED:
+ return BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS;
+ case BIOMETRIC_NOT_ENABLED_FOR_APPS:
+ return BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+ default:
+ return BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+ }
+ }
+
+ /**
+ * Returns the status of the authenticator, with errors returned in a specific priority order.
+ * For example, {@link #BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE} is only returned
+ * if it has enrollments, and is enabled for apps.
+ *
+ * We should only return the modality if the authenticator should be exposed. e.g.
+ * BIOMETRIC_NOT_ENROLLED_FOR_APPS should not expose the authenticator's type.
+ *
+ * @return A Pair with `first` being modality, and `second` being @BiometricStatus
+ */
+ private Pair<Integer, Integer> getStatusForBiometricAuthenticator(
+ AuthenticatorWrapper authenticator, int userId, String opPackageName,
+ boolean checkDevicePolicyManager, int requestedStrength) {
+ if (checkDevicePolicyManager) {
+ if (isBiometricDisabledByDevicePolicy(authenticator.modality, userId)) {
+ return new Pair<>(TYPE_NONE, BIOMETRIC_DISABLED_BY_DEVICE_POLICY);
+ }
+ }
+
+ final boolean wasStrongEnough =
+ Utils.isAtLeastStrength(authenticator.OEMStrength, requestedStrength);
+ final boolean isStrongEnough =
+ Utils.isAtLeastStrength(authenticator.getActualStrength(), requestedStrength);
+
+ if (wasStrongEnough && !isStrongEnough) {
+ return new Pair<>(authenticator.modality,
+ BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE);
+ } else if (!wasStrongEnough) {
+ return new Pair<>(TYPE_NONE, BIOMETRIC_INSUFFICIENT_STRENGTH);
+ }
+
+ try {
+ if (!authenticator.impl.isHardwareDetected(opPackageName)) {
+ return new Pair<>(authenticator.modality, BIOMETRIC_HARDWARE_NOT_DETECTED);
+ }
+
+ if (!authenticator.impl.hasEnrolledTemplates(userId, opPackageName)) {
+ return new Pair<>(authenticator.modality, BIOMETRIC_NOT_ENROLLED);
+ }
+ } catch (RemoteException e) {
+ return new Pair<>(authenticator.modality, BIOMETRIC_HARDWARE_NOT_DETECTED);
+ }
+
+ if (!isEnabledForApp(authenticator.modality, userId)) {
+ return new Pair<>(TYPE_NONE, BIOMETRIC_NOT_ENABLED_FOR_APPS);
+ }
+
+ return new Pair<>(authenticator.modality, BIOMETRIC_OK);
+ }
+
/**
* Depending on the requested authentication (credential/biometric combination), checks their
* availability.
@@ -1029,10 +1126,9 @@
private Pair<Integer, Integer> checkAndGetAuthenticators(int userId, Bundle bundle,
String opPackageName, boolean checkDevicePolicyManager) throws RemoteException {
- final boolean biometricRequested = Utils.isBiometricAllowed(bundle);
- final boolean credentialRequested = Utils.isDeviceCredentialAllowed(bundle);
+ final boolean biometricRequested = Utils.isBiometricRequested(bundle);
+ final boolean credentialRequested = Utils.isCredentialRequested(bundle);
- final boolean biometricOk;
final boolean credentialOk = mTrustManager.isDeviceSecure(userId);
// Assuming that biometric authenticators are listed in priority-order, the rest of this
@@ -1041,96 +1137,56 @@
// the correct error. Error strings that are modality-specific should also respect the
// priority-order.
- // Find first biometric authenticator that's strong enough, detected, enrolled, and enabled.
- boolean disabledByDevicePolicy = false;
- boolean hasSufficientStrength = false;
- boolean isHardwareDetected = false;
- boolean hasTemplatesEnrolled = false;
- boolean enabledForApps = false;
+ int firstBiometricModality = TYPE_NONE;
+ @BiometricStatus int firstBiometricStatus = BIOMETRIC_NO_HARDWARE;
- int modality = TYPE_NONE;
- int firstHwAvailable = TYPE_NONE;
+ int biometricModality = TYPE_NONE;
+ @BiometricStatus int biometricStatus = BIOMETRIC_NO_HARDWARE;
+
for (AuthenticatorWrapper authenticator : mAuthenticators) {
- final int actualStrength = authenticator.getActualStrength();
final int requestedStrength = Utils.getPublicBiometricStrength(bundle);
+ Pair<Integer, Integer> result = getStatusForBiometricAuthenticator(
+ authenticator, userId, opPackageName, checkDevicePolicyManager,
+ requestedStrength);
- if (isBiometricDisabledByDevicePolicy(authenticator.modality, userId)) {
- disabledByDevicePolicy = true;
- continue;
- }
- disabledByDevicePolicy = false;
+ biometricStatus = result.second;
- if (!Utils.isAtLeastStrength(actualStrength, requestedStrength)) {
- continue;
- }
- hasSufficientStrength = true;
+ Slog.d(TAG, "Authenticator ID: " + authenticator.id
+ + " Modality: " + authenticator.modality
+ + " ReportedModality: " + result.first
+ + " Status: " + biometricStatus);
- if (!authenticator.impl.isHardwareDetected(opPackageName)) {
- continue;
- }
- isHardwareDetected = true;
-
- if (firstHwAvailable == TYPE_NONE) {
- // Store the first one since we want to return the error in correct
- // priority order.
- firstHwAvailable = authenticator.modality;
+ if (firstBiometricModality == TYPE_NONE) {
+ firstBiometricModality = result.first;
+ firstBiometricStatus = biometricStatus;
}
- if (!authenticator.impl.hasEnrolledTemplates(userId, opPackageName)) {
- continue;
+ if (biometricStatus == BIOMETRIC_OK) {
+ biometricModality = result.first;
+ break;
}
- hasTemplatesEnrolled = true;
-
- if (!isEnabledForApp(authenticator.modality, userId)) {
- continue;
- }
- enabledForApps = true;
- modality = authenticator.modality;
- break;
}
- biometricOk = !disabledByDevicePolicy
- && hasSufficientStrength && isHardwareDetected
- && hasTemplatesEnrolled && enabledForApps;
-
- Slog.d(TAG, "checkAndGetAuthenticators: user=" + userId
- + " checkDevicePolicyManager=" + checkDevicePolicyManager
- + " isHardwareDetected=" + isHardwareDetected
- + " hasTemplatesEnrolled=" + hasTemplatesEnrolled
- + " enabledForApps=" + enabledForApps
- + " disabledByDevicePolicy=" + disabledByDevicePolicy);
-
if (biometricRequested && credentialRequested) {
- if (credentialOk || biometricOk) {
- if (!biometricOk) {
+ if (credentialOk || biometricStatus == BIOMETRIC_OK) {
+ if (biometricStatus != BIOMETRIC_OK) {
// If there's a problem with biometrics but device credential is
// allowed, only show credential UI.
bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED,
Authenticators.DEVICE_CREDENTIAL);
}
- return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
+ return new Pair<>(biometricModality, BiometricConstants.BIOMETRIC_SUCCESS);
} else {
- return new Pair<>(firstHwAvailable,
+ return new Pair<>(firstBiometricModality,
BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
}
} else if (biometricRequested) {
- if (biometricOk) {
- return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
- } else if (disabledByDevicePolicy) {
- return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
- } else if (!hasSufficientStrength) {
- return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
- } else if (!isHardwareDetected) {
- return new Pair<>(firstHwAvailable,
- BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
- } else if (!hasTemplatesEnrolled) {
- return new Pair<>(firstHwAvailable,
- BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
- } else if (!enabledForApps) {
- return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+ if (biometricStatus == BIOMETRIC_OK) {
+ return new Pair<>(biometricModality,
+ biometricStatusToBiometricConstant(biometricStatus));
} else {
- Slog.e(TAG, "Unexpected case");
- return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+ return new Pair<>(firstBiometricModality,
+ biometricStatusToBiometricConstant(firstBiometricStatus));
}
} else if (credentialRequested) {
if (credentialOk) {
@@ -1403,16 +1459,14 @@
return;
}
- if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
- if (message == null) {
- Slog.w(TAG, "Ignoring null message: " + acquiredInfo);
- return;
- }
- try {
- mStatusBarService.onBiometricHelp(message);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
+ if (message == null) {
+ Slog.w(TAG, "Ignoring null message: " + acquiredInfo);
+ return;
+ }
+ try {
+ mStatusBarService.onBiometricHelp(message);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
}
}
diff --git a/services/core/java/com/android/server/biometrics/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/LoggableMonitor.java
index c03c77f..c50ab17 100644
--- a/services/core/java/com/android/server/biometrics/LoggableMonitor.java
+++ b/services/core/java/com/android/server/biometrics/LoggableMonitor.java
@@ -20,6 +20,7 @@
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.FingerprintManager;
import android.util.Slog;
import com.android.internal.util.FrameworkStatsLog;
@@ -69,8 +70,12 @@
protected final void logOnAcquired(Context context, int acquiredInfo, int vendorCode,
int targetUserId) {
- if (statsModality() == BiometricsProtoEnums.MODALITY_FACE) {
- if (acquiredInfo == FaceManager.FACE_ACQUIRED_START) {
+
+ final boolean isFace = statsModality() == BiometricsProtoEnums.MODALITY_FACE;
+ final boolean isFingerprint = statsModality() == BiometricsProtoEnums.MODALITY_FINGERPRINT;
+ if (isFace || isFingerprint) {
+ if ((isFingerprint && acquiredInfo == FingerprintManager.FINGERPRINT_ACQUIRED_START)
+ || (isFace && acquiredInfo == FaceManager.FACE_ACQUIRED_START)) {
mFirstAcquireTimeMs = System.currentTimeMillis();
}
} else if (acquiredInfo == BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
@@ -124,7 +129,7 @@
error,
vendorCode,
Utils.isDebugEnabled(context, targetUserId),
- latency);
+ sanitizeLatency(latency));
}
protected final void logOnAuthenticated(Context context, boolean authenticated,
@@ -165,7 +170,7 @@
statsClient(),
requireConfirmation,
authState,
- latency,
+ sanitizeLatency(latency),
Utils.isDebugEnabled(context, targetUserId));
}
@@ -183,8 +188,16 @@
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENROLLED,
statsModality(),
targetUserId,
- latency,
+ sanitizeLatency(latency),
enrollSuccessful);
}
+ private long sanitizeLatency(long latency) {
+ if (latency < 0) {
+ Slog.w(TAG, "found a negative latency : " + latency);
+ return -1;
+ }
+ return latency;
+ }
+
}
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 2d4ab63..8f3fd36 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -80,7 +80,7 @@
* @param authenticators composed of one or more values from {@link Authenticators}
* @return true if device credential is allowed.
*/
- public static boolean isDeviceCredentialAllowed(@Authenticators.Types int authenticators) {
+ public static boolean isCredentialRequested(@Authenticators.Types int authenticators) {
return (authenticators & Authenticators.DEVICE_CREDENTIAL) != 0;
}
@@ -88,8 +88,8 @@
* @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
* @return true if device credential is allowed.
*/
- public static boolean isDeviceCredentialAllowed(Bundle bundle) {
- return isDeviceCredentialAllowed(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
+ public static boolean isCredentialRequested(Bundle bundle) {
+ return isCredentialRequested(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
}
/**
@@ -120,7 +120,7 @@
* @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
* @return true if biometric authentication is allowed.
*/
- public static boolean isBiometricAllowed(Bundle bundle) {
+ public static boolean isBiometricRequested(Bundle bundle) {
return getPublicBiometricStrength(bundle) != 0;
}
@@ -169,7 +169,7 @@
// should be set.
final int biometricBits = authenticators & Authenticators.BIOMETRIC_MIN_STRENGTH;
if (biometricBits == Authenticators.EMPTY_SET
- && isDeviceCredentialAllowed(authenticators)) {
+ && isCredentialRequested(authenticators)) {
return true;
} else if (biometricBits == Authenticators.BIOMETRIC_STRONG) {
return true;
@@ -209,6 +209,9 @@
case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE;
break;
+ case BiometricConstants.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED:
+ biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED;
+ break;
default:
Slog.e(BiometricService.TAG, "Unhandled result code: " + biometricConstantsCode);
biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 57d1867..0a61988 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -41,7 +41,7 @@
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
+import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
@@ -601,6 +601,11 @@
@Override
public void onAcquired(final long deviceId, final int acquiredInfo, final int vendorCode) {
+ onAcquired_2_2(deviceId, acquiredInfo, vendorCode);
+ }
+
+ @Override
+ public void onAcquired_2_2(long deviceId, int acquiredInfo, int vendorCode) {
mHandler.post(() -> {
FingerprintService.super.handleAcquired(deviceId, acquiredInfo, vendorCode);
});
diff --git a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java b/services/core/java/com/android/server/integrity/model/ComponentBitSize.java
index c389963..94e6708 100644
--- a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java
+++ b/services/core/java/com/android/server/integrity/model/ComponentBitSize.java
@@ -29,13 +29,14 @@
public static final int KEY_BITS = 4;
public static final int OPERATOR_BITS = 3;
public static final int CONNECTOR_BITS = 2;
- public static final int SEPARATOR_BITS = 2;
+ public static final int SEPARATOR_BITS = 3;
public static final int VALUE_SIZE_BITS = 8;
public static final int IS_HASHED_BITS = 1;
public static final int ATOMIC_FORMULA_START = 0;
public static final int COMPOUND_FORMULA_START = 1;
public static final int COMPOUND_FORMULA_END = 2;
+ public static final int INSTALLER_ALLOWED_BY_MANIFEST_START = 3;
public static final int DEFAULT_FORMAT_VERSION = 1;
public static final int SIGNAL_BIT = 1;
diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
index 4b8efaf..11e8d91 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
@@ -23,6 +23,7 @@
import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.INSTALLER_ALLOWED_BY_MANIFEST_START;
import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS;
import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
@@ -35,6 +36,7 @@
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
+import android.content.integrity.InstallerAllowedByManifestFormula;
import android.content.integrity.IntegrityFormula;
import android.content.integrity.Rule;
@@ -140,6 +142,8 @@
return parseCompoundFormula(bitInputStream);
case COMPOUND_FORMULA_END:
return null;
+ case INSTALLER_ALLOWED_BY_MANIFEST_START:
+ return new InstallerAllowedByManifestFormula();
default:
throw new IllegalArgumentException(
String.format("Unknown formula separator: %s", separator));
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
index 00e0545..8ba5870 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
@@ -23,6 +23,7 @@
import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION;
import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.INSTALLER_ALLOWED_BY_MANIFEST_START;
import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
@@ -36,6 +37,7 @@
import android.content.integrity.AtomicFormula;
import android.content.integrity.CompoundFormula;
+import android.content.integrity.InstallerAllowedByManifestFormula;
import android.content.integrity.IntegrityFormula;
import android.content.integrity.IntegrityUtils;
import android.content.integrity.Rule;
@@ -202,6 +204,8 @@
serializeAtomicFormula((AtomicFormula) formula, bitOutputStream);
} else if (formula instanceof CompoundFormula) {
serializeCompoundFormula((CompoundFormula) formula, bitOutputStream);
+ } else if (formula instanceof InstallerAllowedByManifestFormula) {
+ bitOutputStream.setNext(SEPARATOR_BITS, INSTALLER_ALLOWED_BY_MANIFEST_START);
} else {
throw new IllegalArgumentException(
String.format("Invalid formula type: %s", formula.getClass()));
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
index 6f7d172..e723559 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifier.java
@@ -84,6 +84,7 @@
return getIndexingDetailsForStringAtomicFormula(
(AtomicFormula.StringAtomicFormula) formula);
case IntegrityFormula.LONG_ATOMIC_FORMULA_TAG:
+ case IntegrityFormula.INSTALLER_ALLOWED_BY_MANIFEST_FORMULA_TAG:
case IntegrityFormula.BOOLEAN_ATOMIC_FORMULA_TAG:
// Package name and app certificate related formulas are string atomic formulas.
return new RuleIndexingDetails(NOT_INDEXED);
diff --git a/services/core/java/com/android/server/location/SettingsHelper.java b/services/core/java/com/android/server/location/SettingsHelper.java
index 9163490..6d1d1f9 100644
--- a/services/core/java/com/android/server/location/SettingsHelper.java
+++ b/services/core/java/com/android/server/location/SettingsHelper.java
@@ -135,6 +135,24 @@
}
/**
+ * Set location enabled for a user.
+ */
+ public void setLocationEnabled(boolean enabled, int userId) {
+ long identity = Binder.clearCallingIdentity();
+ try {
+ Settings.Secure.putIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.LOCATION_MODE,
+ enabled
+ ? Settings.Secure.LOCATION_MODE_ON
+ : Settings.Secure.LOCATION_MODE_OFF,
+ userId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
* Add a listener for changes to the location enabled setting. Callbacks occur on an unspecified
* thread.
*/
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index b726e57..ac49fa2 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -590,8 +590,8 @@
throw new IllegalStateException("Failed to create new SID for user", e);
}
if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
- Slog.e(TAG, "Fail to create new SID for user " + userId);
- return;
+ throw new IllegalStateException("Fail to create new SID for user " + userId
+ + " response: " + response.getResponseCode());
}
saveSyntheticPasswordHandle(response.getPayload(), userId);
}
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index 061cbd8..dc61fb0 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -231,6 +231,9 @@
public void disableHistory() {
synchronized (mLock) {
+ for (AtomicFile file : mHistoryFiles) {
+ file.delete();
+ }
mHistoryDir.delete();
mHistoryFiles.clear();
}
@@ -249,6 +252,10 @@
final AtomicFile currentOldestFile = mHistoryFiles.get(i);
final long creationTime =
mFileAttrProvider.getCreationTime(currentOldestFile.getBaseFile());
+ if (DEBUG) {
+ Slog.d(TAG, "Pruning " + currentOldestFile.getBaseFile().getName()
+ + " created on " + creationTime);
+ }
if (creationTime <= retentionBoundary.getTimeInMillis()) {
if (DEBUG) {
Slog.d(TAG, "Removed " + currentOldestFile.getBaseFile().getName());
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 1c02161..6e4ec7e 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -462,8 +462,42 @@
@GuardedBy("mUsersLock")
private boolean mForceEphemeralUsers;
+ /**
+ * The member mUserStates affects the return value of isUserUnlocked.
+ * If any value in mUserStates changes, then the binder cache for
+ * isUserUnlocked must be invalidated. When adding mutating methods to
+ * WatchedUserStates, be sure to invalidate the cache in the new
+ * methods.
+ */
+ private class WatchedUserStates {
+ final SparseIntArray states;
+ public WatchedUserStates() {
+ states = new SparseIntArray();
+ }
+ public int get(int userId) {
+ return states.get(userId);
+ }
+ public int get(int userId, int fallback) {
+ return states.indexOfKey(userId) >= 0 ? states.get(userId) : fallback;
+ }
+ public void put(int userId, int state) {
+ states.put(userId, state);
+ invalidateIsUserUnlockedCache();
+ }
+ public void delete(int userId) {
+ states.delete(userId);
+ invalidateIsUserUnlockedCache();
+ }
+ @Override
+ public String toString() {
+ return states.toString();
+ }
+ private void invalidateIsUserUnlockedCache() {
+ UserManager.invalidateIsUserUnlockedCache();
+ }
+ }
@GuardedBy("mUserStates")
- private final SparseIntArray mUserStates = new SparseIntArray();
+ private final WatchedUserStates mUserStates = new WatchedUserStates();
private static UserManagerService sInstance;
@@ -4801,6 +4835,11 @@
|| (state == UserState.STATE_RUNNING_UNLOCKED);
}
+ /**
+ * The return values of this method are cached in clients. If the
+ * logic in this function changes then the cache invalidation code
+ * may need to be revisited.
+ */
@Override
public boolean isUserUnlocked(@UserIdInt int userId) {
int state;
diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
index 77bb48e..4c40448 100644
--- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
+++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java
@@ -89,7 +89,7 @@
* <ul>
* <li> 0 - disable whitelist (install all system packages; no logging)</li>
* <li> 1 - enforce (only install system packages if they are whitelisted)</li>
- * <li> 2 - log (log when a non-whitelisted package is run)</li>
+ * <li> 2 - log (log non-whitelisted packages)</li>
* <li> 4 - for all users: implicitly whitelist any package not mentioned in the whitelist</li>
* <li> 8 - for SYSTEM: implicitly whitelist any package not mentioned in the whitelist</li>
* <li> 16 - ignore OTAs (don't install system packages during OTAs)</li>
diff --git a/services/core/java/com/android/server/power/PreRebootLogger.java b/services/core/java/com/android/server/power/PreRebootLogger.java
new file mode 100644
index 0000000..cda00b4
--- /dev/null
+++ b/services/core/java/com/android/server/power/PreRebootLogger.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Environment;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileWriter;
+import java.io.IOException;
+
+/**
+ * Provides utils to dump/wipe pre-reboot information.
+ */
+final class PreRebootLogger {
+ private static final String TAG = "PreRebootLogger";
+ private static final String PREREBOOT_DIR = "prereboot";
+
+ private static final String[] BUFFERS_TO_DUMP = {"system"};
+ private static final String[] SERVICES_TO_DUMP = {Context.ROLLBACK_SERVICE, "package"};
+
+ private static final Object sLock = new Object();
+
+ /**
+ * Process pre-reboot information. Dump pre-reboot information to {@link #PREREBOOT_DIR} if
+ * enabled {@link Settings.Global#ADB_ENABLED}; wipe dumped information otherwise.
+ */
+ static void log(Context context) {
+ log(context, getDumpDir());
+ }
+
+ @VisibleForTesting
+ static void log(Context context, @NonNull File dumpDir) {
+ if (Settings.Global.getInt(
+ context.getContentResolver(), Settings.Global.ADB_ENABLED, 0) == 1) {
+ Slog.d(TAG, "Dumping pre-reboot information...");
+ dump(dumpDir);
+ } else {
+ Slog.d(TAG, "Wiping pre-reboot information...");
+ wipe(dumpDir);
+ }
+ }
+
+ private static void dump(@NonNull File dumpDir) {
+ synchronized (sLock) {
+ for (String buffer : BUFFERS_TO_DUMP) {
+ dumpLogsLocked(dumpDir, buffer);
+ }
+ for (String service : SERVICES_TO_DUMP) {
+ dumpServiceLocked(dumpDir, service);
+ }
+ }
+ }
+
+ private static void wipe(@NonNull File dumpDir) {
+ synchronized (sLock) {
+ for (File file : dumpDir.listFiles()) {
+ file.delete();
+ }
+ }
+ }
+
+ private static File getDumpDir() {
+ final File dumpDir = new File(Environment.getDataMiscDirectory(), PREREBOOT_DIR);
+ if (!dumpDir.exists() || !dumpDir.isDirectory()) {
+ throw new UnsupportedOperationException("Pre-reboot dump directory not found");
+ }
+ return dumpDir;
+ }
+
+ @GuardedBy("sLock")
+ private static void dumpLogsLocked(@NonNull File dumpDir, @NonNull String buffer) {
+ try {
+ final File dumpFile = new File(dumpDir, buffer);
+ if (dumpFile.createNewFile()) {
+ dumpFile.setWritable(true /* writable */, true /* ownerOnly */);
+ } else {
+ // Wipes dumped information in existing file before recording new information.
+ new FileWriter(dumpFile, false).flush();
+ }
+
+ final String[] cmdline =
+ {"logcat", "-d", "-b", buffer, "-f", dumpFile.getAbsolutePath()};
+ Runtime.getRuntime().exec(cmdline).waitFor();
+ } catch (IOException | InterruptedException e) {
+ Slog.d(TAG, "Dump system log buffer before reboot fail", e);
+ }
+ }
+
+ @GuardedBy("sLock")
+ private static void dumpServiceLocked(@NonNull File dumpDir, @NonNull String serviceName) {
+ final IBinder binder = ServiceManager.checkService(serviceName);
+ if (binder == null) {
+ return;
+ }
+
+ try {
+ final File dumpFile = new File(dumpDir, serviceName);
+ final ParcelFileDescriptor fd = ParcelFileDescriptor.open(dumpFile,
+ ParcelFileDescriptor.MODE_CREATE | ParcelFileDescriptor.MODE_TRUNCATE
+ | ParcelFileDescriptor.MODE_WRITE_ONLY);
+ binder.dump(fd.getFileDescriptor(), ArrayUtils.emptyArray(String.class));
+ } catch (FileNotFoundException | RemoteException e) {
+ Slog.d(TAG, String.format("Dump %s service before reboot fail", serviceName), e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index cc1cddd..bc722f1 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -44,6 +44,7 @@
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Slog;
import android.util.TimingsTraceLog;
import android.view.WindowManager;
@@ -446,6 +447,15 @@
SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
}
+ shutdownTimingLog.traceBegin("DumpPreRebootInfo");
+ try {
+ Slog.i(TAG, "Logging pre-reboot information...");
+ PreRebootLogger.log(mContext);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to log pre-reboot information", e);
+ }
+ shutdownTimingLog.traceEnd(); // DumpPreRebootInfo
+
metricStarted(METRIC_SEND_BROADCAST);
shutdownTimingLog.traceBegin("SendShutdownBroadcast");
Log.i(TAG, "Sending shutdown broadcast...");
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 5abd9f0..7b96777 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -181,15 +181,6 @@
private int mNumPackageSessionsWithSuccess;
/**
- * A temp flag to facilitate merging of the 2 rollback collections managed by
- * RollbackManagerServiceImpl. True if this rollback is in the process of enabling and was
- * originally managed by RollbackManagerServiceImpl#mNewRollbacks.
- * TODO: remove this flag when merge is completed.
- */
- @GuardedBy("mLock")
- private boolean mIsNewRollback = false;
-
- /**
* Constructs a new, empty Rollback instance.
*
* @param rollbackId the id of the rollback.
@@ -837,18 +828,6 @@
}
}
- void setIsNewRollback(boolean newRollback) {
- synchronized (mLock) {
- mIsNewRollback = newRollback;
- }
- }
-
- boolean isNewRollback() {
- synchronized (mLock) {
- return mIsNewRollback;
- }
- }
-
static String rollbackStateToString(@RollbackState int state) {
switch (state) {
case Rollback.ROLLBACK_STATE_ENABLING: return "enabling";
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 1421258..91e7cc9 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -164,8 +164,16 @@
// Load rollback data from device storage.
synchronized (mLock) {
mRollbacks = mRollbackStore.loadRollbacks();
- for (Rollback rollback : mRollbacks) {
- mAllocatedRollbackIds.put(rollback.info.getRollbackId(), true);
+ if (!context.getPackageManager().isDeviceUpgrading()) {
+ for (Rollback rollback : mRollbacks) {
+ mAllocatedRollbackIds.put(rollback.info.getRollbackId(), true);
+ }
+ } else {
+ // Delete rollbacks when build fingerprint has changed.
+ for (Rollback rollback : mRollbacks) {
+ rollback.delete(mAppDataRollbackHelper);
+ }
+ mRollbacks.clear();
}
}
@@ -788,14 +796,13 @@
Rollback newRollback;
synchronized (mLock) {
- // See if we already have a NewRollback that contains this package
- // session. If not, create a NewRollback for the parent session
+ // See if we already have a Rollback that contains this package
+ // session. If not, create a new Rollback for the parent session
// that we will use for all the packages in the session.
- newRollback = getNewRollbackForPackageSessionLocked(packageSession.getSessionId());
+ newRollback = getRollbackForSessionLocked(packageSession.getSessionId());
if (newRollback == null) {
newRollback = createNewRollbackLocked(parentSession);
mRollbacks.add(newRollback);
- newRollback.setIsNewRollback(true);
}
}
newRollback.addToken(token);
@@ -1148,24 +1155,23 @@
}
if (success) {
- Rollback newRollback;
+ Rollback rollback;
synchronized (mLock) {
- newRollback = getNewRollbackForPackageSessionLocked(sessionId);
- if (newRollback != null && newRollback.notifySessionWithSuccess()) {
- mRollbacks.remove(newRollback);
- newRollback.setIsNewRollback(false);
- } else {
- // Not all child sessions finished with success.
- // Don't enable the rollback yet.
- newRollback = null;
+ rollback = getRollbackForSessionLocked(sessionId);
+ if (rollback == null || rollback.isStaged() || !rollback.isEnabling()
+ || !rollback.notifySessionWithSuccess()) {
+ return;
}
+ // All child sessions finished with success. We can enable this rollback now.
+ // TODO: refactor #completeEnableRollback so we won't remove 'rollback' from
+ // mRollbacks here and add it back in #completeEnableRollback later.
+ mRollbacks.remove(rollback);
}
-
- if (newRollback != null) {
- Rollback rollback = completeEnableRollback(newRollback);
- if (rollback != null && !rollback.isStaged()) {
- makeRollbackAvailable(rollback);
- }
+ // TODO: Now #completeEnableRollback returns the same rollback object as the
+ // parameter on success. It would be more readable to return a boolean to indicate
+ // success or failure.
+ if (completeEnableRollback(rollback) != null) {
+ makeRollbackAvailable(rollback);
}
} else {
synchronized (mLock) {
@@ -1354,22 +1360,4 @@
}
return null;
}
-
- /**
- * Returns the NewRollback associated with the given package session.
- * Returns null if no NewRollback is found for the given package
- * session.
- */
- @WorkerThread
- @GuardedBy("mLock")
- Rollback getNewRollbackForPackageSessionLocked(int packageSessionId) {
- // We expect mRollbacks to be a very small list; linear search
- // should be plenty fast.
- for (Rollback rollback: mRollbacks) {
- if (rollback.isNewRollback() && rollback.containsSessionId(packageSessionId)) {
- return rollback;
- }
- }
- return null;
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
index fbb55fd..5a96347 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilitySecurityPolicyTest.java
@@ -25,7 +25,9 @@
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
+import static org.mockito.AdditionalAnswers.returnsFirstArg;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -133,6 +135,8 @@
mA11ySecurityPolicy = new AccessibilitySecurityPolicy(mMockContext, mMockA11yUserManager);
mA11ySecurityPolicy.setAccessibilityWindowManager(mMockA11yWindowManager);
mA11ySecurityPolicy.setAppWidgetManager(mMockAppWidgetManager);
+
+ when(mMockA11yWindowManager.resolveParentWindowIdLocked(anyInt())).then(returnsFirstArg());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index 9db5a08..10a86f9 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -88,6 +88,10 @@
private static final int DEFAULT_FOCUSED_INDEX = 1;
private static final int SCREEN_WIDTH = 1080;
private static final int SCREEN_HEIGHT = 1920;
+ private static final int INVALID_ID = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
+ private static final int HOST_WINDOW_ID = 10;
+ private static final int EMBEDDED_WINDOW_ID = 11;
+ private static final int OTHER_WINDOW_ID = 12;
private AccessibilityWindowManager mA11yWindowManager;
// Window manager will support multiple focused window if config_perDisplayFocusEnabled is true,
@@ -117,6 +121,10 @@
@Mock private AccessibilitySecurityPolicy mMockA11ySecurityPolicy;
@Mock private AccessibilitySecurityPolicy.AccessibilityUserManager mMockA11yUserManager;
+ @Mock private IBinder mMockHostToken;
+ @Mock private IBinder mMockEmbeddedToken;
+ @Mock private IBinder mMockInvalidToken;
+
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
@@ -140,6 +148,8 @@
// AccessibilityEventSender is invoked during onWindowsForAccessibilityChanged.
// Resets it for mockito verify of further test case.
Mockito.reset(mMockA11yEventSender);
+
+ registerLeashedTokenAndWindowId();
}
@After
@@ -407,6 +417,51 @@
}
@Test
+ public void resolveParentWindowId_windowIsNotEmbedded_shouldReturnGivenId()
+ throws RemoteException {
+ final int windowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, false,
+ Mockito.mock(IBinder.class), USER_SYSTEM_ID);
+ assertEquals(windowId, mA11yWindowManager.resolveParentWindowIdLocked(windowId));
+ }
+
+ @Test
+ public void resolveParentWindowId_windowIsNotRegistered_shouldReturnGivenId() {
+ final int windowId = -1;
+ assertEquals(windowId, mA11yWindowManager.resolveParentWindowIdLocked(windowId));
+ }
+
+ @Test
+ public void resolveParentWindowId_windowIsAssociated_shouldReturnParentWindowId()
+ throws RemoteException {
+ final IBinder mockHostToken = Mockito.mock(IBinder.class);
+ final IBinder mockEmbeddedToken = Mockito.mock(IBinder.class);
+ final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ false, mockHostToken, USER_SYSTEM_ID);
+ final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ false, mockEmbeddedToken, USER_SYSTEM_ID);
+ mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken);
+ final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked(
+ embeddedWindowId);
+ assertEquals(hostWindowId, resolvedWindowId);
+ }
+
+ @Test
+ public void resolveParentWindowId_windowIsDisassociated_shouldReturnGivenId()
+ throws RemoteException {
+ final IBinder mockHostToken = Mockito.mock(IBinder.class);
+ final IBinder mockEmbeddedToken = Mockito.mock(IBinder.class);
+ final int hostWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ false, mockHostToken, USER_SYSTEM_ID);
+ final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ false, mockEmbeddedToken, USER_SYSTEM_ID);
+ mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken);
+ mA11yWindowManager.disassociateEmbeddedHierarchyLocked(mockEmbeddedToken);
+ final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked(
+ embeddedWindowId);
+ assertEquals(embeddedWindowId, resolvedWindowId);
+ }
+
+ @Test
public void computePartialInteractiveRegionForWindow_wholeVisible_returnWholeRegion() {
// Updates top 2 z-order WindowInfo are whole visible.
WindowInfo windowInfo = mWindowInfos.get(Display.DEFAULT_DISPLAY).get(0);
@@ -726,6 +781,64 @@
token.asBinder(), -1);
}
+ @Test
+ public void getHostTokenLocked_hierarchiesAreAssociated_shouldReturnHostToken() {
+ mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken);
+ final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken);
+ assertEquals(hostToken, mMockHostToken);
+ }
+
+ @Test
+ public void getHostTokenLocked_hierarchiesAreNotAssociated_shouldReturnNull() {
+ final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken);
+ assertNull(hostToken);
+ }
+
+ @Test
+ public void getHostTokenLocked_embeddedHierarchiesAreDisassociated_shouldReturnNull() {
+ mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken);
+ mA11yWindowManager.disassociateLocked(mMockEmbeddedToken);
+ final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockEmbeddedToken);
+ assertNull(hostToken);
+ }
+
+ @Test
+ public void getHostTokenLocked_hostHierarchiesAreDisassociated_shouldReturnNull() {
+ mA11yWindowManager.associateLocked(mMockEmbeddedToken, mMockHostToken);
+ mA11yWindowManager.disassociateLocked(mMockHostToken);
+ final IBinder hostToken = mA11yWindowManager.getHostTokenLocked(mMockHostToken);
+ assertNull(hostToken);
+ }
+
+ @Test
+ public void getWindowIdLocked_windowIsRegistered_shouldReturnWindowId() {
+ final int windowId = mA11yWindowManager.getWindowIdLocked(mMockHostToken);
+ assertEquals(windowId, HOST_WINDOW_ID);
+ }
+
+ @Test
+ public void getWindowIdLocked_windowIsNotRegistered_shouldReturnInvalidWindowId() {
+ final int windowId = mA11yWindowManager.getWindowIdLocked(mMockInvalidToken);
+ assertEquals(windowId, INVALID_ID);
+ }
+
+ @Test
+ public void getTokenLocked_windowIsRegistered_shouldReturnToken() {
+ final IBinder token = mA11yWindowManager.getTokenLocked(HOST_WINDOW_ID);
+ assertEquals(token, mMockHostToken);
+ }
+
+ @Test
+ public void getTokenLocked_windowIsNotRegistered_shouldReturnNull() {
+ final IBinder token = mA11yWindowManager.getTokenLocked(OTHER_WINDOW_ID);
+ assertNull(token);
+ }
+
+ private void registerLeashedTokenAndWindowId() {
+ mA11yWindowManager.registerIdLocked(mMockHostToken, HOST_WINDOW_ID);
+ mA11yWindowManager.registerIdLocked(mMockEmbeddedToken, EMBEDDED_WINDOW_ID);
+ }
+
private void startTrackingPerDisplay(int displayId) throws RemoteException {
ArrayList<WindowInfo> windowInfosForDisplay = new ArrayList<>();
// Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy
@@ -784,6 +897,7 @@
IAccessibilityInteractionConnection.class);
final IBinder mockConnectionBinder = Mockito.mock(IBinder.class);
final IBinder mockWindowBinder = Mockito.mock(IBinder.class);
+ final IBinder mockLeashToken = Mockito.mock(IBinder.class);
when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder);
when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
@@ -792,11 +906,31 @@
.thenReturn(displayId);
int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
- mockWindowToken, mockA11yConnection, PACKAGE_NAME, userId);
+ mockWindowToken, mockLeashToken, mockA11yConnection, PACKAGE_NAME, userId);
mA11yWindowTokens.put(windowId, mockWindowToken);
return mockWindowToken;
}
+ private int addAccessibilityInteractionConnection(int displayId, boolean bGlobal,
+ IBinder leashToken, int userId) throws RemoteException {
+ final IWindow mockWindowToken = Mockito.mock(IWindow.class);
+ final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock(
+ IAccessibilityInteractionConnection.class);
+ final IBinder mockConnectionBinder = Mockito.mock(IBinder.class);
+ final IBinder mockWindowBinder = Mockito.mock(IBinder.class);
+ when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder);
+ when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
+ when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
+ .thenReturn(bGlobal);
+ when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowToken.asBinder()))
+ .thenReturn(displayId);
+
+ int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
+ mockWindowToken, leashToken, mockA11yConnection, PACKAGE_NAME, userId);
+ mA11yWindowTokens.put(windowId, mockWindowToken);
+ return windowId;
+ }
+
private void addWindowInfo(ArrayList<WindowInfo> windowInfos, IWindow windowToken, int layer) {
final WindowInfo windowInfo = WindowInfo.obtain();
windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
index 156cd6e..e5adb80 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -283,7 +283,7 @@
null /* authenticators */);
waitForIdle();
verify(mReceiver1).onError(
- eq(BiometricAuthenticator.TYPE_NONE),
+ eq(BiometricAuthenticator.TYPE_FINGERPRINT),
eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE),
eq(0 /* vendorCode */));
}
@@ -1117,14 +1117,14 @@
// STRONG-only auth is not available
int authenticators = Authenticators.BIOMETRIC_STRONG;
- assertEquals(BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE,
+ assertEquals(BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
invokeCanAuthenticate(mBiometricService, authenticators));
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
authenticators);
waitForIdle();
verify(mReceiver1).onError(
- eq(BiometricAuthenticator.TYPE_NONE),
- eq(BiometricPrompt.BIOMETRIC_ERROR_HW_NOT_PRESENT),
+ eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ eq(BiometricPrompt.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED),
eq(0) /* vendorCode */);
// Request for weak auth works
@@ -1154,7 +1154,7 @@
false /* requireConfirmation */,
authenticators);
waitForIdle();
- assertTrue(Utils.isDeviceCredentialAllowed(mBiometricService.mCurrentAuthSession.mBundle));
+ assertTrue(Utils.isCredentialRequested(mBiometricService.mCurrentAuthSession.mBundle));
verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
eq(mBiometricService.mCurrentAuthSession.mBundle),
any(IBiometricServiceReceiverInternal.class),
@@ -1162,6 +1162,28 @@
anyBoolean() /* requireConfirmation */,
anyInt() /* userId */,
eq(TEST_PACKAGE_NAME));
+
+ // Un-downgrading the authenticator allows successful strong auth
+ for (BiometricService.AuthenticatorWrapper wrapper : mBiometricService.mAuthenticators) {
+ if (wrapper.id == testId) {
+ wrapper.updateStrength(Authenticators.BIOMETRIC_STRONG);
+ }
+ }
+
+ resetReceiver();
+ authenticators = Authenticators.BIOMETRIC_STRONG;
+ assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
+ invokeCanAuthenticate(mBiometricService, authenticators));
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */, authenticators);
+ waitForIdle();
+ verify(mBiometricService.mStatusBarService).showAuthenticationDialog(
+ eq(mBiometricService.mCurrentAuthSession.mBundle),
+ any(IBiometricServiceReceiverInternal.class),
+ eq(BiometricAuthenticator.TYPE_FINGERPRINT /* biometricModality */),
+ anyBoolean() /* requireConfirmation */,
+ anyInt() /* userId */,
+ eq(TEST_PACKAGE_NAME));
}
@Test(expected = IllegalStateException.class)
@@ -1245,6 +1267,19 @@
}
@Test
+ public void testAuthentication_normalAppIgnoresDevicePolicy() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG);
+ when(mDevicePolicyManager
+ .getKeyguardDisabledFeatures(any() /* admin */, anyInt() /* userHandle */))
+ .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */, Authenticators.BIOMETRIC_STRONG);
+ waitForIdle();
+ assertEquals(mBiometricService.mCurrentAuthSession.mState,
+ BiometricService.STATE_AUTH_STARTED);
+ }
+
+ @Test
public void testWorkAuthentication_faceWorksIfNotDisabledByDevicePolicyManager()
throws Exception {
setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
index 312ff2c..df242f3 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/UtilsTest.java
@@ -91,31 +91,31 @@
@Test
public void testIsDeviceCredentialAllowed_withIntegerFlags() {
int authenticators = 0;
- assertFalse(Utils.isDeviceCredentialAllowed(authenticators));
+ assertFalse(Utils.isCredentialRequested(authenticators));
authenticators |= Authenticators.DEVICE_CREDENTIAL;
- assertTrue(Utils.isDeviceCredentialAllowed(authenticators));
+ assertTrue(Utils.isCredentialRequested(authenticators));
authenticators |= Authenticators.BIOMETRIC_WEAK;
- assertTrue(Utils.isDeviceCredentialAllowed(authenticators));
+ assertTrue(Utils.isCredentialRequested(authenticators));
}
@Test
public void testIsDeviceCredentialAllowed_withBundle() {
Bundle bundle = new Bundle();
- assertFalse(Utils.isDeviceCredentialAllowed(bundle));
+ assertFalse(Utils.isCredentialRequested(bundle));
int authenticators = 0;
bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
- assertFalse(Utils.isDeviceCredentialAllowed(bundle));
+ assertFalse(Utils.isCredentialRequested(bundle));
authenticators |= Authenticators.DEVICE_CREDENTIAL;
bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
- assertTrue(Utils.isDeviceCredentialAllowed(bundle));
+ assertTrue(Utils.isCredentialRequested(bundle));
authenticators |= Authenticators.BIOMETRIC_WEAK;
bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
- assertTrue(Utils.isDeviceCredentialAllowed(bundle));
+ assertTrue(Utils.isCredentialRequested(bundle));
}
@Test
@@ -140,14 +140,14 @@
for (int i = 0; i <= 7; i++) {
int authenticators = 1 << i;
bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
- assertTrue(Utils.isBiometricAllowed(bundle));
+ assertTrue(Utils.isBiometricRequested(bundle));
}
// The rest of the bits are not allowed to integrate with the public APIs
for (int i = 8; i < 32; i++) {
int authenticators = 1 << i;
bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
- assertFalse(Utils.isBiometricAllowed(bundle));
+ assertFalse(Utils.isBiometricRequested(bundle));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index d40130a..8dae48c 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -211,7 +211,8 @@
IntentSender mockReceiver = mock(IntentSender.class);
List<Rule> rules =
Arrays.asList(
- new Rule(IntegrityFormula.PACKAGE_NAME.equalTo(PACKAGE_NAME), Rule.DENY));
+ new Rule(IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME),
+ Rule.DENY));
mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver);
runJobInHandler();
@@ -230,7 +231,8 @@
IntentSender mockReceiver = mock(IntentSender.class);
List<Rule> rules =
Arrays.asList(
- new Rule(IntegrityFormula.PACKAGE_NAME.equalTo(PACKAGE_NAME), Rule.DENY));
+ new Rule(IntegrityFormula.Application.packageNameEquals(PACKAGE_NAME),
+ Rule.DENY));
mService.updateRuleSet(VERSION, new ParceledListSlice<>(rules), mockReceiver);
runJobInHandler();
@@ -390,7 +392,7 @@
public void getCurrentRules() throws Exception {
whitelistUsAsRuleProvider();
makeUsSystemApp();
- Rule rule = new Rule(IntegrityFormula.PACKAGE_NAME.equalTo("package"), Rule.DENY);
+ Rule rule = new Rule(IntegrityFormula.Application.packageNameEquals("package"), Rule.DENY);
when(mIntegrityFileManager.readRules(any())).thenReturn(Arrays.asList(rule));
assertThat(mService.getCurrentRules().getList()).containsExactly(rule);
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
index 38cf562..3dc26af 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
@@ -58,7 +58,7 @@
getBits(COMPOUND_FORMULA_END, SEPARATOR_BITS);
private static final String ATOMIC_FORMULA_START_BITS =
getBits(ATOMIC_FORMULA_START, SEPARATOR_BITS);
- private static final int INVALID_FORMULA_SEPARATOR_VALUE = 3;
+ private static final int INVALID_FORMULA_SEPARATOR_VALUE = (1 << SEPARATOR_BITS) - 1;
private static final String INVALID_FORMULA_SEPARATOR_BITS =
getBits(INVALID_FORMULA_SEPARATOR_VALUE, SEPARATOR_BITS);
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
index 913aff7..ea9e6ff 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleIndexingDetailsIdentifierTest.java
@@ -112,6 +112,7 @@
ATOMIC_FORMULA_WITH_VERSION_CODE,
ATOMIC_FORMULA_WITH_ISPREINSTALLED)),
Rule.DENY);
+ public static final int INVALID_FORMULA_TAG = -1;
@Test
public void getIndexType_nullRule() {
@@ -290,7 +291,7 @@
return new AtomicFormula(0) {
@Override
public int getTag() {
- return 4;
+ return INVALID_FORMULA_TAG;
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
index 4ae374a..9213e1f 100644
--- a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java
@@ -51,6 +51,7 @@
private static final String APP_PREDICTION_SHARE_UI_SURFACE = "share";
private static final int APP_PREDICTION_TARGET_COUNT = 4;
private static final String TEST_PACKAGE_NAME = "com.example";
+ private static final int USER_ID = 0;
private PeopleServiceInternal mServiceInternal;
private PeopleService.LocalService mLocalService;
@@ -73,7 +74,7 @@
mServiceInternal = LocalServices.getService(PeopleServiceInternal.class);
mLocalService = (PeopleService.LocalService) mServiceInternal;
- mSessionId = new AppPredictionSessionId("abc");
+ mSessionId = new AppPredictionSessionId("abc", USER_ID);
mPredictionContext = new AppPredictionContext.Builder(mContext)
.setUiSurface(APP_PREDICTION_SHARE_UI_SURFACE)
.setPredictedTargetCount(APP_PREDICTION_TARGET_COUNT)
diff --git a/services/tests/servicestests/src/com/android/server/power/PreRebootLoggerTest.java b/services/tests/servicestests/src/com/android/server/power/PreRebootLoggerTest.java
new file mode 100644
index 0000000..a138234
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/power/PreRebootLoggerTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.power;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+
+import com.google.common.io.Files;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+
+/**
+ * Tests for {@link PreRebootLogger}
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PreRebootLoggerTest {
+ @Mock Context mContext;
+ private MockContentResolver mContentResolver;
+ private File mDumpDir;
+
+ @BeforeClass
+ public static void setupOnce() {
+ FakeSettingsProvider.clearSettingsProvider();
+ }
+
+ @AfterClass
+ public static void tearDownOnce() {
+ FakeSettingsProvider.clearSettingsProvider();
+ }
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mContentResolver = new MockContentResolver(getInstrumentation().getTargetContext());
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+
+ mDumpDir = Files.createTempDir();
+ mDumpDir.mkdir();
+ mDumpDir.deleteOnExit();
+ }
+
+ @Test
+ public void log_adbEnabled_dumpsInformationProperly() {
+ Settings.Global.putInt(mContentResolver, Settings.Global.ADB_ENABLED, 1);
+
+ PreRebootLogger.log(mContext, mDumpDir);
+
+ assertThat(mDumpDir.list()).asList().containsExactly("system", "package", "rollback");
+ }
+
+ @Test
+ public void log_adbDisabled_wipesDumpedInformation() {
+ Settings.Global.putInt(mContentResolver, Settings.Global.ADB_ENABLED, 1);
+ PreRebootLogger.log(mContext, mDumpDir);
+ Settings.Global.putInt(mContentResolver, Settings.Global.ADB_ENABLED, 0);
+
+ PreRebootLogger.log(mContext, mDumpDir);
+
+ assertThat(mDumpDir.listFiles()).isEmpty();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 03dc213..290683d 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -115,6 +115,7 @@
private static final long WORKING_SET_THRESHOLD = 12 * HOUR_MS;
private static final long FREQUENT_THRESHOLD = 24 * HOUR_MS;
private static final long RARE_THRESHOLD = 48 * HOUR_MS;
+ private static final long RESTRICTED_THRESHOLD = 96 * HOUR_MS;
/** Mock variable used in {@link MyInjector#isPackageInstalled(String, int, int)} */
private static boolean isPackageInstalled = true;
@@ -232,9 +233,8 @@
@Override
String getAppIdleSettings() {
return "screen_thresholds=0/0/0/" + HOUR_MS + ",elapsed_thresholds=0/"
- + WORKING_SET_THRESHOLD + "/"
- + FREQUENT_THRESHOLD + "/"
- + RARE_THRESHOLD;
+ + WORKING_SET_THRESHOLD + "/" + FREQUENT_THRESHOLD + "/" + RARE_THRESHOLD
+ + "/" + RESTRICTED_THRESHOLD;
}
@Override
@@ -372,12 +372,15 @@
// RARE bucket
assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE);
+ // RESTRICTED bucket
+ assertTimeout(mController, RESTRICTED_THRESHOLD + 1, STANDBY_BUCKET_RESTRICTED);
+
reportEvent(mController, USER_INTERACTION, RARE_THRESHOLD + 1, PACKAGE_1);
assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE);
- // RARE bucket
- assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
+ // RESTRICTED bucket
+ assertTimeout(mController, RESTRICTED_THRESHOLD * 2 + 2, STANDBY_BUCKET_RESTRICTED);
}
@Test
@@ -437,7 +440,7 @@
assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController, PACKAGE_1));
mInjector.setDisplayOn(true);
- assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
+ assertTimeout(mController, RARE_THRESHOLD + 2 * HOUR_MS + 1, STANDBY_BUCKET_RARE);
}
@Test
@@ -642,7 +645,7 @@
assertBucket(STANDBY_BUCKET_FREQUENT);
// Way past prediction timeout, use system thresholds
- mInjector.mElapsedRealtime = RARE_THRESHOLD * 4;
+ mInjector.mElapsedRealtime = RARE_THRESHOLD;
mController.checkIdleStates(USER_ID);
assertBucket(STANDBY_BUCKET_RARE);
}
@@ -669,7 +672,7 @@
assertBucket(STANDBY_BUCKET_RESTRICTED);
// Way past all timeouts. Make sure timeout processing doesn't raise bucket.
- mInjector.mElapsedRealtime += RARE_THRESHOLD * 4;
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
mController.checkIdleStates(USER_ID);
assertBucket(STANDBY_BUCKET_RESTRICTED);
}
@@ -697,6 +700,22 @@
}
@Test
+ public void testPredictionRaiseFromRestrictedTimeout() {
+ reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
+
+ // Way past all timeouts. App times out into RESTRICTED bucket.
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
+ mController.checkIdleStates(USER_ID);
+ assertBucket(STANDBY_BUCKET_RESTRICTED);
+
+ // Since the app timed out into RESTRICTED, prediction should be able to remove from the
+ // bucket.
+ mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD;
+ mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+ REASON_MAIN_PREDICTED);
+ }
+
+ @Test
public void testCascadingTimeouts() throws Exception {
reportEvent(mController, USER_INTERACTION, 0, PACKAGE_1);
assertBucket(STANDBY_BUCKET_ACTIVE);
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index f54f8d1..ec99f36 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -573,6 +573,7 @@
/**
* Indicates that the call is an adhoc conference call. This property can be set for both
* incoming and outgoing calls.
+ * @hide
*/
public static final int PROPERTY_IS_ADHOC_CONFERENCE = 0x00002000;
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 6b0845f..56acdff 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -174,6 +174,7 @@
/**
* Returns whether this conference is requesting that the system play a ringback tone
* on its behalf.
+ * @hide
*/
public final boolean isRingbackRequested() {
return mRingbackRequested;
@@ -324,6 +325,7 @@
* the default dialer's {@link InCallService}.
*
* @param videoState The video state in which to answer the connection.
+ * @hide
*/
public void onAnswer(int videoState) {}
@@ -343,6 +345,7 @@
* a request to reject.
* For managed {@link ConnectionService}s, this will be called when the user rejects a call via
* the default dialer's {@link InCallService}.
+ * @hide
*/
public void onReject() {}
@@ -362,6 +365,7 @@
/**
* Sets state to be ringing.
+ * @hide
*/
public final void setRinging() {
setState(Connection.STATE_RINGING);
@@ -487,6 +491,7 @@
* that do not play a ringback tone themselves in the conference's audio stream.
*
* @param ringback Whether the ringback tone is to be played.
+ * @hide
*/
public final void setRingbackRequested(boolean ringback) {
if (mRingbackRequested != ringback) {
@@ -736,6 +741,7 @@
*
* @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
* @return A {@code Conference} which indicates failure.
+ * @hide
*/
public @NonNull static Conference createFailedConference(
@NonNull DisconnectCause disconnectCause, @NonNull PhoneAccountHandle phoneAccount) {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 72c66d2..8049459 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -501,7 +501,7 @@
* Set by the framework to indicate that it is an adhoc conference call.
* <p>
* This is used for Outgoing and incoming conference calls.
- *
+ * @hide
*/
public static final int PROPERTY_IS_ADHOC_CONFERENCE = 1 << 12;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index bf4dee2..a28cc4f 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1875,8 +1875,8 @@
* {@link #registerPhoneAccount}.
* @param extras A bundle that will be passed through to
* {@link ConnectionService#onCreateIncomingConference}.
+ * @hide
*/
-
public void addNewIncomingConference(@NonNull PhoneAccountHandle phoneAccount,
@NonNull Bundle extras) {
try {
@@ -2115,6 +2115,7 @@
*
* @param participants List of participants to start conference with
* @param extras Bundle of extras to use with the call
+ * @hide
*/
@RequiresPermission(android.Manifest.permission.CALL_PHONE)
public void startConference(@NonNull List<Uri> participants,
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 945c888..ebb53c5 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1084,6 +1084,7 @@
/**
* Determines whether adhoc conference calls are supported by a carrier. When {@code true},
* adhoc conference calling is supported, {@code false otherwise}.
+ * @hide
*/
public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL =
"support_adhoc_conference_calls_bool";
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 8e83c4c..6c920f1 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -801,11 +801,7 @@
"Invalid pdu format. format must be either 3gpp or 3gpp2");
}
try {
- ISms iSms = ISms.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSmsServiceRegisterer()
- .get());
+ ISms iSms = TelephonyManager.getSmsService();
if (iSms != null) {
iSms.injectSmsPduForSubscriber(
getSubscriptionId(), pdu, format, receivedIntent);
@@ -1642,7 +1638,7 @@
* the service does not exist.
*/
private static ISms getISmsServiceOrThrow() {
- ISms iSms = getISmsService();
+ ISms iSms = TelephonyManager.getSmsService();
if (iSms == null) {
throw new UnsupportedOperationException("Sms is not supported");
}
@@ -1650,11 +1646,7 @@
}
private static ISms getISmsService() {
- return ISms.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSmsServiceRegisterer()
- .get());
+ return TelephonyManager.getSmsService();
}
/**
@@ -2091,11 +2083,7 @@
public boolean isSMSPromptEnabled() {
ISms iSms = null;
try {
- iSms = ISms.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSmsServiceRegisterer()
- .get());
+ iSms = TelephonyManager.getSmsService();
return iSms.isSMSPromptEnabled();
} catch (RemoteException ex) {
return false;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 97f50fd..b32e9d7 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1141,11 +1141,7 @@
SubscriptionInfo subInfo = null;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
subInfo = iSub.getActiveSubscriptionInfo(subId, mContext.getOpPackageName(),
mContext.getFeatureId());
@@ -1179,11 +1175,7 @@
SubscriptionInfo result = null;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = iSub.getActiveSubscriptionInfoForIccId(iccId, mContext.getOpPackageName(),
mContext.getFeatureId());
@@ -1217,11 +1209,7 @@
SubscriptionInfo result = null;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = iSub.getActiveSubscriptionInfoForSimSlotIndex(slotIndex,
mContext.getOpPackageName(), mContext.getFeatureId());
@@ -1245,11 +1233,7 @@
List<SubscriptionInfo> result = null;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = iSub.getAllSubInfoList(mContext.getOpPackageName(),
mContext.getFeatureId());
@@ -1330,11 +1314,7 @@
List<SubscriptionInfo> activeList = null;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName(),
mContext.getFeatureId());
@@ -1385,11 +1365,7 @@
List<SubscriptionInfo> result = null;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = iSub.getAvailableSubscriptionInfoList(mContext.getOpPackageName(),
mContext.getFeatureId());
@@ -1428,11 +1404,7 @@
List<SubscriptionInfo> result = null;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = iSub.getAccessibleSubscriptionInfoList(mContext.getOpPackageName());
}
@@ -1461,11 +1433,7 @@
public void requestEmbeddedSubscriptionInfoListRefresh() {
int cardId = TelephonyManager.from(mContext).getCardIdForDefaultEuicc();
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
}
@@ -1494,11 +1462,7 @@
@SystemApi
public void requestEmbeddedSubscriptionInfoListRefresh(int cardId) {
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
iSub.requestEmbeddedSubscriptionInfoListRefresh(cardId);
}
@@ -1519,11 +1483,7 @@
int result = 0;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = iSub.getAllSubInfoCount(mContext.getOpPackageName(),
mContext.getFeatureId());
@@ -1552,11 +1512,7 @@
int result = 0;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = iSub.getActiveSubInfoCount(mContext.getOpPackageName(),
mContext.getFeatureId());
@@ -1577,11 +1533,7 @@
int result = 0;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = iSub.getActiveSubInfoCountMax();
}
@@ -1638,11 +1590,7 @@
}
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub == null) {
Log.e(LOG_TAG, "[addSubscriptionInfoRecord]- ISub service is null");
return;
@@ -1676,11 +1624,7 @@
}
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub == null) {
Log.e(LOG_TAG, "[removeSubscriptionInfoRecord]- ISub service is null");
return;
@@ -1783,11 +1727,7 @@
int result = INVALID_SIM_SLOT_INDEX;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = iSub.getSlotIndex(subscriptionId);
}
@@ -1821,11 +1761,7 @@
int[] subId = null;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
subId = iSub.getSubId(slotIndex);
}
@@ -1849,11 +1785,7 @@
int result = INVALID_PHONE_INDEX;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = iSub.getPhoneId(subId);
}
@@ -1887,11 +1819,7 @@
int subId = INVALID_SUBSCRIPTION_ID;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
subId = iSub.getDefaultSubId();
}
@@ -1914,11 +1842,7 @@
int subId = INVALID_SUBSCRIPTION_ID;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
subId = iSub.getDefaultVoiceSubId();
}
@@ -1948,11 +1872,7 @@
public void setDefaultVoiceSubscriptionId(int subscriptionId) {
if (VDBG) logd("setDefaultVoiceSubId sub id = " + subscriptionId);
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
iSub.setDefaultVoiceSubId(subscriptionId);
}
@@ -2000,11 +1920,7 @@
int subId = INVALID_SUBSCRIPTION_ID;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
subId = iSub.getDefaultSmsSubId();
}
@@ -2030,11 +1946,7 @@
public void setDefaultSmsSubId(int subscriptionId) {
if (VDBG) logd("setDefaultSmsSubId sub id = " + subscriptionId);
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
iSub.setDefaultSmsSubId(subscriptionId);
}
@@ -2072,11 +1984,7 @@
int subId = INVALID_SUBSCRIPTION_ID;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
subId = iSub.getDefaultDataSubId();
}
@@ -2102,11 +2010,7 @@
public void setDefaultDataSubId(int subscriptionId) {
if (VDBG) logd("setDataSubscription sub id = " + subscriptionId);
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
iSub.setDefaultDataSubId(subscriptionId);
}
@@ -2137,11 +2041,7 @@
/** @hide */
public void clearSubscriptionInfo() {
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
iSub.clearSubInfo();
}
@@ -2277,11 +2177,7 @@
*/
public @NonNull int[] getActiveSubscriptionIdList(boolean visibleOnly) {
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
int[] subId = iSub.getActiveSubIdList(visibleOnly);
if (subId != null) return subId;
@@ -2332,11 +2228,7 @@
int simState = TelephonyManager.SIM_STATE_UNKNOWN;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
simState = iSub.getSimStateForSlotIndex(slotIndex);
}
@@ -2355,11 +2247,7 @@
*/
public static void setSubscriptionProperty(int subId, String propKey, String propValue) {
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
iSub.setSubscriptionProperty(subId, propKey, propValue);
}
@@ -2379,11 +2267,7 @@
Context context) {
String resultValue = null;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
resultValue = iSub.getSubscriptionProperty(subId, propKey,
context.getOpPackageName(), context.getFeatureId());
@@ -2547,11 +2431,7 @@
@UnsupportedAppUsage
public boolean isActiveSubId(int subId) {
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
return iSub.isActiveSubId(subId, mContext.getOpPackageName(),
mContext.getFeatureId());
@@ -2747,11 +2627,7 @@
@TelephonyManager.SetOpportunisticSubscriptionResult Consumer<Integer> callback) {
if (VDBG) logd("[setPreferredDataSubscriptionId]+ subId:" + subId);
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub == null) return;
ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() {
@@ -2794,11 +2670,7 @@
public int getPreferredDataSubscriptionId() {
int preferredSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
preferredSubId = iSub.getPreferredDataSubscriptionId();
}
@@ -2829,11 +2701,7 @@
List<SubscriptionInfo> subInfoList = null;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
subInfoList = iSub.getOpportunisticSubscriptions(contextPkg, contextFeature);
}
@@ -2934,11 +2802,7 @@
ParcelUuid groupUuid = null;
int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
groupUuid = iSub.createSubscriptionGroup(subIdArray, pkgForDebug);
} else {
@@ -2988,11 +2852,7 @@
int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
iSub.addSubscriptionsIntoGroup(subIdArray, groupUuid, pkgForDebug);
} else {
@@ -3044,11 +2904,7 @@
int[] subIdArray = subIdList.stream().mapToInt(i->i).toArray();
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
iSub.removeSubscriptionsFromGroup(subIdArray, groupUuid, pkgForDebug);
} else {
@@ -3093,11 +2949,7 @@
List<SubscriptionInfo> result = null;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = iSub.getSubscriptionsInGroup(groupUuid, contextPkg, contextFeature);
} else {
@@ -3210,11 +3062,7 @@
logd("setSubscriptionActivated subId= " + subscriptionId + " enable " + enable);
}
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
return iSub.setSubscriptionEnabled(enable, subscriptionId);
}
@@ -3303,11 +3151,7 @@
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isSubscriptionEnabled(int subscriptionId) {
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
return iSub.isSubscriptionEnabled(subscriptionId);
}
@@ -3330,11 +3174,7 @@
int subId = INVALID_SUBSCRIPTION_ID;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
subId = iSub.getEnabledSubscriptionId(slotIndex);
}
@@ -3360,11 +3200,7 @@
int result = 0;
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
result = helper.callMethod(iSub);
}
@@ -3387,11 +3223,7 @@
*/
public static int getActiveDataSubscriptionId() {
try {
- ISub iSub = ISub.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getSubscriptionServiceRegisterer()
- .get());
+ ISub iSub = TelephonyManager.getSubscriptionService();
if (iSub != null) {
return iSub.getActiveDataSubscriptionId();
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e520a02..15103bf 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -54,6 +54,7 @@
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
@@ -93,6 +94,7 @@
import android.util.Pair;
import com.android.ims.internal.IImsServiceFeatureCallback;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.IBooleanConsumer;
@@ -100,6 +102,8 @@
import com.android.internal.telephony.IOns;
import com.android.internal.telephony.IPhoneSubInfo;
import com.android.internal.telephony.ISetOpportunisticDataCallback;
+import com.android.internal.telephony.ISms;
+import com.android.internal.telephony.ISub;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.IUpdateAvailableNetworksCallback;
import com.android.internal.telephony.OperatorInfo;
@@ -295,6 +299,21 @@
private SubscriptionManager mSubscriptionManager;
private TelephonyScanManager mTelephonyScanManager;
+ /** Cached service handles, cleared by resetServiceHandles() at death */
+ private static final Object sCacheLock = new Object();
+
+ /** @hide */
+ private static boolean sServiceHandleCacheEnabled = true;
+
+ @GuardedBy("sCacheLock")
+ private static IPhoneSubInfo sIPhoneSubInfo;
+ @GuardedBy("sCacheLock")
+ private static ISub sISub;
+ @GuardedBy("sCacheLock")
+ private static ISms sISms;
+ @GuardedBy("sCacheLock")
+ private static final DeathRecipient sServiceDeath = new DeathRecipient();
+
/** Enum indicating multisim variants
* DSDS - Dual SIM Dual Standby
* DSDA - Dual SIM Dual Active
@@ -1991,7 +2010,7 @@
public String getDeviceId(int slotIndex) {
// FIXME this assumes phoneId == slotIndex
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
return info.getDeviceIdForPhone(slotIndex, mContext.getOpPackageName(),
@@ -2245,7 +2264,7 @@
private String getNaiBySubscriberId(int subId) {
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
String nai = info.getNaiForSubscriber(subId, mContext.getOpPackageName(),
@@ -3944,7 +3963,7 @@
@UnsupportedAppUsage
public String getSimSerialNumber(int subId) {
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
return info.getIccSerialNumberForSubscriber(subId, mContext.getOpPackageName(),
@@ -4217,7 +4236,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public String getSubscriberId(int subId) {
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
return info.getSubscriberIdForSubscriber(subId, mContext.getOpPackageName(),
@@ -4253,7 +4272,7 @@
@Nullable
public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(@KeyType int keyType) {
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null) {
Rlog.e(TAG,"IMSI error: Subscriber Info is null");
return null;
@@ -4296,7 +4315,7 @@
@SystemApi
public void resetCarrierKeysForImsiEncryption() {
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null) {
Rlog.e(TAG, "IMSI error: Subscriber Info is null");
if (!isSystemProcess()) {
@@ -4361,7 +4380,7 @@
*/
public void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo) {
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null) return;
info.setCarrierInfoForImsiEncryption(mSubId, mContext.getOpPackageName(),
imsiEncryptionInfo);
@@ -4385,7 +4404,7 @@
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public String getGroupIdLevel1() {
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
return info.getGroupIdLevel1ForSubscriber(getSubId(), mContext.getOpPackageName(),
@@ -4409,7 +4428,7 @@
@UnsupportedAppUsage
public String getGroupIdLevel1(int subId) {
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
return info.getGroupIdLevel1ForSubscriber(subId, mContext.getOpPackageName(),
@@ -4472,7 +4491,7 @@
return number;
}
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
return info.getLine1NumberForSubscriber(subId, mContext.getOpPackageName(),
@@ -4563,7 +4582,7 @@
return alphaTag;
}
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
return info.getLine1AlphaTagForSubscriber(subId, getOpPackageName(),
@@ -4651,7 +4670,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public String getMsisdn(int subId) {
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
return info.getMsisdnForSubscriber(subId, getOpPackageName(), getFeatureId());
@@ -4685,7 +4704,7 @@
@UnsupportedAppUsage
public String getVoiceMailNumber(int subId) {
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
return info.getVoiceMailNumberForSubscriber(subId, getOpPackageName(),
@@ -5284,7 +5303,7 @@
@UnsupportedAppUsage
public String getVoiceMailAlphaTag(int subId) {
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
return info.getVoiceMailAlphaTagForSubscriber(subId, getOpPackageName(),
@@ -5332,7 +5351,7 @@
@UnsupportedAppUsage
public String getIsimImpi() {
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
//get the Isim Impi based on subId
@@ -5359,7 +5378,7 @@
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public String getIsimDomain() {
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
//get the Isim Domain based on subId
@@ -5383,7 +5402,7 @@
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public String[] getIsimImpu() {
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
//get the Isim Impu based on subId
@@ -5396,19 +5415,6 @@
}
}
- /**
- * @hide
- */
- @UnsupportedAppUsage
- private IPhoneSubInfo getSubscriberInfo() {
- // get it each time because that process crashes a lot
- return IPhoneSubInfo.Stub.asInterface(
- TelephonyFrameworkInitializer
- .getTelephonyServiceManager()
- .getPhoneSubServiceRegisterer()
- .get());
- }
-
/**
* Device call state: No activity.
*/
@@ -5465,6 +5471,14 @@
}
/**
+ * @hide
+ */
+ @UnsupportedAppUsage
+ private IPhoneSubInfo getSubscriberInfo() {
+ return getSubscriberInfoService();
+ }
+
+ /**
* Returns the Telephony call state for calls on a specific SIM slot.
* <p>
* Note: This method considers ONLY telephony/mobile calls, where {@link #getCallState()}
@@ -7124,7 +7138,7 @@
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public String getIsimIst() {
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
//get the Isim Ist based on subId
@@ -7146,7 +7160,7 @@
@UnsupportedAppUsage
public String[] getIsimPcscf() {
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
//get the Isim Pcscf based on subId
@@ -7227,7 +7241,7 @@
@UnsupportedAppUsage
public String getIccAuthentication(int subId, int appType, int authType, String data) {
try {
- IPhoneSubInfo info = getSubscriberInfo();
+ IPhoneSubInfo info = getSubscriberInfoService();
if (info == null)
return null;
return info.getIccSimChallengeResponse(subId, appType, authType, data);
@@ -13086,4 +13100,153 @@
throw e.rethrowFromSystemServer();
}
}
+
+ private static class DeathRecipient implements IBinder.DeathRecipient {
+ @Override
+ public void binderDied() {
+ resetServiceCache();
+ }
+ }
+
+ /**
+ * Reset everything in the service cache; if one handle died then they are
+ * all probably broken.
+ * @hide
+ */
+ private static void resetServiceCache() {
+ synchronized (sCacheLock) {
+ if (sISub != null) {
+ sISub.asBinder().unlinkToDeath(sServiceDeath, 0);
+ sISub = null;
+ }
+ if (sISms != null) {
+ sISms.asBinder().unlinkToDeath(sServiceDeath, 0);
+ sISms = null;
+ }
+ if (sIPhoneSubInfo != null) {
+ sIPhoneSubInfo.asBinder().unlinkToDeath(sServiceDeath, 0);
+ sIPhoneSubInfo = null;
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ static IPhoneSubInfo getSubscriberInfoService() {
+ // Keeps cache disabled until test fixes are checked into AOSP.
+ if (true) {
+ return IPhoneSubInfo.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getPhoneSubServiceRegisterer()
+ .get());
+ }
+
+ if (sIPhoneSubInfo == null) {
+ IPhoneSubInfo temp = IPhoneSubInfo.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getPhoneSubServiceRegisterer()
+ .get());
+ synchronized (sCacheLock) {
+ if (sIPhoneSubInfo == null && temp != null) {
+ try {
+ sIPhoneSubInfo = temp;
+ sIPhoneSubInfo.asBinder().linkToDeath(sServiceDeath, 0);
+ } catch (Exception e) {
+ // something has gone horribly wrong
+ sIPhoneSubInfo = null;
+ }
+ }
+ }
+ }
+ return sIPhoneSubInfo;
+ }
+
+ /**
+ * @hide
+ */
+ static ISub getSubscriptionService() {
+ // Keeps cache disabled until test fixes are checked into AOSP.
+ if (true) {
+ return ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
+ }
+
+ if (sISub == null) {
+ ISub temp = ISub.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSubscriptionServiceRegisterer()
+ .get());
+ synchronized (sCacheLock) {
+ if (sISub == null && temp != null) {
+ try {
+ sISub = temp;
+ sISub.asBinder().linkToDeath(sServiceDeath, 0);
+ } catch (Exception e) {
+ // something has gone horribly wrong
+ sISub = null;
+ }
+ }
+ }
+ }
+ return sISub;
+ }
+
+ /**
+ * @hide
+ */
+ static ISms getSmsService() {
+ // Keeps cache disabled until test fixes are checked into AOSP.
+ if (true) {
+ return ISms.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSmsServiceRegisterer()
+ .get());
+ }
+
+ if (sISms == null) {
+ ISms temp = ISms.Stub.asInterface(
+ TelephonyFrameworkInitializer
+ .getTelephonyServiceManager()
+ .getSmsServiceRegisterer()
+ .get());
+ synchronized (sCacheLock) {
+ if (sISms == null && temp != null) {
+ try {
+ sISms = temp;
+ sISms.asBinder().linkToDeath(sServiceDeath, 0);
+ } catch (Exception e) {
+ // something has gone horribly wrong
+ sISms = null;
+ }
+ }
+ }
+ }
+ return sISms;
+ }
+
+ /**
+ * Disables service handle caching for tests that utilize mock services.
+ * @hide
+ */
+ @VisibleForTesting
+ public static void disableServiceHandleCaching() {
+ sServiceHandleCacheEnabled = false;
+ }
+
+ /**
+ * Reenables service handle caching.
+ * @hide
+ */
+ @VisibleForTesting
+ public static void enableServiceHandleCaching() {
+ sServiceHandleCacheEnabled = true;
+ }
}
diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java
index d483291..c506cd5 100644
--- a/telephony/java/android/telephony/ims/ImsRcsManager.java
+++ b/telephony/java/android/telephony/ims/ImsRcsManager.java
@@ -59,6 +59,7 @@
* @see #isAvailable(int)
* @see #registerRcsAvailabilityCallback(Executor, AvailabilityCallback)
* @see #unregisterRcsAvailabilityCallback(AvailabilityCallback)
+ * @hide
*/
public static class AvailabilityCallback {
@@ -141,14 +142,16 @@
/**
* @return A {@link RcsUceAdapter} used for User Capability Exchange (UCE) operations for
* this subscription.
- * @hide
*/
@NonNull
public RcsUceAdapter getUceAdapter() {
return new RcsUceAdapter(mSubId);
}
- /**{@inheritDoc}*/
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
@Override
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void registerImsRegistrationCallback(
@@ -177,7 +180,10 @@
}
}
- /**{@inheritDoc}*/
+ /**
+ * {@inheritDoc
+ * @hide
+ */
@Override
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void unregisterImsRegistrationCallback(
@@ -199,7 +205,10 @@
}
}
- /**{@inheritDoc}*/
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
@Override
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void getRegistrationState(@NonNull @CallbackExecutor Executor executor,
@@ -229,7 +238,10 @@
}
}
- /**{@inheritDoc}*/
+ /**
+ * {@inheritDoc}
+ * @hide
+ */
@Override
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor,
@@ -280,6 +292,7 @@
* {@link ImsRcsManager} is valid, but the ImsService associated with the subscription is not
* available. This can happen if the ImsService has crashed, for example, or if the subscription
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+ * @hide
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void registerRcsAvailabilityCallback(@NonNull @CallbackExecutor Executor executor,
@@ -319,6 +332,7 @@
* @throws ImsException if the IMS service is not available when calling this method
* {@link ImsRcsController#unregisterRcsAvailabilityCallback()}.
* See {@link ImsException#getCode()} for more information on the error codes.
+ * @hide
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void unregisterRcsAvailabilityCallback(@NonNull AvailabilityCallback c)
@@ -361,6 +375,7 @@
* @throws ImsException if the IMS service is not available when calling this method
* {@link ImsRcsController#isCapable(int, int)}.
* See {@link ImsException#getCode()} for more information on the error codes.
+ * @hide
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isCapable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability,
@@ -395,6 +410,7 @@
* @throws ImsException if the IMS service is not available when calling this method
* {@link ImsRcsController#isAvailable(int, int)}.
* See {@link ImsException#getCode()} for more information on the error codes.
+ * @hide
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isAvailable(@RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability)
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 72a00ce..fc7c1ee 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -51,55 +51,67 @@
/**
* An unknown error has caused the request to fail.
+ * @hide
*/
public static final int ERROR_GENERIC_FAILURE = 1;
/**
* The carrier network does not have UCE support enabled for this subscriber.
+ * @hide
*/
public static final int ERROR_NOT_ENABLED = 2;
/**
* The data network that the device is connected to does not support UCE currently (e.g. it is
* 1x only currently).
+ * @hide
*/
public static final int ERROR_NOT_AVAILABLE = 3;
/**
* The network has responded with SIP 403 error and a reason "User not registered."
+ * @hide
*/
public static final int ERROR_NOT_REGISTERED = 4;
/**
* The network has responded to this request with a SIP 403 error and reason "not authorized for
* presence" for this subscriber.
+ * @hide
*/
public static final int ERROR_NOT_AUTHORIZED = 5;
/**
* The network has responded to this request with a SIP 403 error and no reason.
+ * @hide
*/
public static final int ERROR_FORBIDDEN = 6;
/**
* The contact URI requested is not provisioned for VoLTE or it is not known as an IMS
* subscriber to the carrier network.
+ * @hide
*/
public static final int ERROR_NOT_FOUND = 7;
/**
* The capabilities request contained too many URIs for the carrier network to handle. Retry
* with a lower number of contact numbers. The number varies per carrier.
+ * @hide
*/
// TODO: Try to integrate this into the API so that the service will split based on carrier.
public static final int ERROR_REQUEST_TOO_LARGE = 8;
/**
* The network did not respond to the capabilities request before the request timed out.
+ * @hide
*/
public static final int ERROR_REQUEST_TIMEOUT = 10;
/**
* The request failed due to the service having insufficient memory.
+ * @hide
*/
public static final int ERROR_INSUFFICIENT_MEMORY = 11;
/**
* The network was lost while trying to complete the request.
+ * @hide
*/
public static final int ERROR_LOST_NETWORK = 12;
/**
* The request has failed because the same request has already been added to the queue.
+ * @hide
*/
public static final int ERROR_ALREADY_IN_QUEUE = 13;
@@ -124,28 +136,33 @@
/**
* The last publish has resulted in a "200 OK" response or the device is using SIP OPTIONS for
* UCE.
+ * @hide
*/
public static final int PUBLISH_STATE_200_OK = 1;
/**
* The hasn't published its capabilities since boot or hasn't gotten any publish response yet.
+ * @hide
*/
public static final int PUBLISH_STATE_NOT_PUBLISHED = 2;
/**
* The device has tried to publish its capabilities, which has resulted in an error. This error
* is related to the fact that the device is not VoLTE provisioned.
+ * @hide
*/
public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3;
/**
* The device has tried to publish its capabilities, which has resulted in an error. This error
* is related to the fact that the device is not RCS or UCE provisioned.
+ * @hide
*/
public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4;
/**
* The last publish resulted in a "408 Request Timeout" response.
+ * @hide
*/
public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5;
@@ -154,6 +171,7 @@
* or SIP 423 - "Interval too short".
* <p>
* Device shall retry with exponential back-off.
+ * @hide
*/
public static final int PUBLISH_STATE_OTHER_ERROR = 6;
@@ -174,6 +192,7 @@
* Provides a one-time callback for the response to a UCE request. After this callback is called
* by the framework, the reference to this callback will be discarded on the service side.
* @see #requestCapabilities(Executor, List, CapabilitiesCallback)
+ * @hide
*/
public static class CapabilitiesCallback {
@@ -225,6 +244,7 @@
* {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
* available. This can happen if the ImsService has crashed, for example, or if the subscription
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+ * @hide
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void requestCapabilities(@NonNull @CallbackExecutor Executor executor,
@@ -287,6 +307,7 @@
* {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
* available. This can happen if the ImsService has crashed, for example, or if the subscription
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
+ * @hide
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public @PublishState int getUcePublishState() throws ImsException {
@@ -307,14 +328,13 @@
}
/**
- * The user’s setting for whether or not Presence and User Capability Exchange (UCE) is enabled
- * for the associated subscription.
+ * The user’s setting for whether or not User Capability Exchange (UCE) is enabled for the
+ * associated subscription.
+ * <p>
+ * Note: This setting does not affect whether or not the device publishes its service
+ * capabilities if the subscription supports presence publication.
*
- * @return true if the user’s setting for UCE is enabled, false otherwise. If false,
- * {@link ImsRcsManager#isCapable(int, int)} will return false for
- * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and
- * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE}
- * @see #setUceSettingEnabled(boolean)
+ * @return true if the user’s setting for UCE is enabled, false otherwise.
* @throws ImsException if the subscription associated with this instance of
* {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
* available. This can happen if the ImsService has crashed, for example, or if the subscription
@@ -340,12 +360,12 @@
/**
* Change the user’s setting for whether or not UCE is enabled for the associated subscription.
- * @param isEnabled the user's setting for whether or not they wish for Presence and User
- * Capability Exchange to be enabled. If false,
- * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and
- * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} capability will be
- * disabled, depending on which type of UCE the carrier supports.
- * @see #isUceSettingEnabled()
+ * <p>
+ * Note: This setting does not affect whether or not the device publishes its service
+ * capabilities if the subscription supports presence publication.
+ *
+ * @param isEnabled the user's setting for whether or not they wish for User
+ * Capability Exchange to be enabled.
* @throws ImsException if the subscription associated with this instance of
* {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not
* available. This can happen if the ImsService has crashed, for example, or if the subscription
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 8e67621..98b0bcf 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -193,6 +193,7 @@
* of the capability and notify the capability status as true using
* {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)}. This will signal to the
* framework that the capability is available for usage.
+ * @hide
*/
public static class RcsImsCapabilities extends Capabilities {
/** @hide*/
@@ -286,6 +287,7 @@
* set, the {@link RcsFeature} has brought up the capability and is ready for framework
* requests. To change the status of the capabilities
* {@link #notifyCapabilitiesStatusChanged(RcsImsCapabilities)} should be called.
+ * @hide
*/
@Override
public @NonNull final RcsImsCapabilities queryCapabilityStatus() {
@@ -296,6 +298,7 @@
* Notify the framework that the capabilities status has changed. If a capability is enabled,
* this signals to the framework that the capability has been initialized and is ready.
* Call {@link #queryCapabilityStatus()} to return the current capability status.
+ * @hide
*/
public final void notifyCapabilitiesStatusChanged(@NonNull RcsImsCapabilities c) {
if (c == null) {
@@ -310,6 +313,7 @@
* {@link #changeEnabledCapabilities(CapabilityChangeRequest, CapabilityCallbackProxy)} to
* enable or disable capability A, this method should return the correct configuration for
* capability A afterwards (until it has changed).
+ * @hide
*/
public boolean queryCapabilityConfiguration(
@RcsImsCapabilities.RcsImsCapabilityFlag int capability,
@@ -331,6 +335,7 @@
* If for some reason one or more of these capabilities can not be enabled/disabled,
* {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError(int, int, int)} should
* be called for each capability change that resulted in an error.
+ * @hide
*/
@Override
public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request,
@@ -349,6 +354,7 @@
*
* @return An instance of {@link RcsSipOptionsImplBase} that implements SIP options exchange if
* it is supported by the device.
+ * @hide
*/
public @NonNull RcsSipOptionsImplBase getOptionsExchangeImpl() {
// Base Implementation, override to implement functionality
@@ -364,6 +370,7 @@
*
* @return An instance of {@link RcsPresenceExchangeImplBase} that implements presence
* exchange if it is supported by the device.
+ * @hide
*/
public @NonNull RcsPresenceExchangeImplBase getPresenceExchangeImpl() {
// Base Implementation, override to implement functionality.
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
index a24af2f..fda295a 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java
@@ -17,8 +17,6 @@
package android.telephony.ims.stub;
import android.annotation.IntDef;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.os.RemoteException;
import android.telephony.ims.ImsException;
import android.telephony.ims.aidl.IRcsFeatureListener;
@@ -34,8 +32,6 @@
*
* @hide
*/
-@SystemApi
-@TestApi
public class RcsCapabilityExchange {
/** Service is unknown. */
diff --git a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
index f200ea2..bb03448 100644
--- a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java
@@ -18,8 +18,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.net.Uri;
import android.os.RemoteException;
import android.telephony.ims.ImsException;
@@ -39,8 +37,6 @@
*
* @hide
*/
-@SystemApi
-@TestApi
public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange {
private static final String LOG_TAG = "RcsPresenceExchangeIB";
diff --git a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
index 355c4dd..2035fac 100644
--- a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java
@@ -19,8 +19,6 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.net.Uri;
import android.os.RemoteException;
import android.telephony.ims.ImsException;
@@ -37,8 +35,6 @@
*
* @hide
*/
-@SystemApi
-@TestApi
public class RcsSipOptionsImplBase extends RcsCapabilityExchange {
private static final String LOG_TAG = "RcsSipOptionsImplBase";
diff --git a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
index 2c2e8282..0393248 100644
--- a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
+++ b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
@@ -16,22 +16,98 @@
package com.android.tests.rollback.host;
+import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertThrows;
+
+import com.android.tradefed.device.LogcatReceiver;
+import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import org.junit.After;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Runs the network rollback tests.
*/
@RunWith(DeviceJUnit4ClassRunner.class)
public class NetworkStagedRollbackTest extends BaseHostJUnit4Test {
/**
+ * Runs the given phase of a test by calling into the device.
+ * Throws an exception if the test phase fails.
+ * <p>
+ * For example, <code>runPhase("testApkOnlyEnableRollback");</code>
+ */
+ private void runPhase(String phase) throws Exception {
+ assertTrue(runDeviceTests("com.android.tests.rollback",
+ "com.android.tests.rollback.NetworkStagedRollbackTest",
+ phase));
+ }
+
+ private static final String REASON_EXPLICIT_HEALTH_CHECK = "REASON_EXPLICIT_HEALTH_CHECK";
+
+ private static final String ROLLBACK_INITIATE = "ROLLBACK_INITIATE";
+ private static final String ROLLBACK_BOOT_TRIGGERED = "ROLLBACK_BOOT_TRIGGERED";
+
+ private LogcatReceiver mReceiver;
+
+ @Before
+ public void setUp() throws Exception {
+ mReceiver = new LogcatReceiver(getDevice(), "logcat -s WatchdogRollbackLogger",
+ getDevice().getOptions().getMaxLogcatDataSize(), 0);
+ mReceiver.start();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mReceiver.stop();
+ mReceiver.clear();
+ }
+
+ /**
* Tests failed network health check triggers watchdog staged rollbacks.
*/
@Test
public void testNetworkFailedRollback() throws Exception {
+ try {
+ // Disconnect internet so we can test network health triggered rollbacks
+ getDevice().executeShellCommand("svc wifi disable");
+ getDevice().executeShellCommand("svc data disable");
+
+ runPhase("testNetworkFailedRollback_Phase1");
+ // Reboot device to activate staged package
+ getDevice().reboot();
+
+ // Verify rollback was enabled
+ runPhase("testNetworkFailedRollback_Phase2");
+ assertThrows(AssertionError.class, () -> runPhase("testNetworkFailedRollback_Phase3"));
+
+ getDevice().waitForDeviceAvailable();
+ // Verify rollback was executed after health check deadline
+ runPhase("testNetworkFailedRollback_Phase4");
+ InputStreamSource logcatStream = mReceiver.getLogcatData();
+ try {
+ List<String> watchdogEvents = getWatchdogLoggingEvents(logcatStream);
+ assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null,
+ REASON_EXPLICIT_HEALTH_CHECK, null));
+ assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
+ null, null));
+ } finally {
+ logcatStream.close();
+ }
+ } finally {
+ // Reconnect internet again so we won't break tests which assume internet available
+ getDevice().executeShellCommand("svc wifi enable");
+ getDevice().executeShellCommand("svc data enable");
+ }
}
/**
@@ -40,4 +116,57 @@
@Test
public void testNetworkPassedDoesNotRollback() throws Exception {
}
+
+ /**
+ * Returns a list of all Watchdog logging events which have occurred.
+ */
+ private List<String> getWatchdogLoggingEvents(InputStreamSource inputStreamSource)
+ throws Exception {
+ List<String> watchdogEvents = new ArrayList<>();
+ InputStream inputStream = inputStreamSource.createInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ if (line.contains("Watchdog event occurred")) {
+ watchdogEvents.add(line);
+ }
+ }
+ return watchdogEvents;
+ }
+
+ /**
+ * Returns whether a Watchdog event has occurred that matches the given criteria.
+ *
+ * Check the value of all non-null parameters against the list of Watchdog events that have
+ * occurred, and return {@code true} if an event exists which matches all criteria.
+ */
+ private boolean watchdogEventOccurred(List<String> loggingEvents,
+ String type, String logPackage,
+ String rollbackReason, String failedPackageName) throws Exception {
+ List<String> eventCriteria = new ArrayList<>();
+ if (type != null) {
+ eventCriteria.add("type: " + type);
+ }
+ if (logPackage != null) {
+ eventCriteria.add("logPackage: " + logPackage);
+ }
+ if (rollbackReason != null) {
+ eventCriteria.add("rollbackReason: " + rollbackReason);
+ }
+ if (failedPackageName != null) {
+ eventCriteria.add("failedPackageName: " + failedPackageName);
+ }
+ for (String loggingEvent: loggingEvents) {
+ boolean matchesCriteria = true;
+ for (String criterion: eventCriteria) {
+ if (!loggingEvent.contains(criterion)) {
+ matchesCriteria = false;
+ }
+ }
+ if (matchesCriteria) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
index 04004d6..e5c8a68 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/NetworkStagedRollbackTest.java
@@ -16,9 +16,143 @@
package com.android.tests.rollback;
+import static com.android.cts.rollback.lib.RollbackInfoSubject.assertThat;
+import static com.android.cts.rollback.lib.RollbackUtils.getUniqueRollbackInfoForPackage;
+
+import android.Manifest;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.rollback.RollbackManager;
+import android.os.ParcelFileDescriptor;
+import android.provider.DeviceConfig;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.cts.install.lib.Install;
+import com.android.cts.install.lib.InstallUtils;
+import com.android.cts.install.lib.TestApp;
+import com.android.cts.rollback.lib.RollbackUtils;
+
+import libcore.io.IoUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
@RunWith(JUnit4.class)
public class NetworkStagedRollbackTest {
+ private static final String NETWORK_STACK_CONNECTOR_CLASS =
+ "android.net.INetworkStackConnector";
+ private static final String PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS =
+ "watchdog_request_timeout_millis";
+
+ private static final TestApp NETWORK_STACK = new TestApp("NetworkStack",
+ getNetworkStackPackageName(), -1, false, findNetworkStackApk());
+
+ private static File findNetworkStackApk() {
+ final File apk = new File("/system/priv-app/NetworkStack/NetworkStack.apk");
+ if (apk.isFile()) {
+ return apk;
+ }
+ return new File("/system/priv-app/NetworkStackNext/NetworkStackNext.apk");
+ }
+
+ /**
+ * Adopts common shell permissions needed for rollback tests.
+ */
+ @Before
+ public void adoptShellPermissions() {
+ InstallUtils.adoptShellPermissionIdentity(
+ Manifest.permission.INSTALL_PACKAGES,
+ Manifest.permission.DELETE_PACKAGES,
+ Manifest.permission.TEST_MANAGE_ROLLBACKS,
+ Manifest.permission.FORCE_STOP_PACKAGES,
+ Manifest.permission.WRITE_DEVICE_CONFIG);
+ }
+
+ /**
+ * Drops shell permissions needed for rollback tests.
+ */
+ @After
+ public void dropShellPermissions() {
+ InstallUtils.dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void testNetworkFailedRollback_Phase1() throws Exception {
+ // Remove available rollbacks and uninstall NetworkStack on /data/
+ RollbackManager rm = RollbackUtils.getRollbackManager();
+ String networkStack = getNetworkStackPackageName();
+
+ rm.expireRollbackForPackage(networkStack);
+ uninstallNetworkStackPackage();
+
+ assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+ networkStack)).isNull();
+
+ // Reduce health check deadline
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
+ PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS,
+ Integer.toString(120000), false);
+ // Simulate re-installation of new NetworkStack with rollbacks enabled
+ installNetworkStackPackage();
+ }
+
+ @Test
+ public void testNetworkFailedRollback_Phase2() throws Exception {
+ RollbackManager rm = RollbackUtils.getRollbackManager();
+ assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
+ getNetworkStackPackageName())).isNotNull();
+
+ // Sleep for < health check deadline
+ Thread.sleep(TimeUnit.SECONDS.toMillis(5));
+ // Verify rollback was not executed before health check deadline
+ assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
+ getNetworkStackPackageName())).isNull();
+ }
+
+ @Test
+ public void testNetworkFailedRollback_Phase3() throws Exception {
+ // Sleep for > health check deadline (120s to trigger rollback + 120s to reboot)
+ // The device is expected to reboot during sleeping. This device method will fail and
+ // the host will catch the assertion. If reboot doesn't happen, the host will fail the
+ // assertion.
+ Thread.sleep(TimeUnit.SECONDS.toMillis(240));
+ }
+
+ @Test
+ public void testNetworkFailedRollback_Phase4() throws Exception {
+ RollbackManager rm = RollbackUtils.getRollbackManager();
+ assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
+ getNetworkStackPackageName())).isNotNull();
+ }
+
+ private static String getNetworkStackPackageName() {
+ Intent intent = new Intent(NETWORK_STACK_CONNECTOR_CLASS);
+ ComponentName comp = intent.resolveSystemService(
+ InstrumentationRegistry.getInstrumentation().getContext().getPackageManager(), 0);
+ return comp.getPackageName();
+ }
+
+ private static void installNetworkStackPackage() throws Exception {
+ Install.single(NETWORK_STACK).setStaged().setEnableRollback()
+ .addInstallFlags(PackageManager.INSTALL_REPLACE_EXISTING).commit();
+ }
+
+ private static void uninstallNetworkStackPackage() {
+ // Uninstall the package as a privileged user so we won't fail due to permission.
+ runShellCommand("pm uninstall " + getNetworkStackPackageName());
+ }
+
+ private static void runShellCommand(String cmd) {
+ ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .executeShellCommand(cmd);
+ IoUtils.closeQuietly(pfd);
+ }
}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 0fdcbc5..bddd93c 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -274,55 +274,6 @@
TestApp.B)).isNotNull();
}
- @Test
- public void testNetworkFailedRollback_Phase1() throws Exception {
- // Remove available rollbacks and uninstall NetworkStack on /data/
- RollbackManager rm = RollbackUtils.getRollbackManager();
- String networkStack = getNetworkStackPackageName();
-
- rm.expireRollbackForPackage(networkStack);
- uninstallNetworkStackPackage();
-
- assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
- networkStack)).isNull();
-
- // Reduce health check deadline
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ROLLBACK,
- PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS,
- Integer.toString(120000), false);
- // Simulate re-installation of new NetworkStack with rollbacks enabled
- installNetworkStackPackage();
- }
-
- @Test
- public void testNetworkFailedRollback_Phase2() throws Exception {
- RollbackManager rm = RollbackUtils.getRollbackManager();
- assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
- getNetworkStackPackageName())).isNotNull();
-
- // Sleep for < health check deadline
- Thread.sleep(TimeUnit.SECONDS.toMillis(5));
- // Verify rollback was not executed before health check deadline
- assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
- getNetworkStackPackageName())).isNull();
- }
-
- @Test
- public void testNetworkFailedRollback_Phase3() throws Exception {
- // Sleep for > health check deadline (120s to trigger rollback + 120s to reboot)
- // The device is expected to reboot during sleeping. This device method will fail and
- // the host will catch the assertion. If reboot doesn't happen, the host will fail the
- // assertion.
- Thread.sleep(TimeUnit.SECONDS.toMillis(240));
- }
-
- @Test
- public void testNetworkFailedRollback_Phase4() throws Exception {
- RollbackManager rm = RollbackUtils.getRollbackManager();
- assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
- getNetworkStackPackageName())).isNotNull();
- }
-
private static String getNetworkStackPackageName() {
Intent intent = new Intent(NETWORK_STACK_CONNECTOR_CLASS);
ComponentName comp = intent.resolveSystemService(
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 032f182..3c5eaef 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -228,44 +228,6 @@
}
/**
- * Tests failed network health check triggers watchdog staged rollbacks.
- */
- @Test
- public void testNetworkFailedRollback() throws Exception {
- try {
- // Disconnect internet so we can test network health triggered rollbacks
- getDevice().executeShellCommand("svc wifi disable");
- getDevice().executeShellCommand("svc data disable");
-
- runPhase("testNetworkFailedRollback_Phase1");
- // Reboot device to activate staged package
- getDevice().reboot();
-
- // Verify rollback was enabled
- runPhase("testNetworkFailedRollback_Phase2");
- assertThrows(AssertionError.class, () -> runPhase("testNetworkFailedRollback_Phase3"));
-
- getDevice().waitForDeviceAvailable();
- // Verify rollback was executed after health check deadline
- runPhase("testNetworkFailedRollback_Phase4");
- InputStreamSource logcatStream = mReceiver.getLogcatData();
- try {
- List<String> watchdogEvents = getWatchdogLoggingEvents(logcatStream);
- assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null,
- REASON_EXPLICIT_HEALTH_CHECK, null));
- assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
- null, null));
- } finally {
- logcatStream.close();
- }
- } finally {
- // Reconnect internet again so we won't break tests which assume internet available
- getDevice().executeShellCommand("svc wifi enable");
- getDevice().executeShellCommand("svc data enable");
- }
- }
-
- /**
* Tests passed network health check does not trigger watchdog staged rollbacks.
*/
@Test
diff --git a/tests/RollbackTest/TEST_MAPPING b/tests/RollbackTest/TEST_MAPPING
index fefde5b..0f4c460 100644
--- a/tests/RollbackTest/TEST_MAPPING
+++ b/tests/RollbackTest/TEST_MAPPING
@@ -7,6 +7,9 @@
"name": "StagedRollbackTest"
},
{
+ "name": "NetworkStagedRollbackTest"
+ },
+ {
"name": "MultiUserRollbackTest"
}
]
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index d4e024d..1c330e2 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -272,4 +272,8 @@
boolean isScanThrottleEnabled();
Map getAllMatchingPasspointProfilesForScanResults(in List<ScanResult> scanResult);
+
+ void setAutoWakeupEnabled(boolean enable);
+
+ boolean isAutoWakeupEnabled();
}
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index a720236..0cce23d 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -951,7 +951,7 @@
* Indicate whether the network is trusted or not. Networks are considered trusted
* if the user explicitly allowed this network connection.
* This bit can be used by suggestion network, see
- * {@link WifiNetworkSuggestion.Builder#setUnTrusted(boolean)}
+ * {@link WifiNetworkSuggestion.Builder#setUntrusted(boolean)}
* @hide
*/
public boolean trusted;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index fb30910c..b6f4490 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -6163,4 +6163,50 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Enable/disable wifi auto wakeup feature.
+ *
+ * <p>
+ * The feature is described in
+ * <a href="Wi-Fi Turn on automatically">
+ * https://source.android.com/devices/tech/connect/wifi-infrastructure
+ * #turn_on_wi-fi_automatically
+ * </a>
+ *
+ * @param enable true to enable, false to disable.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void setAutoWakeupEnabled(boolean enable) {
+ try {
+ mService.setAutoWakeupEnabled(enable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the persisted Wi-Fi auto wakeup feature state. Defaults to false, unless changed by the
+ * user via Settings.
+ *
+ * <p>
+ * The feature is described in
+ * <a href="Wi-Fi Turn on automatically">
+ * https://source.android.com/devices/tech/connect/wifi-infrastructure
+ * #turn_on_wi-fi_automatically
+ * </a>
+ *
+ * @return true to indicate that wakeup feature is enabled, false to indicate that wakeup
+ * feature is disabled.
+ */
+ @RequiresPermission(ACCESS_WIFI_STATE)
+ public boolean isAutoWakeupEnabled() {
+ try {
+ return mService.isAutoWakeupEnabled();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 847040c..853212a 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -2391,4 +2391,14 @@
assertFalse(mWifiManager.isScanThrottleEnabled());
verify(mWifiService).isScanThrottleEnabled();
}
+
+ @Test
+ public void testAutoWakeup() throws Exception {
+ mWifiManager.setAutoWakeupEnabled(true);
+ verify(mWifiService).setAutoWakeupEnabled(true);
+
+ when(mWifiService.isAutoWakeupEnabled()).thenReturn(false);
+ assertFalse(mWifiManager.isAutoWakeupEnabled());
+ verify(mWifiService).isAutoWakeupEnabled();
+ }
}