Merge "Fix InTouchMode is not consistent between WMS and InputFlinger"
diff --git a/Android.bp b/Android.bp
index ffb4d3a..2318f7b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -734,6 +734,7 @@
"core/java/android/annotation/RequiresPermission.java",
"core/java/android/annotation/SystemApi.java",
"core/java/android/annotation/TestApi.java",
+ "core/java/com/android/internal/annotations/GuardedBy.java",
],
}
// Build ext.jar
diff --git a/apex/blobstore/TEST_MAPPING b/apex/blobstore/TEST_MAPPING
index 4dc0c49..cfe19a5 100644
--- a/apex/blobstore/TEST_MAPPING
+++ b/apex/blobstore/TEST_MAPPING
@@ -2,6 +2,14 @@
"presubmit": [
{
"name": "CtsBlobStoreTestCases"
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {
+ "include-filter": "com.android.server.blob"
+ }
+ ]
}
]
}
\ No newline at end of file
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
index f7e6a98..f110b36 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
@@ -45,6 +45,10 @@
public final class BlobHandle implements Parcelable {
private static final String ALGO_SHA_256 = "SHA-256";
+ private static final String[] SUPPORTED_ALGOS = {
+ ALGO_SHA_256
+ };
+
private static final int LIMIT_BLOB_TAG_LENGTH = 128; // characters
/**
@@ -104,14 +108,9 @@
public static @NonNull BlobHandle create(@NonNull String algorithm, @NonNull byte[] digest,
@NonNull CharSequence label, @CurrentTimeMillisLong long expiryTimeMillis,
@NonNull String tag) {
- Preconditions.checkNotNull(algorithm, "algorithm must not be null");
- Preconditions.checkNotNull(digest, "digest must not be null");
- Preconditions.checkNotNull(label, "label must not be null");
- Preconditions.checkArgumentNonnegative(expiryTimeMillis,
- "expiryTimeMillis must not be negative");
- Preconditions.checkNotNull(tag, "tag must not be null");
- Preconditions.checkArgument(tag.length() <= LIMIT_BLOB_TAG_LENGTH, "tag too long");
- return new BlobHandle(algorithm, digest, label, expiryTimeMillis, tag);
+ final BlobHandle handle = new BlobHandle(algorithm, digest, label, expiryTimeMillis, tag);
+ handle.assertIsValid();
+ return handle;
}
/**
@@ -215,12 +214,47 @@
}
/** @hide */
- public void dump(IndentingPrintWriter fout) {
- fout.println("algo: " + algorithm);
- fout.println("digest: " + Base64.encodeToString(digest, Base64.NO_WRAP));
- fout.println("label: " + label);
- fout.println("expiryMs: " + expiryTimeMillis);
- fout.println("tag: " + tag);
+ public void dump(IndentingPrintWriter fout, boolean dumpFull) {
+ if (dumpFull) {
+ fout.println("algo: " + algorithm);
+ fout.println("digest: " + (dumpFull ? encodeDigest() : safeDigest()));
+ fout.println("label: " + label);
+ fout.println("expiryMs: " + expiryTimeMillis);
+ fout.println("tag: " + tag);
+ } else {
+ fout.println(toString());
+ }
+ }
+
+ /** @hide */
+ public void assertIsValid() {
+ Preconditions.checkArgumentIsSupported(SUPPORTED_ALGOS, algorithm);
+ Preconditions.checkByteArrayNotEmpty(digest, "digest");
+ Preconditions.checkStringNotEmpty(label, "label must not be null");
+ Preconditions.checkArgumentNonnegative(expiryTimeMillis,
+ "expiryTimeMillis must not be negative");
+ Preconditions.checkStringNotEmpty(tag, "tag must not be null");
+ Preconditions.checkArgument(tag.length() <= LIMIT_BLOB_TAG_LENGTH, "tag too long");
+ }
+
+ @Override
+ public String toString() {
+ return "BlobHandle {"
+ + "algo:" + algorithm + ","
+ + "digest:" + safeDigest() + ","
+ + "label:" + label + ","
+ + "expiryMs:" + expiryTimeMillis + ","
+ + "tag:" + tag
+ + "}";
+ }
+
+ private String safeDigest() {
+ final String digestStr = encodeDigest();
+ return digestStr.substring(0, 2) + ".." + digestStr.substring(digestStr.length() - 2);
+ }
+
+ private String encodeDigest() {
+ return Base64.encodeToString(digest, Base64.NO_WRAP);
}
public static final @NonNull Creator<BlobHandle> CREATOR = new Creator<BlobHandle>() {
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 e9838d6..aba3e8c 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -48,6 +48,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
+import com.android.server.blob.BlobStoreManagerService.DumpArgs;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -156,6 +157,12 @@
}
}
+ boolean hasLeases() {
+ synchronized (mMetadataLock) {
+ return mLeasees.isEmpty();
+ }
+ }
+
boolean isAccessAllowedForCaller(String callingPackage, int callingUid) {
// TODO: verify blob is still valid (expiryTime is not elapsed)
synchronized (mMetadataLock) {
@@ -234,10 +241,10 @@
return revocableFd.getRevocableFileDescriptor();
}
- void dump(IndentingPrintWriter fout) {
+ void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) {
fout.println("blobHandle:");
fout.increaseIndent();
- blobHandle.dump(fout);
+ blobHandle.dump(fout, dumpArgs.shouldDumpFull());
fout.decreaseIndent();
fout.println("Committers:");
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 fcc30e3..13f095e 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -40,6 +40,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.blob.BlobHandle;
import android.app.blob.IBlobStoreManager;
import android.app.blob.IBlobStoreSession;
@@ -49,6 +50,7 @@
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
+import android.content.res.ResourceId;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
@@ -67,6 +69,8 @@
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
@@ -91,6 +95,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* Service responsible for maintaining and facilitating access to data blobs published by apps.
@@ -112,20 +117,35 @@
private final Context mContext;
private final Handler mHandler;
+ private final Injector mInjector;
private final SessionStateChangeListener mSessionStateChangeListener =
new SessionStateChangeListener();
private PackageManagerInternal mPackageManagerInternal;
- public BlobStoreManagerService(Context context) {
- super(context);
- mContext = context;
+ private final Runnable mSaveBlobsInfoRunnable = this::writeBlobsInfo;
+ private final Runnable mSaveSessionsRunnable = this::writeBlobSessions;
+ public BlobStoreManagerService(Context context) {
+ this(context, new Injector());
+ }
+
+ @VisibleForTesting
+ BlobStoreManagerService(Context context, Injector injector) {
+ super(context);
+
+ mContext = context;
+ mInjector = injector;
+ mHandler = mInjector.initializeMessageHandler();
+ }
+
+ private static Handler initializeMessageHandler() {
final HandlerThread handlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
handlerThread.start();
- mHandler = new Handler(handlerThread.getLooper());
- Watchdog.getInstance().addThread(mHandler);
+ final Handler handler = new Handler(handlerThread.getLooper());
+ Watchdog.getInstance().addThread(handler);
+ return handler;
}
@Override
@@ -181,6 +201,20 @@
return userBlobs;
}
+ @VisibleForTesting
+ void addUserSessionsForTest(LongSparseArray<BlobStoreSession> userSessions, int userId) {
+ synchronized (mBlobsLock) {
+ mSessions.put(userId, userSessions);
+ }
+ }
+
+ @VisibleForTesting
+ void addUserBlobsForTest(ArrayMap<BlobHandle, BlobMetadata> userBlobs, int userId) {
+ synchronized (mBlobsLock) {
+ mBlobsMap.put(userId, userBlobs);
+ }
+ }
+
private long createSessionInternal(BlobHandle blobHandle,
int callingUid, String callingPackage) {
synchronized (mBlobsLock) {
@@ -293,23 +327,23 @@
case STATE_ABANDONED:
case STATE_VERIFIED_INVALID:
session.getSessionFile().delete();
- getUserSessionsLocked(UserHandle.getUserId(session.ownerUid))
- .remove(session.sessionId);
+ getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
+ .remove(session.getSessionId());
break;
case STATE_COMMITTED:
session.verifyBlobData();
break;
case STATE_VERIFIED_VALID:
- final int userId = UserHandle.getUserId(session.ownerUid);
+ final int userId = UserHandle.getUserId(session.getOwnerUid());
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(userId);
- BlobMetadata blob = userBlobs.get(session.blobHandle);
+ BlobMetadata blob = userBlobs.get(session.getBlobHandle());
if (blob == null) {
blob = new BlobMetadata(mContext,
- session.sessionId, session.blobHandle, userId);
- userBlobs.put(session.blobHandle, blob);
+ session.getSessionId(), session.getBlobHandle(), userId);
+ userBlobs.put(session.getBlobHandle(), blob);
}
- final Committer newCommitter = new Committer(session.ownerPackageName,
- session.ownerUid, session.getBlobAccessMode());
+ final Committer newCommitter = new Committer(session.getOwnerPackageName(),
+ session.getOwnerUid(), session.getBlobAccessMode());
final Committer existingCommitter = blob.getExistingCommitter(newCommitter);
blob.addCommitter(newCommitter);
try {
@@ -319,8 +353,8 @@
blob.addCommitter(existingCommitter);
session.sendCommitCallbackResult(COMMIT_RESULT_ERROR);
}
- getUserSessionsLocked(UserHandle.getUserId(session.ownerUid))
- .remove(session.sessionId);
+ getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
+ .remove(session.getSessionId());
break;
default:
Slog.wtf(TAG, "Invalid session state: "
@@ -399,17 +433,17 @@
continue;
}
final SparseArray<String> userPackages = allPackages.get(
- UserHandle.getUserId(session.ownerUid));
+ UserHandle.getUserId(session.getOwnerUid()));
if (userPackages != null
- && session.ownerPackageName.equals(
- userPackages.get(session.ownerUid))) {
- getUserSessionsLocked(UserHandle.getUserId(session.ownerUid)).put(
- session.sessionId, session);
+ && session.getOwnerPackageName().equals(
+ userPackages.get(session.getOwnerUid()))) {
+ getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid())).put(
+ session.getSessionId(), session);
} else {
// Unknown package or the session data does not belong to this package.
session.getSessionFile().delete();
}
- mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, session.sessionId);
+ mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, session.getSessionId());
}
}
} catch (Exception e) {
@@ -504,9 +538,9 @@
}
private void writeBlobsInfoAsync() {
- mHandler.post(PooledLambda.obtainRunnable(
- BlobStoreManagerService::writeBlobsInfo,
- BlobStoreManagerService.this).recycleOnUse());
+ if (!mHandler.hasCallbacks(mSaveBlobsInfoRunnable)) {
+ mHandler.post(mSaveBlobsInfoRunnable);
+ }
}
private void writeBlobSessions() {
@@ -520,9 +554,9 @@
}
private void writeBlobSessionsAsync() {
- mHandler.post(PooledLambda.obtainRunnable(
- BlobStoreManagerService::writeBlobSessions,
- BlobStoreManagerService.this).recycleOnUse());
+ if (!mHandler.hasCallbacks(mSaveSessionsRunnable)) {
+ mHandler.post(mSaveSessionsRunnable);
+ }
}
private int getPackageUid(String packageName, int userId) {
@@ -568,7 +602,8 @@
return new AtomicFile(file, "blobs_index" /* commitLogTag */);
}
- private void handlePackageRemoved(String packageName, int uid) {
+ @VisibleForTesting
+ void handlePackageRemoved(String packageName, int uid) {
synchronized (mBlobsLock) {
// Clean up any pending sessions
final LongSparseArray<BlobStoreSession> userSessions =
@@ -576,25 +611,35 @@
final ArrayList<Integer> indicesToRemove = new ArrayList<>();
for (int i = 0, count = userSessions.size(); i < count; ++i) {
final BlobStoreSession session = userSessions.valueAt(i);
- if (session.ownerUid == uid
- && session.ownerPackageName.equals(packageName)) {
+ if (session.getOwnerUid() == uid
+ && session.getOwnerPackageName().equals(packageName)) {
session.getSessionFile().delete();
indicesToRemove.add(i);
}
}
for (int i = 0, count = indicesToRemove.size(); i < count; ++i) {
- userSessions.removeAt(i);
+ userSessions.removeAt(indicesToRemove.get(i));
}
+ writeBlobSessionsAsync();
// Remove the package from the committer and leasee list
final ArrayMap<BlobHandle, BlobMetadata> userBlobs =
getUserBlobsLocked(UserHandle.getUserId(uid));
+ indicesToRemove.clear();
for (int i = 0, count = userBlobs.size(); i < count; ++i) {
final BlobMetadata blobMetadata = userBlobs.valueAt(i);
blobMetadata.removeCommitter(packageName, uid);
blobMetadata.removeLeasee(packageName, uid);
+ // Delete the blob if it doesn't have any active leases.
+ if (!blobMetadata.hasLeases()) {
+ blobMetadata.getBlobFile().delete();
+ indicesToRemove.add(i);
+ }
}
- // TODO: clean-up blobs which doesn't have any active leases.
+ for (int i = 0, count = indicesToRemove.size(); i < count; ++i) {
+ userBlobs.removeAt(indicesToRemove.get(i));
+ }
+ writeBlobsInfoAsync();
}
}
@@ -620,6 +665,80 @@
}
}
+ void runClearAllSessions(@UserIdInt int userId) {
+ synchronized (mBlobsLock) {
+ if (userId == UserHandle.USER_ALL) {
+ mSessions.clear();
+ } else {
+ mSessions.remove(userId);
+ }
+ writeBlobSessionsAsync();
+ }
+ }
+
+ void runClearAllBlobs(@UserIdInt int userId) {
+ synchronized (mBlobsLock) {
+ if (userId == UserHandle.USER_ALL) {
+ mBlobsMap.clear();
+ } else {
+ mBlobsMap.remove(userId);
+ }
+ writeBlobsInfoAsync();
+ }
+ }
+
+ @GuardedBy("mBlobsLock")
+ private void dumpSessionsLocked(IndentingPrintWriter fout, DumpArgs dumpArgs) {
+ for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
+ final int userId = mSessions.keyAt(i);
+ if (!dumpArgs.shouldDumpUser(userId)) {
+ continue;
+ }
+ final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i);
+ fout.println("List of sessions in user #"
+ + userId + " (" + userSessions.size() + "):");
+ fout.increaseIndent();
+ for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) {
+ final long sessionId = userSessions.keyAt(j);
+ final BlobStoreSession session = userSessions.valueAt(j);
+ if (!dumpArgs.shouldDumpSession(session.getOwnerPackageName(),
+ session.getOwnerUid(), session.getSessionId())) {
+ continue;
+ }
+ fout.println("Session #" + sessionId);
+ fout.increaseIndent();
+ session.dump(fout, dumpArgs);
+ fout.decreaseIndent();
+ }
+ fout.decreaseIndent();
+ }
+ }
+
+ @GuardedBy("mBlobsLock")
+ private void dumpBlobsLocked(IndentingPrintWriter fout, DumpArgs dumpArgs) {
+ for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
+ final int userId = mBlobsMap.keyAt(i);
+ if (!dumpArgs.shouldDumpUser(userId)) {
+ continue;
+ }
+ final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
+ fout.println("List of blobs in user #"
+ + userId + " (" + userBlobs.size() + "):");
+ fout.increaseIndent();
+ for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
+ final BlobMetadata blobMetadata = userBlobs.valueAt(j);
+ if (!dumpArgs.shouldDumpBlob(blobMetadata.blobId)) {
+ continue;
+ }
+ fout.println("Blob #" + blobMetadata.blobId);
+ fout.increaseIndent();
+ blobMetadata.dump(fout, dumpArgs);
+ fout.decreaseIndent();
+ }
+ fout.decreaseIndent();
+ }
+ }
+
private class PackageChangedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -658,10 +777,9 @@
@IntRange(from = 1)
public long createSession(@NonNull BlobHandle blobHandle,
@NonNull String packageName) {
- Preconditions.checkNotNull(blobHandle, "blobHandle must not be null");
- Preconditions.checkNotNull(packageName, "packageName must not be null");
- // TODO: verify blobHandle.algorithm is sha-256
- // TODO: assert blobHandle is valid.
+ Objects.requireNonNull(blobHandle, "blobHandle must not be null");
+ blobHandle.assertIsValid();
+ Objects.requireNonNull(packageName, "packageName must not be null");
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
@@ -682,7 +800,7 @@
@NonNull String packageName) {
Preconditions.checkArgumentPositive(sessionId,
"sessionId must be positive: " + sessionId);
- Preconditions.checkNotNull(packageName, "packageName must not be null");
+ Objects.requireNonNull(packageName, "packageName must not be null");
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
@@ -695,7 +813,7 @@
@NonNull String packageName) {
Preconditions.checkArgumentPositive(sessionId,
"sessionId must be positive: " + sessionId);
- Preconditions.checkNotNull(packageName, "packageName must not be null");
+ Objects.requireNonNull(packageName, "packageName must not be null");
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
@@ -706,8 +824,9 @@
@Override
public ParcelFileDescriptor openBlob(@NonNull BlobHandle blobHandle,
@NonNull String packageName) {
- Preconditions.checkNotNull(blobHandle, "blobHandle must not be null");
- Preconditions.checkNotNull(packageName, "packageName must not be null");
+ Objects.requireNonNull(blobHandle, "blobHandle must not be null");
+ blobHandle.assertIsValid();
+ Objects.requireNonNull(packageName, "packageName must not be null");
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
@@ -727,24 +846,27 @@
@Override
public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId,
- @CurrentTimeSecondsLong long leaseTimeoutSecs, @NonNull String packageName) {
- Preconditions.checkNotNull(blobHandle, "blobHandle must not be null");
- Preconditions.checkNotNull(packageName, "packageName must not be null");
- Preconditions.checkArgumentPositive(descriptionResId,
- "descriptionResId must be positive; value=" + descriptionResId);
+ @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.checkArgumentNonnegative(leaseExpiryTimeMillis,
+ "leaseExpiryTimeMillis must not be negative");
+ Objects.requireNonNull(packageName, "packageName must not be null");
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
- acquireLeaseInternal(blobHandle, descriptionResId, leaseTimeoutSecs,
+ acquireLeaseInternal(blobHandle, descriptionResId, leaseExpiryTimeMillis,
callingUid, packageName);
}
@Override
public void releaseLease(@NonNull BlobHandle blobHandle, @NonNull String packageName) {
- Preconditions.checkNotNull(blobHandle, "blobHandle must not be null");
- Preconditions.checkNotNull(packageName, "packageName must not be null");
-
+ Objects.requireNonNull(blobHandle, "blobHandle must not be null");
+ blobHandle.assertIsValid();
+ Objects.requireNonNull(packageName, "packageName must not be null");
final int callingUid = Binder.getCallingUid();
verifyCallingPackage(callingUid, packageName);
@@ -754,7 +876,7 @@
@Override
public void waitForIdle(@NonNull RemoteCallback remoteCallback) {
- Preconditions.checkNotNull(remoteCallback, "remoteCallback must not be null");
+ Objects.requireNonNull(remoteCallback, "remoteCallback must not be null");
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
"Caller is not allowed to call this; caller=" + Binder.getCallingUid());
@@ -766,47 +888,164 @@
public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
@Nullable String[] args) {
// TODO: add proto-based version of this.
- if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+ if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, writer)) return;
+
+ final DumpArgs dumpArgs = DumpArgs.parse(args);
final IndentingPrintWriter fout = new IndentingPrintWriter(writer, " ");
synchronized (mBlobsLock) {
fout.println("mCurrentMaxSessionId: " + mCurrentMaxSessionId);
fout.println();
- for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
- final int userId = mSessions.keyAt(i);
- final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i);
- fout.println("List of sessions in user #"
- + userId + " (" + userSessions.size() + "):");
- fout.increaseIndent();
- for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) {
- final long sessionId = userSessions.keyAt(j);
- final BlobStoreSession session = userSessions.valueAt(j);
- fout.println("Session #" + sessionId);
- fout.increaseIndent();
- session.dump(fout);
- fout.decreaseIndent();
- }
- fout.decreaseIndent();
+
+ if (dumpArgs.shouldDumpSessions()) {
+ dumpSessionsLocked(fout, dumpArgs);
+ fout.println();
}
-
- fout.print("\n\n");
-
- for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
- final int userId = mBlobsMap.keyAt(i);
- final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
- fout.println("List of blobs in user #"
- + userId + " (" + userBlobs.size() + "):");
- fout.increaseIndent();
- for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
- final BlobMetadata blobMetadata = userBlobs.valueAt(j);
- fout.println("Blob #" + blobMetadata.blobId);
- fout.increaseIndent();
- blobMetadata.dump(fout);
- fout.decreaseIndent();
- }
- fout.decreaseIndent();
+ if (dumpArgs.shouldDumpBlobs()) {
+ dumpBlobsLocked(fout, dumpArgs);
+ fout.println();
}
}
}
+
+ @Override
+ public int handleShellCommand(@NonNull ParcelFileDescriptor in,
+ @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+ @NonNull String[] args) {
+ return (new BlobStoreManagerShellCommand(BlobStoreManagerService.this)).exec(this,
+ in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args);
+ }
+ }
+
+ static final class DumpArgs {
+ private boolean mDumpFull;
+ private final ArrayList<String> mDumpPackages = new ArrayList<>();
+ private final ArrayList<Integer> mDumpUids = new ArrayList<>();
+ private final ArrayList<Integer> mDumpUserIds = new ArrayList<>();
+ private final ArrayList<Long> mDumpBlobIds = new ArrayList<>();
+ private boolean mDumpOnlySelectedSections;
+ private boolean mDumpSessions;
+ private boolean mDumpBlobs;
+
+ public boolean shouldDumpSession(String packageName, int uid, long blobId) {
+ if (!CollectionUtils.isEmpty(mDumpPackages)
+ && mDumpPackages.indexOf(packageName) < 0) {
+ return false;
+ }
+ if (!CollectionUtils.isEmpty(mDumpUids)
+ && mDumpUids.indexOf(uid) < 0) {
+ return false;
+ }
+ if (!CollectionUtils.isEmpty(mDumpBlobIds)
+ && mDumpBlobIds.indexOf(blobId) < 0) {
+ return false;
+ }
+ return true;
+ }
+
+ public boolean shouldDumpSessions() {
+ if (!mDumpOnlySelectedSections) {
+ return true;
+ }
+ return mDumpSessions;
+ }
+
+ public boolean shouldDumpBlobs() {
+ if (!mDumpOnlySelectedSections) {
+ return true;
+ }
+ return mDumpBlobs;
+ }
+
+ public boolean shouldDumpBlob(long blobId) {
+ return CollectionUtils.isEmpty(mDumpBlobIds)
+ || mDumpBlobIds.indexOf(blobId) >= 0;
+ }
+
+ public boolean shouldDumpFull() {
+ return mDumpFull;
+ }
+
+ public boolean shouldDumpUser(int userId) {
+ return CollectionUtils.isEmpty(mDumpUserIds)
+ || mDumpUserIds.indexOf(userId) >= 0;
+ }
+
+ private DumpArgs() {}
+
+ public static DumpArgs parse(String[] args) {
+ final DumpArgs dumpArgs = new DumpArgs();
+ if (args == null) {
+ return dumpArgs;
+ }
+
+ for (int i = 0; i < args.length; ++i) {
+ final String opt = args[i];
+ if ("--full".equals(opt) || "-f".equals(opt)) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
+ dumpArgs.mDumpFull = true;
+ }
+ } else if ("--sessions".equals(opt)) {
+ dumpArgs.mDumpOnlySelectedSections = true;
+ dumpArgs.mDumpSessions = true;
+ } else if ("--blobs".equals(opt)) {
+ dumpArgs.mDumpOnlySelectedSections = true;
+ dumpArgs.mDumpBlobs = true;
+ } else if ("--package".equals(opt) || "-p".equals(opt)) {
+ dumpArgs.mDumpPackages.add(getStringArgRequired(args, ++i, "packageName"));
+ } else if ("--uid".equals(opt) || "-u".equals(opt)) {
+ dumpArgs.mDumpUids.add(getIntArgRequired(args, ++i, "uid"));
+ } else if ("--user".equals(opt)) {
+ dumpArgs.mDumpUserIds.add(getIntArgRequired(args, ++i, "userId"));
+ } else if ("--blob".equals(opt) || "-b".equals(opt)) {
+ dumpArgs.mDumpBlobIds.add(getLongArgRequired(args, ++i, "blobId"));
+ } else {
+ // Everything else is assumed to be blob ids.
+ dumpArgs.mDumpBlobIds.add(getLongArgRequired(args, i, "blobId"));
+ }
+ }
+ return dumpArgs;
+ }
+
+ private static String getStringArgRequired(String[] args, int index, String argName) {
+ if (index >= args.length) {
+ throw new IllegalArgumentException("Missing " + argName);
+ }
+ return args[index];
+ }
+
+ private static int getIntArgRequired(String[] args, int index, String argName) {
+ if (index >= args.length) {
+ throw new IllegalArgumentException("Missing " + argName);
+ }
+ final int value;
+ try {
+ value = Integer.parseInt(args[index]);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid " + argName + ": " + args[index]);
+ }
+ return value;
+ }
+
+ private static long getLongArgRequired(String[] args, int index, String argName) {
+ if (index >= args.length) {
+ throw new IllegalArgumentException("Missing " + argName);
+ }
+ final long value;
+ try {
+ value = Long.parseLong(args[index]);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Invalid " + argName + ": " + args[index]);
+ }
+ return value;
+ }
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ public Handler initializeMessageHandler() {
+ return BlobStoreManagerService.initializeMessageHandler();
+ }
}
}
\ No newline at end of file
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerShellCommand.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerShellCommand.java
new file mode 100644
index 0000000..3ac30f8
--- /dev/null
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerShellCommand.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 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.blob;
+
+import android.os.ShellCommand;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+
+class BlobStoreManagerShellCommand extends ShellCommand {
+
+ private final BlobStoreManagerService mService;
+
+ BlobStoreManagerShellCommand(BlobStoreManagerService blobStoreManagerService) {
+ mService = blobStoreManagerService;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(null);
+ }
+ final PrintWriter pw = getOutPrintWriter();
+ switch (cmd) {
+ case "clear-all-sessions":
+ return runClearAllSessions(pw);
+ case "clear-all-blobs":
+ return runClearAllBlobs(pw);
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ private int runClearAllSessions(PrintWriter pw) {
+ final ParsedArgs args = new ParsedArgs();
+ args.userId = UserHandle.USER_ALL;
+
+ if (parseOptions(pw, args) < 0) {
+ return -1;
+ }
+
+ mService.runClearAllSessions(args.userId);
+ return 0;
+ }
+
+ private int runClearAllBlobs(PrintWriter pw) {
+ final ParsedArgs args = new ParsedArgs();
+ args.userId = UserHandle.USER_ALL;
+
+ if (parseOptions(pw, args) < 0) {
+ return -1;
+ }
+
+ mService.runClearAllBlobs(args.userId);
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("BlobStore service (blob_store) commands:");
+ pw.println("help");
+ pw.println(" Print this help text.");
+ pw.println();
+ pw.println("clear-all-sessions [-u | --user USER_ID]");
+ pw.println(" Remove all sessions.");
+ pw.println(" Options:");
+ pw.println(" -u or --user: specify which user's sessions to be removed;");
+ pw.println(" If not specified, sessions in all users are removed.");
+ pw.println();
+ pw.println("clear-all-blobs [-u | --user USER_ID]");
+ pw.println(" Remove all blobs.");
+ pw.println(" Options:");
+ pw.println(" -u or --user: specify which user's blobs to be removed;");
+ pw.println(" If not specified, blobs in all users are removed.");
+ pw.println();
+ }
+
+ private int parseOptions(PrintWriter pw, ParsedArgs args) {
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-u":
+ case "--user":
+ args.userId = Integer.parseInt(getNextArgRequired());
+ break;
+ default:
+ pw.println("Error: unknown option '" + opt + "'");
+ return -1;
+ }
+ }
+ return 0;
+ }
+
+ private static class ParsedArgs {
+ public int userId;
+ }
+}
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 7d1c166..54a2997 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -47,9 +47,11 @@
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
+import com.android.server.blob.BlobStoreManagerService.DumpArgs;
import com.android.server.blob.BlobStoreManagerService.SessionStateChangeListener;
import org.xmlpull.v1.XmlPullParser;
@@ -62,9 +64,11 @@
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Objects;
/** TODO: add doc */
-public class BlobStoreSession extends IBlobStoreSession.Stub {
+@VisibleForTesting
+class BlobStoreSession extends IBlobStoreSession.Stub {
static final int STATE_OPENED = 1;
static final int STATE_CLOSED = 0;
@@ -78,10 +82,10 @@
private final Context mContext;
private final SessionStateChangeListener mListener;
- public final BlobHandle blobHandle;
- public final long sessionId;
- public final int ownerUid;
- public final String ownerPackageName;
+ private final BlobHandle mBlobHandle;
+ private final long mSessionId;
+ private final int mOwnerUid;
+ private final String mOwnerPackageName;
// Do not access this directly, instead use getSessionFile().
private File mSessionFile;
@@ -101,15 +105,31 @@
BlobStoreSession(Context context, long sessionId, BlobHandle blobHandle,
int ownerUid, String ownerPackageName, SessionStateChangeListener listener) {
this.mContext = context;
- this.blobHandle = blobHandle;
- this.sessionId = sessionId;
- this.ownerUid = ownerUid;
- this.ownerPackageName = ownerPackageName;
+ this.mBlobHandle = blobHandle;
+ this.mSessionId = sessionId;
+ this.mOwnerUid = ownerUid;
+ this.mOwnerPackageName = ownerPackageName;
this.mListener = listener;
}
+ public BlobHandle getBlobHandle() {
+ return mBlobHandle;
+ }
+
+ public long getSessionId() {
+ return mSessionId;
+ }
+
+ public int getOwnerUid() {
+ return mOwnerUid;
+ }
+
+ public String getOwnerPackageName() {
+ return mOwnerPackageName;
+ }
+
boolean hasAccess(int callingUid, String callingPackageName) {
- return ownerUid == callingUid && ownerPackageName.equals(callingPackageName);
+ return mOwnerUid == callingUid && mOwnerPackageName.equals(callingPackageName);
}
void open() {
@@ -155,6 +175,8 @@
@NonNull
public ParcelFileDescriptor openWrite(@BytesLong long offsetBytes,
@BytesLong long lengthBytes) {
+ Preconditions.checkArgumentNonnegative(offsetBytes, "offsetBytes must not be negative");
+
assertCallerIsOwner();
synchronized (mSessionLock) {
if (mState != STATE_OPENED) {
@@ -242,7 +264,7 @@
public void allowPackageAccess(@NonNull String packageName,
@NonNull byte[] certificate) {
assertCallerIsOwner();
- Preconditions.checkNotNull(packageName, "packageName must not be null");
+ Objects.requireNonNull(packageName, "packageName must not be null");
synchronized (mSessionLock) {
if (mState != STATE_OPENED) {
throw new IllegalStateException("Not allowed to change access type in state: "
@@ -280,7 +302,9 @@
public boolean isPackageAccessAllowed(@NonNull String packageName,
@NonNull byte[] certificate) {
assertCallerIsOwner();
- Preconditions.checkNotNull(packageName, "packageName must not be null");
+ Objects.requireNonNull(packageName, "packageName must not be null");
+ Preconditions.checkByteArrayNotEmpty(certificate, "certificate");
+
synchronized (mSessionLock) {
if (mState != STATE_OPENED) {
throw new IllegalStateException("Not allowed to get access type in state: "
@@ -357,12 +381,12 @@
void verifyBlobData() {
byte[] actualDigest = null;
try {
- actualDigest = FileUtils.digest(getSessionFile(), blobHandle.algorithm);
+ actualDigest = FileUtils.digest(getSessionFile(), mBlobHandle.algorithm);
} catch (IOException | NoSuchAlgorithmException e) {
Slog.e(TAG, "Error computing the digest", e);
}
synchronized (mSessionLock) {
- if (actualDigest != null && Arrays.equals(actualDigest, blobHandle.digest)) {
+ if (actualDigest != null && Arrays.equals(actualDigest, mBlobHandle.digest)) {
mState = STATE_VERIFIED_VALID;
// Commit callback will be sent once the data is persisted.
} else {
@@ -401,7 +425,7 @@
@Nullable
File getSessionFile() {
if (mSessionFile == null) {
- mSessionFile = BlobStoreConfig.prepareBlobFile(sessionId);
+ mSessionFile = BlobStoreConfig.prepareBlobFile(mSessionId);
}
return mSessionFile;
}
@@ -425,20 +449,20 @@
private void assertCallerIsOwner() {
final int callingUid = Binder.getCallingUid();
- if (callingUid != ownerUid) {
- throw new SecurityException(ownerUid + " is not the session owner");
+ if (callingUid != mOwnerUid) {
+ throw new SecurityException(mOwnerUid + " is not the session owner");
}
}
- void dump(IndentingPrintWriter fout) {
+ void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) {
synchronized (mSessionLock) {
fout.println("state: " + stateToString(mState));
- fout.println("ownerUid: " + ownerUid);
- fout.println("ownerPkg: " + ownerPackageName);
+ fout.println("ownerUid: " + mOwnerUid);
+ fout.println("ownerPkg: " + mOwnerPackageName);
fout.println("blobHandle:");
fout.increaseIndent();
- blobHandle.dump(fout);
+ mBlobHandle.dump(fout, dumpArgs.shouldDumpFull());
fout.decreaseIndent();
fout.println("accessMode:");
@@ -452,12 +476,12 @@
void writeToXml(@NonNull XmlSerializer out) throws IOException {
synchronized (mSessionLock) {
- XmlUtils.writeLongAttribute(out, ATTR_ID, sessionId);
- XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, ownerPackageName);
- XmlUtils.writeIntAttribute(out, ATTR_UID, ownerUid);
+ XmlUtils.writeLongAttribute(out, ATTR_ID, mSessionId);
+ XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, mOwnerPackageName);
+ XmlUtils.writeIntAttribute(out, ATTR_UID, mOwnerUid);
out.startTag(null, TAG_BLOB_HANDLE);
- blobHandle.writeToXml(out);
+ mBlobHandle.writeToXml(out);
out.endTag(null, TAG_BLOB_HANDLE);
out.startTag(null, TAG_ACCESS_MODE);
diff --git a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
index bdd1da7..b94928f 100644
--- a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
+++ b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
@@ -61,4 +61,11 @@
/** Tells StatsCompaionService to grab the uid map snapshot and send it to statsd. */
oneway void triggerUidSnapshot();
+
+ /**
+ * Ask StatsCompanionService if the given permission is allowed for a particular process
+ * and user ID. statsd is incapable of doing this check itself because checkCallingPermission
+ * is not currently supported by libbinder_ndk.
+ */
+ boolean checkPermission(String permission, int pid, int uid);
}
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index 1e92826..3e9a488 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -516,6 +516,11 @@
}
}
+ @Override // Binder call
+ public boolean checkPermission(String permission, int pid, int uid) {
+ StatsCompanion.enforceStatsCompanionPermission(mContext);
+ return mContext.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED;
+ }
// Statsd related code
diff --git a/api/current.txt b/api/current.txt
index 1306bfa..5ac58d0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2875,9 +2875,17 @@
method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.graphics.Bitmap>);
field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13
+ field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a
+ field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b
+ field public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28; // 0x1c
+ field public static final int GESTURE_2_FINGER_SWIPE_UP = 25; // 0x19
field public static final int GESTURE_2_FINGER_TRIPLE_TAP = 21; // 0x15
field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17
field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16
+ field public static final int GESTURE_3_FINGER_SWIPE_DOWN = 30; // 0x1e
+ field public static final int GESTURE_3_FINGER_SWIPE_LEFT = 31; // 0x1f
+ field public static final int GESTURE_3_FINGER_SWIPE_RIGHT = 32; // 0x20
+ field public static final int GESTURE_3_FINGER_SWIPE_UP = 29; // 0x1d
field public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24; // 0x18
field public static final int GESTURE_DOUBLE_TAP = 17; // 0x11
field public static final int GESTURE_DOUBLE_TAP_AND_HOLD = 18; // 0x12
@@ -8631,7 +8639,7 @@
field public static final String ACTION_ACL_CONNECTED = "android.bluetooth.device.action.ACL_CONNECTED";
field public static final String ACTION_ACL_DISCONNECTED = "android.bluetooth.device.action.ACL_DISCONNECTED";
field public static final String ACTION_ACL_DISCONNECT_REQUESTED = "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED";
- field public static final String ACTION_ALIAS_CHANGED = "android.bluetooth.action.ALIAS_CHANGED";
+ field public static final String ACTION_ALIAS_CHANGED = "android.bluetooth.device.action.ALIAS_CHANGED";
field public static final String ACTION_BOND_STATE_CHANGED = "android.bluetooth.device.action.BOND_STATE_CHANGED";
field public static final String ACTION_CLASS_CHANGED = "android.bluetooth.device.action.CLASS_CHANGED";
field public static final String ACTION_FOUND = "android.bluetooth.device.action.FOUND";
@@ -26729,6 +26737,7 @@
method public int getVolume();
method public int getVolumeHandling();
method public int getVolumeMax();
+ method public boolean isSystemRoute();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int CONNECTION_STATE_CONNECTED = 2; // 0x2
field public static final int CONNECTION_STATE_CONNECTING = 1; // 0x1
@@ -31129,11 +31138,6 @@
field @Deprecated public static final String[] strings;
}
- @Deprecated public static class WifiConfiguration.SuiteBCipher {
- field @Deprecated public static final int ECDHE_ECDSA = 0; // 0x0
- field @Deprecated public static final int ECDHE_RSA = 1; // 0x1
- }
-
public class WifiEnterpriseConfig implements android.os.Parcelable {
ctor public WifiEnterpriseConfig();
ctor public WifiEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
@@ -31616,6 +31620,7 @@
method public android.net.wifi.hotspot2.pps.Credential getCredential();
method public android.net.wifi.hotspot2.pps.HomeSp getHomeSp();
method public long getSubscriptionExpirationTimeInMillis();
+ method @NonNull public String getUniqueId() throws java.lang.IllegalStateException;
method public boolean isOsuProvisioned();
method public void setCredential(android.net.wifi.hotspot2.pps.Credential);
method public void setHomeSp(android.net.wifi.hotspot2.pps.HomeSp);
@@ -31796,8 +31801,8 @@
method public boolean isGroupOwner();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pGroup> CREATOR;
- field public static final int PERSISTENT_NET_ID = -2; // 0xfffffffe
- field public static final int TEMPORARY_NET_ID = -1; // 0xffffffff
+ field public static final int NETWORK_ID_PERSISTENT = -2; // 0xfffffffe
+ field public static final int NETWORK_ID_TEMPORARY = -1; // 0xffffffff
}
public class WifiP2pInfo implements android.os.Parcelable {
@@ -36944,7 +36949,7 @@
ctor public VibrationAttributes.Builder();
ctor public VibrationAttributes.Builder(@Nullable android.os.VibrationAttributes);
method @NonNull public android.os.VibrationAttributes build();
- method @NonNull public android.os.VibrationAttributes.Builder replaceFlags(int);
+ method @NonNull public android.os.VibrationAttributes.Builder setFlags(int, int);
method @NonNull public android.os.VibrationAttributes.Builder setUsage(int);
}
@@ -46381,7 +46386,6 @@
public final class BarringInfo implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.telephony.BarringInfo.BarringServiceInfo getBarringServiceInfo(int);
- method public boolean isServiceBarred(int);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int BARRING_SERVICE_TYPE_CS_FALLBACK = 5; // 0x5
field public static final int BARRING_SERVICE_TYPE_CS_SERVICE = 0; // 0x0
@@ -54811,7 +54815,6 @@
method public final void removeOnFrameMetricsAvailableListener(android.view.Window.OnFrameMetricsAvailableListener);
method public boolean requestFeature(int);
method @NonNull public final <T extends android.view.View> T requireViewById(@IdRes int);
- method public void resetOnContentApplyWindowInsetsListener();
method public abstract void restoreHierarchyState(android.os.Bundle);
method public abstract android.os.Bundle saveHierarchyState();
method public void setAllowEnterTransitionOverlap(boolean);
@@ -54829,6 +54832,7 @@
method public abstract void setContentView(android.view.View);
method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
method public abstract void setDecorCaptionShade(int);
+ method public void setDecorFitsSystemWindows(boolean);
method protected void setDefaultWindowFormat(int);
method public void setDimAmount(float);
method public void setElevation(float);
@@ -54850,7 +54854,6 @@
method public abstract void setNavigationBarColor(@ColorInt int);
method public void setNavigationBarContrastEnforced(boolean);
method public void setNavigationBarDividerColor(@ColorInt int);
- method public void setOnContentApplyWindowInsetsListener(@Nullable android.view.Window.OnContentApplyWindowInsetsListener);
method public void setPreferMinimalPostProcessing(boolean);
method public void setReenterTransition(android.transition.Transition);
method public abstract void setResizingCaptionDrawable(android.graphics.drawable.Drawable);
@@ -54945,10 +54948,6 @@
method @Nullable public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
}
- public static interface Window.OnContentApplyWindowInsetsListener {
- method @NonNull public android.util.Pair<android.graphics.Insets,android.view.WindowInsets> onContentApplyWindowInsets(@NonNull android.view.WindowInsets);
- }
-
public static interface Window.OnFrameMetricsAvailableListener {
method public void onFrameMetricsAvailable(android.view.Window, android.view.FrameMetrics, int);
}
@@ -55099,16 +55098,13 @@
}
public interface WindowInsetsController {
- method public default void controlInputMethodAnimation(long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
method public void controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
method public int getSystemBarsAppearance();
method public int getSystemBarsBehavior();
method public void hide(int);
- method public default void hideInputMethod();
method public void setSystemBarsAppearance(int, int);
method public void setSystemBarsBehavior(int);
method public void show(int);
- method public default void showInputMethod();
field public static final int APPEARANCE_LIGHT_NAVIGATION_BARS = 16; // 0x10
field public static final int APPEARANCE_LIGHT_STATUS_BARS = 8; // 0x8
field public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1; // 0x1
diff --git a/api/system-current.txt b/api/system-current.txt
index 0930f68..ed9f134 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1482,8 +1482,9 @@
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public long getDiscoveryEndMillis();
method public boolean isBleScanAlwaysAvailable();
method public boolean isLeEnabled();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeActiveDevice(int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setActiveDevice(@Nullable android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setActiveDevice(@NonNull android.bluetooth.BluetoothDevice, int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean setScanMode(int, int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean setScanMode(int);
field public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
@@ -1556,7 +1557,7 @@
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInSilenceMode();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean removeBond();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean setAlias(@NonNull String);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setAlias(@NonNull String);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMessageAccessPermission(int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, @NonNull byte[]);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int);
@@ -1604,7 +1605,7 @@
}
public final class BluetoothHidDevice implements android.bluetooth.BluetoothProfile {
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
}
public final class BluetoothHidHost implements android.bluetooth.BluetoothProfile {
@@ -4104,7 +4105,6 @@
public class Location implements android.os.Parcelable {
method public boolean isComplete();
method public void makeComplete();
- method public void setExtraLocation(@Nullable String, @Nullable android.location.Location);
method public void setIsFromMockProvider(boolean);
field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
}
@@ -6233,7 +6233,7 @@
}
public abstract class NetworkAgent {
- ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, int, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider);
+ ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkScore, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider);
method @Nullable public android.net.Network getNetwork();
method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
method public void onAutomaticReconnectDisabled();
@@ -6248,7 +6248,7 @@
method @NonNull public android.net.Network register();
method public void sendLinkProperties(@NonNull android.net.LinkProperties);
method public void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
- method public void sendNetworkScore(int);
+ method public void sendNetworkScore(@NonNull android.net.NetworkScore);
method public void sendSocketKeepaliveEvent(int, int);
method public void setConnected();
method @Deprecated public void setLegacyExtraInfo(@Nullable String);
@@ -6351,6 +6351,55 @@
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
}
+ public final class NetworkScore implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.net.NetworkScore.Metrics getEndToEndMetrics();
+ method @NonNull public android.net.NetworkScore.Metrics getLinkLayerMetrics();
+ method public int getRange();
+ method @IntRange(from=android.net.NetworkScore.UNKNOWN_SIGNAL_STRENGTH, to=android.net.NetworkScore.MAX_SIGNAL_STRENGTH) public int getSignalStrength();
+ method public boolean hasPolicy(int);
+ method public boolean isExiting();
+ method @NonNull public android.net.NetworkScore withExiting(boolean);
+ method @NonNull public android.net.NetworkScore withSignalStrength(@IntRange(from=android.net.NetworkScore.UNKNOWN_SIGNAL_STRENGTH) int);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkScore> CREATOR;
+ field public static final int MAX_SIGNAL_STRENGTH = 1000; // 0x3e8
+ field public static final int MIN_SIGNAL_STRENGTH = 0; // 0x0
+ field public static final int POLICY_DEFAULT_SUBSCRIPTION = 8; // 0x8
+ field public static final int POLICY_IGNORE_ON_WIFI = 4; // 0x4
+ field public static final int POLICY_LOCKDOWN_VPN = 1; // 0x1
+ field public static final int POLICY_VPN = 2; // 0x2
+ field public static final int RANGE_CLOSE = 1; // 0x1
+ field public static final int RANGE_LONG = 4; // 0x4
+ field public static final int RANGE_MEDIUM = 3; // 0x3
+ field public static final int RANGE_SHORT = 2; // 0x2
+ field public static final int RANGE_UNKNOWN = 0; // 0x0
+ field public static final int UNKNOWN_SIGNAL_STRENGTH = -1; // 0xffffffff
+ }
+
+ public static class NetworkScore.Builder {
+ ctor public NetworkScore.Builder();
+ method @NonNull public android.net.NetworkScore.Builder addPolicy(int);
+ method @NonNull public android.net.NetworkScore build();
+ method @NonNull public android.net.NetworkScore.Builder clearPolicy(int);
+ method @NonNull public android.net.NetworkScore.Builder setEndToEndMetrics(@NonNull android.net.NetworkScore.Metrics);
+ method @NonNull public android.net.NetworkScore.Builder setExiting(boolean);
+ method @NonNull public android.net.NetworkScore.Builder setLegacyScore(int);
+ method @NonNull public android.net.NetworkScore.Builder setLinkLayerMetrics(@NonNull android.net.NetworkScore.Metrics);
+ method @NonNull public android.net.NetworkScore.Builder setRange(int);
+ method @NonNull public android.net.NetworkScore.Builder setSignalStrength(@IntRange(from=android.net.NetworkScore.UNKNOWN_SIGNAL_STRENGTH, to=android.net.NetworkScore.MAX_SIGNAL_STRENGTH) int);
+ }
+
+ public static class NetworkScore.Metrics {
+ ctor public NetworkScore.Metrics(@IntRange(from=android.net.NetworkScore.Metrics.LATENCY_UNKNOWN) int, @IntRange(from=android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN) int, @IntRange(from=android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN) int);
+ field public static final int BANDWIDTH_UNKNOWN = -1; // 0xffffffff
+ field @NonNull public static final android.net.NetworkScore.Metrics EMPTY;
+ field public static final int LATENCY_UNKNOWN = -1; // 0xffffffff
+ field @IntRange(from=android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN) public final int downlinkBandwidthKBps;
+ field @IntRange(from=android.net.NetworkScore.Metrics.LATENCY_UNKNOWN) public final int latencyMs;
+ field @IntRange(from=android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN) public final int uplinkBandwidthKBps;
+ }
+
public class NetworkScoreManager {
method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public boolean clearScores() throws java.lang.SecurityException;
method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public void disableScoring() throws java.lang.SecurityException;
@@ -7465,6 +7514,7 @@
method @Deprecated @NonNull public android.net.wifi.WifiConfiguration.NetworkSelectionStatus getNetworkSelectionStatus();
method @Deprecated @NonNull public String getPrintableSsid();
method @Deprecated @NonNull public android.net.IpConfiguration.ProxySettings getProxySettings();
+ method @Deprecated public int getRecentFailureReason();
method @Deprecated @Nullable public android.net.StaticIpConfiguration getStaticIpConfiguration();
method @Deprecated public boolean hasNoInternetAccess();
method @Deprecated public boolean isEphemeral();
@@ -7482,6 +7532,8 @@
field @Deprecated public static final int METERED_OVERRIDE_NOT_METERED = 2; // 0x2
field @Deprecated public static final int RANDOMIZATION_NONE = 0; // 0x0
field @Deprecated public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1
+ field @Deprecated public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11
+ field @Deprecated public static final int RECENT_FAILURE_NONE = 0; // 0x0
field @Deprecated public boolean allowAutojoin;
field @Deprecated public int apBand;
field @Deprecated public int carrierId;
@@ -7497,7 +7549,6 @@
field @Deprecated public int numAssociation;
field @Deprecated public int numScorerOverride;
field @Deprecated public int numScorerOverrideAndSwitchedNetwork;
- field @Deprecated @NonNull public final android.net.wifi.WifiConfiguration.RecentFailure recentFailure;
field @Deprecated public boolean requirePMF;
field @Deprecated @Nullable public String saePasswordId;
field @Deprecated public boolean shared;
@@ -7543,12 +7594,6 @@
method @Deprecated @NonNull public android.net.wifi.WifiConfiguration.NetworkSelectionStatus.Builder setNetworkSelectionStatus(int);
}
- @Deprecated public static class WifiConfiguration.RecentFailure {
- method @Deprecated public int getAssociationStatus();
- field @Deprecated public static final int NONE = 0; // 0x0
- field @Deprecated public static final int STATUS_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11
- }
-
public class WifiEnterpriseConfig implements android.os.Parcelable {
method @Nullable public String[] getCaCertificateAliases();
method @NonNull public String getCaPath();
@@ -10062,6 +10107,7 @@
ctor public AugmentedAutofillService();
method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
method protected void dump(@NonNull java.io.PrintWriter, @NonNull String[]);
+ method @Nullable public final android.service.autofill.FillEventHistory getFillEventHistory();
method public void onConnected();
method public void onDisconnected();
method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback);
@@ -10091,6 +10137,7 @@
public static final class FillResponse.Builder {
ctor public FillResponse.Builder();
method @NonNull public android.service.autofill.augmented.FillResponse build();
+ method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@Nullable android.service.autofill.augmented.FillWindow);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@Nullable java.util.List<android.service.autofill.Dataset>);
}
diff --git a/api/test-current.txt b/api/test-current.txt
index e0b5b34..fda3ab4 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1271,7 +1271,6 @@
public class Location implements android.os.Parcelable {
method public void makeComplete();
- method public void setExtraLocation(@Nullable String, @Nullable android.location.Location);
field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
}
@@ -1387,6 +1386,10 @@
method public int getMaxMacroBlocks();
}
+ public final class MediaRoute2Info implements android.os.Parcelable {
+ method @NonNull public String getOriginalId();
+ }
+
public final class PlaybackParams implements android.os.Parcelable {
method public int getAudioStretchMode();
method public android.media.PlaybackParams setAudioStretchMode(int);
@@ -1688,6 +1691,55 @@
field public static final int TRANSPORT_TEST = 7; // 0x7
}
+ public final class NetworkScore implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.net.NetworkScore.Metrics getEndToEndMetrics();
+ method @NonNull public android.net.NetworkScore.Metrics getLinkLayerMetrics();
+ method public int getRange();
+ method @IntRange(from=android.net.NetworkScore.UNKNOWN_SIGNAL_STRENGTH, to=android.net.NetworkScore.MAX_SIGNAL_STRENGTH) public int getSignalStrength();
+ method public boolean hasPolicy(int);
+ method public boolean isExiting();
+ method @NonNull public android.net.NetworkScore withExiting(boolean);
+ method @NonNull public android.net.NetworkScore withSignalStrength(@IntRange(from=android.net.NetworkScore.UNKNOWN_SIGNAL_STRENGTH) int);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkScore> CREATOR;
+ field public static final int MAX_SIGNAL_STRENGTH = 1000; // 0x3e8
+ field public static final int MIN_SIGNAL_STRENGTH = 0; // 0x0
+ field public static final int POLICY_DEFAULT_SUBSCRIPTION = 8; // 0x8
+ field public static final int POLICY_IGNORE_ON_WIFI = 4; // 0x4
+ field public static final int POLICY_LOCKDOWN_VPN = 1; // 0x1
+ field public static final int POLICY_VPN = 2; // 0x2
+ field public static final int RANGE_CLOSE = 1; // 0x1
+ field public static final int RANGE_LONG = 4; // 0x4
+ field public static final int RANGE_MEDIUM = 3; // 0x3
+ field public static final int RANGE_SHORT = 2; // 0x2
+ field public static final int RANGE_UNKNOWN = 0; // 0x0
+ field public static final int UNKNOWN_SIGNAL_STRENGTH = -1; // 0xffffffff
+ }
+
+ public static class NetworkScore.Builder {
+ ctor public NetworkScore.Builder();
+ method @NonNull public android.net.NetworkScore.Builder addPolicy(int);
+ method @NonNull public android.net.NetworkScore build();
+ method @NonNull public android.net.NetworkScore.Builder clearPolicy(int);
+ method @NonNull public android.net.NetworkScore.Builder setEndToEndMetrics(@NonNull android.net.NetworkScore.Metrics);
+ method @NonNull public android.net.NetworkScore.Builder setExiting(boolean);
+ method @NonNull public android.net.NetworkScore.Builder setLegacyScore(int);
+ method @NonNull public android.net.NetworkScore.Builder setLinkLayerMetrics(@NonNull android.net.NetworkScore.Metrics);
+ method @NonNull public android.net.NetworkScore.Builder setRange(int);
+ method @NonNull public android.net.NetworkScore.Builder setSignalStrength(@IntRange(from=android.net.NetworkScore.UNKNOWN_SIGNAL_STRENGTH, to=android.net.NetworkScore.MAX_SIGNAL_STRENGTH) int);
+ }
+
+ public static class NetworkScore.Metrics {
+ ctor public NetworkScore.Metrics(@IntRange(from=android.net.NetworkScore.Metrics.LATENCY_UNKNOWN) int, @IntRange(from=android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN) int, @IntRange(from=android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN) int);
+ field public static final int BANDWIDTH_UNKNOWN = -1; // 0xffffffff
+ field @NonNull public static final android.net.NetworkScore.Metrics EMPTY;
+ field public static final int LATENCY_UNKNOWN = -1; // 0xffffffff
+ field @IntRange(from=android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN) public final int downlinkBandwidthKBps;
+ field @IntRange(from=android.net.NetworkScore.Metrics.LATENCY_UNKNOWN) public final int latencyMs;
+ field @IntRange(from=android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN) public final int uplinkBandwidthKBps;
+ }
+
public class NetworkStack {
field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK";
}
@@ -3052,6 +3104,7 @@
ctor public AugmentedAutofillService();
method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
method protected void dump(@NonNull java.io.PrintWriter, @NonNull String[]);
+ method @Nullable public final android.service.autofill.FillEventHistory getFillEventHistory();
method public void onConnected();
method public void onDisconnected();
method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback);
@@ -3081,6 +3134,7 @@
public static final class FillResponse.Builder {
ctor public FillResponse.Builder();
method @NonNull public android.service.autofill.augmented.FillResponse build();
+ method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@Nullable android.service.autofill.augmented.FillWindow);
method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@Nullable java.util.List<android.service.autofill.Dataset>);
}
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index fdc7fce..bd4397a 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -66,13 +66,11 @@
"src/config/ConfigManager.cpp",
"src/external/GpuStatsPuller.cpp",
"src/external/Perfetto.cpp",
- "src/external/PowerStatsPuller.cpp",
"src/external/PullResultReceiver.cpp",
"src/external/puller_util.cpp",
"src/external/StatsCallbackPuller.cpp",
"src/external/StatsPuller.cpp",
"src/external/StatsPullerManager.cpp",
- "src/external/SubsystemSleepStatePuller.cpp",
"src/external/TrainInfoPuller.cpp",
"src/FieldValue.cpp",
"src/guardrail/StatsdStats.cpp",
@@ -121,9 +119,6 @@
static_libs: [
"android.frameworks.stats@1.0",
- "android.hardware.power.stats@1.0",
- "android.hardware.power@1.0",
- "android.hardware.power@1.1",
"libbase",
"libcutils",
"liblog",
diff --git a/cmds/statsd/benchmark/log_event_benchmark.cpp b/cmds/statsd/benchmark/log_event_benchmark.cpp
index bdfdb2e..30dfe32 100644
--- a/cmds/statsd/benchmark/log_event_benchmark.cpp
+++ b/cmds/statsd/benchmark/log_event_benchmark.cpp
@@ -39,7 +39,7 @@
uint8_t msg[LOGGER_ENTRY_MAX_PAYLOAD];
size_t size = createAndParseStatsEvent(msg);
while (state.KeepRunning()) {
- benchmark::DoNotOptimize(LogEvent(msg, size, /*uid=*/ 1000));
+ benchmark::DoNotOptimize(LogEvent(msg, size, /*uid=*/ 1000, /*pid=*/ 1001));
}
}
BENCHMARK(BM_LogEventCreation);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index c1a8d69..05281f7 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -30,7 +30,6 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
#include <binder/PermissionController.h>
#include <cutils/multiuser.h>
#include <dirent.h>
@@ -77,6 +76,25 @@
return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
}
+
+static bool checkPermission(const char* permission) {
+ sp<IStatsCompanionService> scs = getStatsCompanionService();
+ if (scs == nullptr) {
+ return false;
+ }
+
+ bool success;
+ pid_t pid = IPCThreadState::self()->getCallingPid();
+ uid_t uid = IPCThreadState::self()->getCallingUid();
+
+ binder::Status status = scs->checkPermission(String16(permission), pid, uid, &success);
+ if (!status.isOk()) {
+ return false;
+ }
+ return success;
+}
+
+
binder::Status checkUid(uid_t expectedUid) {
uid_t uid = IPCThreadState::self()->getCallingUid();
if (uid == expectedUid || uid == AID_ROOT) {
@@ -97,11 +115,11 @@
}
// Caller must be granted these permissions
- if (!checkCallingPermission(String16(kPermissionDump))) {
+ if (!checkPermission(kPermissionDump)) {
return exception(binder::Status::EX_SECURITY,
StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, kPermissionDump));
}
- if (!checkCallingPermission(String16(kPermissionUsage))) {
+ if (!checkPermission(kPermissionUsage)) {
return exception(binder::Status::EX_SECURITY,
StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, kPermissionUsage));
}
@@ -285,7 +303,7 @@
* TODO: Come up with a more robust method of enacting <serviceutils/PriorityDumper.h>.
*/
status_t StatsService::dump(int fd, const Vector<String16>& args) {
- if (!checkCallingPermission(String16(kPermissionDump))) {
+ if (!checkPermission(kPermissionDump)) {
return PERMISSION_DENIED;
}
int lastArg = args.size() - 1;
@@ -914,7 +932,7 @@
IPCThreadState* ipc = IPCThreadState::self();
VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i",
ipc->getCallingPid(), ipc->getCallingUid());
- if (checkCallingPermission(String16(kPermissionDump))) {
+ if (checkPermission(kPermissionDump)) {
int cleared = mPullerManager->ForceClearPullerCache();
dprintf(out, "Puller removed %d cached data!\n", cleared);
return NO_ERROR;
@@ -927,7 +945,7 @@
IPCThreadState* ipc = IPCThreadState::self();
VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", ipc->getCallingPid(),
ipc->getCallingUid());
- if (checkCallingPermission(String16(kPermissionDump))) {
+ if (checkPermission(kPermissionDump)) {
bool enabled = true;
if (args.size() >= 2) {
enabled = atoi(args[1].c_str()) != 0;
@@ -1314,12 +1332,12 @@
// Root, system, and shell always have access
if (uid != AID_ROOT && uid != AID_SYSTEM && uid != AID_SHELL) {
// Caller must be granted these permissions
- if (!checkCallingPermission(String16(kPermissionDump))) {
+ if (!checkPermission(kPermissionDump)) {
return exception(binder::Status::EX_SECURITY,
StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
kPermissionDump));
}
- if (!checkCallingPermission(String16(kPermissionUsage))) {
+ if (!checkPermission(kPermissionUsage)) {
return exception(binder::Status::EX_SECURITY,
StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
kPermissionUsage));
@@ -1410,12 +1428,12 @@
// Root, system, and shell always have access
if (uid != AID_ROOT && uid != AID_SYSTEM && uid != AID_SHELL) {
// Caller must be granted these permissions
- if (!checkCallingPermission(String16(kPermissionDump))) {
+ if (!checkPermission(kPermissionDump)) {
return exception(binder::Status::EX_SECURITY,
StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
kPermissionDump));
}
- if (!checkCallingPermission(String16(kPermissionUsage))) {
+ if (!checkPermission(kPermissionUsage)) {
return exception(binder::Status::EX_SECURITY,
StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
kPermissionUsage));
diff --git a/cmds/statsd/src/external/PowerStatsPuller.cpp b/cmds/statsd/src/external/PowerStatsPuller.cpp
deleted file mode 100644
index dc69b78..0000000
--- a/cmds/statsd/src/external/PowerStatsPuller.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include <android/hardware/power/stats/1.0/IPowerStats.h>
-
-#include <vector>
-
-#include "PowerStatsPuller.h"
-#include "statslog.h"
-#include "stats_log_util.h"
-
-using android::hardware::hidl_vec;
-using android::hardware::power::stats::V1_0::IPowerStats;
-using android::hardware::power::stats::V1_0::EnergyData;
-using android::hardware::power::stats::V1_0::RailInfo;
-using android::hardware::power::stats::V1_0::Status;
-using android::hardware::Return;
-using android::hardware::Void;
-
-using std::make_shared;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHal = nullptr;
-static std::mutex gPowerStatsHalMutex;
-static bool gPowerStatsExist = true; // Initialized to ensure making a first attempt.
-static std::vector<RailInfo> gRailInfo;
-
-struct PowerStatsPullerDeathRecipient : virtual public hardware::hidl_death_recipient {
- virtual void serviceDied(uint64_t cookie,
- const wp<android::hidl::base::V1_0::IBase>& who) override {
- // The HAL just died. Reset all handles to HAL services.
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
- gPowerStatsHal = nullptr;
- }
-};
-
-static sp<PowerStatsPullerDeathRecipient> gDeathRecipient = new PowerStatsPullerDeathRecipient();
-
-static bool getPowerStatsHalLocked() {
- if (gPowerStatsHal == nullptr && gPowerStatsExist) {
- gPowerStatsHal = android::hardware::power::stats::V1_0::IPowerStats::getService();
- if (gPowerStatsHal == nullptr) {
- ALOGW("Couldn't load power.stats HAL service");
- gPowerStatsExist = false;
- } else {
- // Link death recipient to power.stats service handle
- hardware::Return<bool> linked = gPowerStatsHal->linkToDeath(gDeathRecipient, 0);
- if (!linked.isOk()) {
- ALOGE("Transaction error in linking to power.stats HAL death: %s",
- linked.description().c_str());
- gPowerStatsHal = nullptr;
- return false;
- } else if (!linked) {
- ALOGW("Unable to link to power.stats HAL death notifications");
- // We should still continue even though linking failed
- }
- }
- }
- return gPowerStatsHal != nullptr;
-}
-
-PowerStatsPuller::PowerStatsPuller() : StatsPuller(android::util::ON_DEVICE_POWER_MEASUREMENT) {
-}
-
-bool PowerStatsPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
- std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
- if (!getPowerStatsHalLocked()) {
- return false;
- }
-
- int64_t wallClockTimestampNs = getWallClockNs();
- int64_t elapsedTimestampNs = getElapsedRealtimeNs();
-
- data->clear();
-
- // Pull getRailInfo if necessary
- if (gRailInfo.empty()) {
- bool resultSuccess = true;
- Return<void> ret = gPowerStatsHal->getRailInfo(
- [&resultSuccess](const hidl_vec<RailInfo> &list, Status status) {
- resultSuccess = (status == Status::SUCCESS || status == Status::NOT_SUPPORTED);
- if (status != Status::SUCCESS) return;
-
- gRailInfo.reserve(list.size());
- for (size_t i = 0; i < list.size(); ++i) {
- gRailInfo.push_back(list[i]);
- }
- });
- if (!resultSuccess || !ret.isOk()) {
- ALOGE("power.stats getRailInfo() failed. Description: %s", ret.description().c_str());
- gPowerStatsHal = nullptr;
- return false;
- }
- // If SUCCESS but empty, or if NOT_SUPPORTED, then never try again.
- if (gRailInfo.empty()) {
- ALOGE("power.stats has no rail information");
- gPowerStatsExist = false; // No rail info, so never try again.
- gPowerStatsHal = nullptr;
- return false;
- }
- }
-
- // Pull getEnergyData and write the data out
- const hidl_vec<uint32_t> desiredRailIndices; // Empty vector indicates we want all.
- bool resultSuccess = true;
- Return<void> ret = gPowerStatsHal->getEnergyData(desiredRailIndices,
- [&data, wallClockTimestampNs, elapsedTimestampNs, &resultSuccess]
- (hidl_vec<EnergyData> energyDataList, Status status) {
- resultSuccess = (status == Status::SUCCESS);
- if (!resultSuccess) return;
-
- for (size_t i = 0; i < energyDataList.size(); i++) {
- const EnergyData& energyData = energyDataList[i];
-
- if (energyData.index >= gRailInfo.size()) {
- ALOGE("power.stats getEnergyData() returned an invalid rail index %u.",
- energyData.index);
- resultSuccess = false;
- return;
- }
- const RailInfo& rail = gRailInfo[energyData.index];
-
- auto ptr = make_shared<LogEvent>(android::util::ON_DEVICE_POWER_MEASUREMENT,
- wallClockTimestampNs, elapsedTimestampNs);
- ptr->write(rail.subsysName);
- ptr->write(rail.railName);
- ptr->write(energyData.timestamp);
- ptr->write(energyData.energy);
- ptr->init();
- data->push_back(ptr);
-
- VLOG("power.stat: %s.%s: %llu, %llu",
- rail.subsysName.c_str(),
- rail.railName.c_str(),
- (unsigned long long)energyData.timestamp,
- (unsigned long long)energyData.energy);
- }
- });
- if (!resultSuccess || !ret.isOk()) {
- ALOGE("power.stats getEnergyData() failed. Description: %s", ret.description().c_str());
- gPowerStatsHal = nullptr;
- return false;
- }
- return true;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
index 6257771..1a11f0e 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -68,7 +68,7 @@
for (const StatsEventParcel& parcel: output) {
shared_ptr<LogEvent> event = make_shared<LogEvent>(
const_cast<uint8_t*>(parcel.buffer.data()), parcel.buffer.size(),
- /*uid=*/-1, /*useNewSchema=*/true);
+ /*uid=*/-1, /*pid=*/-1, /*useNewSchema=*/true);
sharedData->push_back(event);
}
*pullSuccess = success;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 668c11e..fef213d 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -33,9 +33,7 @@
#include "../stats_log_util.h"
#include "../statscompanion_util.h"
#include "GpuStatsPuller.h"
-#include "PowerStatsPuller.h"
#include "StatsCallbackPuller.h"
-#include "SubsystemSleepStatePuller.h"
#include "TrainInfoPuller.h"
#include "statslog.h"
@@ -55,12 +53,6 @@
StatsPullerManager::StatsPullerManager()
: kAllPullAtomInfo({
- // subsystem_sleep_state
- {{.atomTag = android::util::SUBSYSTEM_SLEEP_STATE}, new SubsystemSleepStatePuller()},
-
- // on_device_power_measurement
- {{.atomTag = android::util::ON_DEVICE_POWER_MEASUREMENT}, new PowerStatsPuller()},
-
// TrainInfo.
{{.atomTag = android::util::TRAIN_INFO}, new TrainInfoPuller()},
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
deleted file mode 100644
index f6a4aea..0000000
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include <android/hardware/power/1.0/IPower.h>
-#include <android/hardware/power/1.1/IPower.h>
-#include <android/hardware/power/stats/1.0/IPowerStats.h>
-
-#include <fcntl.h>
-#include <hardware/power.h>
-#include <hardware_legacy/power.h>
-#include <inttypes.h>
-#include <semaphore.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include "external/SubsystemSleepStatePuller.h"
-#include "external/StatsPuller.h"
-
-#include "SubsystemSleepStatePuller.h"
-#include "logd/LogEvent.h"
-#include "statslog.h"
-#include "stats_log_util.h"
-
-using android::hardware::hidl_vec;
-using android::hardware::power::V1_0::IPower;
-using android::hardware::power::V1_0::PowerStatePlatformSleepState;
-using android::hardware::power::V1_0::PowerStateVoter;
-using android::hardware::power::V1_1::PowerStateSubsystem;
-using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
-using android::hardware::power::stats::V1_0::PowerEntityInfo;
-using android::hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
-using android::hardware::power::stats::V1_0::PowerEntityStateSpace;
-
-using android::hardware::Return;
-using android::hardware::Void;
-
-using std::make_shared;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static std::function<bool(vector<shared_ptr<LogEvent>>* data)> gPuller = {};
-
-static sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
-static sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr;
-static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr;
-
-static std::unordered_map<uint32_t, std::string> gEntityNames = {};
-static std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> gStateNames = {};
-
-static std::mutex gPowerHalMutex;
-
-// The caller must be holding gPowerHalMutex.
-static void deinitPowerStatsLocked() {
- gPowerHalV1_0 = nullptr;
- gPowerHalV1_1 = nullptr;
- gPowerStatsHalV1_0 = nullptr;
-}
-
-struct SubsystemSleepStatePullerDeathRecipient : virtual public hardware::hidl_death_recipient {
- virtual void serviceDied(uint64_t cookie,
- const wp<android::hidl::base::V1_0::IBase>& who) override {
-
- // The HAL just died. Reset all handles to HAL services.
- std::lock_guard<std::mutex> lock(gPowerHalMutex);
- deinitPowerStatsLocked();
- }
-};
-
-static sp<SubsystemSleepStatePullerDeathRecipient> gDeathRecipient =
- new SubsystemSleepStatePullerDeathRecipient();
-
-SubsystemSleepStatePuller::SubsystemSleepStatePuller() :
- StatsPuller(android::util::SUBSYSTEM_SLEEP_STATE) {
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool checkResultLocked(const Return<void> &ret, const char* function) {
- if (!ret.isOk()) {
- ALOGE("%s failed: requested HAL service not available. Description: %s",
- function, ret.description().c_str());
- if (ret.isDeadObject()) {
- deinitPowerStatsLocked();
- }
- return false;
- }
- return true;
-}
-
-// The caller must be holding gPowerHalMutex.
-// gPowerStatsHalV1_0 must not be null
-static bool initializePowerStats() {
- using android::hardware::power::stats::V1_0::Status;
-
- // Clear out previous content if we are re-initializing
- gEntityNames.clear();
- gStateNames.clear();
-
- Return<void> ret;
- ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) {
- if (status != Status::SUCCESS) {
- ALOGE("Error getting power entity info");
- return;
- }
-
- // construct lookup table of powerEntityId to power entity name
- for (auto info : infos) {
- gEntityNames.emplace(info.powerEntityId, info.powerEntityName);
- }
- });
- if (!checkResultLocked(ret, __func__)) {
- return false;
- }
-
- ret = gPowerStatsHalV1_0->getPowerEntityStateInfo({}, [](auto stateSpaces, auto status) {
- if (status != Status::SUCCESS) {
- ALOGE("Error getting state info");
- return;
- }
-
- // construct lookup table of powerEntityId, powerEntityStateId to power entity state name
- for (auto stateSpace : stateSpaces) {
- std::unordered_map<uint32_t, std::string> stateNames = {};
- for (auto state : stateSpace.states) {
- stateNames.emplace(state.powerEntityStateId,
- state.powerEntityStateName);
- }
- gStateNames.emplace(stateSpace.powerEntityId, stateNames);
- }
- });
- if (!checkResultLocked(ret, __func__)) {
- return false;
- }
-
- return (!gEntityNames.empty()) && (!gStateNames.empty());
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool getPowerStatsHalLocked() {
- if(gPowerStatsHalV1_0 == nullptr) {
- gPowerStatsHalV1_0 = android::hardware::power::stats::V1_0::IPowerStats::getService();
- if (gPowerStatsHalV1_0 == nullptr) {
- ALOGE("Unable to get power.stats HAL service.");
- return false;
- }
-
- // Link death recipient to power.stats service handle
- hardware::Return<bool> linked = gPowerStatsHalV1_0->linkToDeath(gDeathRecipient, 0);
- if (!linked.isOk()) {
- ALOGE("Transaction error in linking to power.stats HAL death: %s",
- linked.description().c_str());
- deinitPowerStatsLocked();
- return false;
- } else if (!linked) {
- ALOGW("Unable to link to power.stats HAL death notifications");
- // We should still continue even though linking failed
- }
- return initializePowerStats();
- }
- return true;
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool getIPowerStatsDataLocked(vector<shared_ptr<LogEvent>>* data) {
- using android::hardware::power::stats::V1_0::Status;
-
- if(!getPowerStatsHalLocked()) {
- return false;
- }
-
- int64_t wallClockTimestampNs = getWallClockNs();
- int64_t elapsedTimestampNs = getElapsedRealtimeNs();
-
- // Get power entity state residency data
- bool success = false;
- Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData({},
- [&data, &success, wallClockTimestampNs, elapsedTimestampNs]
- (auto results, auto status) {
- if (status == Status::NOT_SUPPORTED) {
- ALOGW("getPowerEntityStateResidencyData is not supported");
- success = false;
- return;
- }
-
- for(auto result : results) {
- for(auto stateResidency : result.stateResidencyData) {
- auto statePtr = make_shared<LogEvent>(
- android::util::SUBSYSTEM_SLEEP_STATE,
- wallClockTimestampNs, elapsedTimestampNs);
- statePtr->write(gEntityNames.at(result.powerEntityId));
- statePtr->write(gStateNames.at(result.powerEntityId)
- .at(stateResidency.powerEntityStateId));
- statePtr->write(stateResidency.totalStateEntryCount);
- statePtr->write(stateResidency.totalTimeInStateMs);
- statePtr->init();
- data->emplace_back(statePtr);
- }
- }
- success = true;
- });
- // Intentionally not returning early here.
- // bool success determines if this succeeded or not.
- checkResultLocked(ret, __func__);
-
- return success;
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool getPowerHalLocked() {
- if(gPowerHalV1_0 == nullptr) {
- gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
- if(gPowerHalV1_0 == nullptr) {
- ALOGE("Unable to get power HAL service.");
- return false;
- }
- gPowerHalV1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
-
- // Link death recipient to power service handle
- hardware::Return<bool> linked = gPowerHalV1_0->linkToDeath(gDeathRecipient, 0);
- if (!linked.isOk()) {
- ALOGE("Transaction error in linking to power HAL death: %s",
- linked.description().c_str());
- gPowerHalV1_0 = nullptr;
- return false;
- } else if (!linked) {
- ALOGW("Unable to link to power. death notifications");
- // We should still continue even though linking failed
- }
- }
- return true;
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool getIPowerDataLocked(vector<shared_ptr<LogEvent>>* data) {
- using android::hardware::power::V1_0::Status;
-
- if(!getPowerHalLocked()) {
- return false;
- }
-
- int64_t wallClockTimestampNs = getWallClockNs();
- int64_t elapsedTimestampNs = getElapsedRealtimeNs();
- Return<void> ret;
- ret = gPowerHalV1_0->getPlatformLowPowerStats(
- [&data, wallClockTimestampNs, elapsedTimestampNs]
- (hidl_vec<PowerStatePlatformSleepState> states, Status status) {
- if (status != Status::SUCCESS) return;
-
- for (size_t i = 0; i < states.size(); i++) {
- const PowerStatePlatformSleepState& state = states[i];
-
- auto statePtr = make_shared<LogEvent>(
- android::util::SUBSYSTEM_SLEEP_STATE,
- wallClockTimestampNs, elapsedTimestampNs);
- statePtr->write(state.name);
- statePtr->write("");
- statePtr->write(state.totalTransitions);
- statePtr->write(state.residencyInMsecSinceBoot);
- statePtr->init();
- data->push_back(statePtr);
- VLOG("powerstate: %s, %lld, %lld, %d", state.name.c_str(),
- (long long)state.residencyInMsecSinceBoot,
- (long long)state.totalTransitions,
- state.supportedOnlyInSuspend ? 1 : 0);
- for (const auto& voter : state.voters) {
- auto voterPtr = make_shared<LogEvent>(
- android::util::SUBSYSTEM_SLEEP_STATE,
- wallClockTimestampNs, elapsedTimestampNs);
- voterPtr->write(state.name);
- voterPtr->write(voter.name);
- voterPtr->write(voter.totalNumberOfTimesVotedSinceBoot);
- voterPtr->write(voter.totalTimeInMsecVotedForSinceBoot);
- voterPtr->init();
- data->push_back(voterPtr);
- VLOG("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(),
- voter.name.c_str(),
- (long long)voter.totalTimeInMsecVotedForSinceBoot,
- (long long)voter.totalNumberOfTimesVotedSinceBoot);
- }
- }
- });
- if (!checkResultLocked(ret, __func__)) {
- return false;
- }
-
- // Trying to cast to IPower 1.1, this will succeed only for devices supporting 1.1
- sp<android::hardware::power::V1_1::IPower> gPowerHal_1_1 =
- android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
- if (gPowerHal_1_1 != nullptr) {
- ret = gPowerHal_1_1->getSubsystemLowPowerStats(
- [&data, wallClockTimestampNs, elapsedTimestampNs]
- (hidl_vec<PowerStateSubsystem> subsystems, Status status) {
- if (status != Status::SUCCESS) return;
-
- if (subsystems.size() > 0) {
- for (size_t i = 0; i < subsystems.size(); i++) {
- const PowerStateSubsystem& subsystem = subsystems[i];
- for (size_t j = 0; j < subsystem.states.size(); j++) {
- const PowerStateSubsystemSleepState& state =
- subsystem.states[j];
- auto subsystemStatePtr = make_shared<LogEvent>(
- android::util::SUBSYSTEM_SLEEP_STATE,
- wallClockTimestampNs, elapsedTimestampNs);
- subsystemStatePtr->write(subsystem.name);
- subsystemStatePtr->write(state.name);
- subsystemStatePtr->write(state.totalTransitions);
- subsystemStatePtr->write(state.residencyInMsecSinceBoot);
- subsystemStatePtr->init();
- data->push_back(subsystemStatePtr);
- VLOG("subsystemstate: %s, %s, %lld, %lld, %lld",
- subsystem.name.c_str(), state.name.c_str(),
- (long long)state.residencyInMsecSinceBoot,
- (long long)state.totalTransitions,
- (long long)state.lastEntryTimestampMs);
- }
- }
- }
- });
- }
- return true;
-}
-
-// The caller must be holding gPowerHalMutex.
-std::function<bool(vector<shared_ptr<LogEvent>>* data)> getPullerLocked() {
- std::function<bool(vector<shared_ptr<LogEvent>>* data)> ret = {};
-
- // First see if power.stats HAL is available. Fall back to power HAL if
- // power.stats HAL is unavailable.
- if(android::hardware::power::stats::V1_0::IPowerStats::getService() != nullptr) {
- ALOGI("Using power.stats HAL");
- ret = getIPowerStatsDataLocked;
- } else if(android::hardware::power::V1_0::IPower::getService() != nullptr) {
- ALOGI("Using power HAL");
- ret = getIPowerDataLocked;
- }
-
- return ret;
-}
-
-bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
- std::lock_guard<std::mutex> lock(gPowerHalMutex);
-
- if(!gPuller) {
- gPuller = getPullerLocked();
- }
-
- if(gPuller) {
- return gPuller(data);
- }
-
- ALOGE("Unable to load Power Hal or power.stats HAL");
- return false;
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 3827b9e..9a0693a 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -37,11 +37,12 @@
// Msg is expected to begin at the start of the serialized atom -- it should not
// include the android_log_header_t or the StatsEventTag.
-LogEvent::LogEvent(uint8_t* msg, uint32_t len, uint32_t uid)
+LogEvent::LogEvent(uint8_t* msg, uint32_t len, int32_t uid, int32_t pid)
: mBuf(msg),
mRemainingLen(len),
mLogdTimestampNs(time(nullptr)),
- mLogUid(uid)
+ mLogUid(uid),
+ mLogPid(pid)
{
#ifdef NEW_ENCODING_SCHEME
initNew();
@@ -52,8 +53,13 @@
#endif
}
-LogEvent::LogEvent(uint8_t* msg, uint32_t len, uint32_t uid, bool useNewSchema)
- : mBuf(msg), mRemainingLen(len), mLogdTimestampNs(time(nullptr)), mLogUid(uid) {
+LogEvent::LogEvent(uint8_t* msg, uint32_t len, int32_t uid, int32_t pid, bool useNewSchema)
+ : mBuf(msg),
+ mRemainingLen(len),
+ mLogdTimestampNs(time(nullptr)),
+ mLogUid(uid),
+ mLogPid(pid)
+{
if (useNewSchema) {
initNew();
} else {
@@ -66,6 +72,7 @@
LogEvent::LogEvent(const LogEvent& event) {
mTagId = event.mTagId;
mLogUid = event.mLogUid;
+ mLogPid = event.mLogPid;
mElapsedTimestampNs = event.mElapsedTimestampNs;
mLogdTimestampNs = event.mLogdTimestampNs;
mValues = event.mValues;
@@ -146,6 +153,7 @@
mElapsedTimestampNs = getElapsedRealtimeNs();
mTagId = android::util::BINARY_PUSH_STATE_CHANGED;
mLogUid = android::IPCThreadState::self()->getCallingUid();
+ mLogPid = android::IPCThreadState::self()->getCallingPid();
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName)));
mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode)));
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 463a1b6..583dae2 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -71,12 +71,12 @@
/**
* Read a LogEvent from the socket
*/
- explicit LogEvent(uint8_t* msg, uint32_t len, uint32_t uid);
+ explicit LogEvent(uint8_t* msg, uint32_t len, int32_t uid, int32_t pid);
/**
* Temp constructor to use for pulled atoms until we flip the socket schema.
*/
- explicit LogEvent(uint8_t* msg, uint32_t len, uint32_t uid, bool useNewSchema);
+ explicit LogEvent(uint8_t* msg, uint32_t len, int32_t uid, int32_t pid, bool useNewSchema);
/**
* Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
@@ -123,9 +123,17 @@
*/
inline int GetTagId() const { return mTagId; }
- inline uint32_t GetUid() const {
- return mLogUid;
- }
+ /**
+ * Get the uid of the logging client.
+ * Returns -1 if the uid is unknown/has not been set.
+ */
+ inline int32_t GetUid() const { return mLogUid; }
+
+ /**
+ * Get the pid of the logging client.
+ * Returns -1 if the pid is unknown/has not been set.
+ */
+ inline int32_t GetPid() const { return mLogPid; }
/**
* Get the nth value, starting at 1.
@@ -305,9 +313,14 @@
// The elapsed timestamp set by statsd log writer.
int64_t mElapsedTimestampNs;
+ // The atom tag of the event.
int mTagId;
- uint32_t mLogUid;
+ // The uid of the logging client (defaults to -1).
+ int32_t mLogUid = -1;
+
+ // The pid of the logging client (defaults to -1).
+ int32_t mLogPid = -1;
};
void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::vector<uint8_t>* protoOut);
diff --git a/cmds/statsd/src/socket/StatsSocketListener.cpp b/cmds/statsd/src/socket/StatsSocketListener.cpp
index 4308a110..cdb4d3a 100755
--- a/cmds/statsd/src/socket/StatsSocketListener.cpp
+++ b/cmds/statsd/src/socket/StatsSocketListener.cpp
@@ -126,9 +126,10 @@
uint8_t* msg = ptr + sizeof(uint32_t);
uint32_t len = n - sizeof(uint32_t);
uint32_t uid = cred->uid;
+ uint32_t pid = cred->pid;
int64_t oldestTimestamp;
- if (!mQueue->push(std::make_unique<LogEvent>(msg, len, uid), &oldestTimestamp)) {
+ if (!mQueue->push(std::make_unique<LogEvent>(msg, len, uid, pid), &oldestTimestamp)) {
StatsdStats::getInstance().noteEventQueueOverflow(oldestTimestamp);
}
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 1cf9fb6..35b0396 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -57,9 +57,11 @@
size_t size;
uint8_t* buf = stats_event_get_buffer(event, &size);
- LogEvent logEvent(buf, size, /*uid=*/ 1000);
+ LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
EXPECT_TRUE(logEvent.isValid());
EXPECT_EQ(100, logEvent.GetTagId());
+ EXPECT_EQ(1000, logEvent.GetUid());
+ EXPECT_EQ(1001, logEvent.GetPid());
const vector<FieldValue>& values = logEvent.getValues();
EXPECT_EQ(4, values.size());
@@ -103,9 +105,11 @@
size_t size;
uint8_t* buf = stats_event_get_buffer(event, &size);
- LogEvent logEvent(buf, size, /*uid=*/ 1000);
+ LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
EXPECT_TRUE(logEvent.isValid());
EXPECT_EQ(100, logEvent.GetTagId());
+ EXPECT_EQ(1000, logEvent.GetUid());
+ EXPECT_EQ(1001, logEvent.GetPid());
const vector<FieldValue>& values = logEvent.getValues();
EXPECT_EQ(2, values.size());
@@ -136,9 +140,11 @@
size_t size;
uint8_t* buf = stats_event_get_buffer(event, &size);
- LogEvent logEvent(buf, size, /*uid=*/ 1000);
+ LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
EXPECT_TRUE(logEvent.isValid());
EXPECT_EQ(100, logEvent.GetTagId());
+ EXPECT_EQ(1000, logEvent.GetUid());
+ EXPECT_EQ(1001, logEvent.GetPid());
const vector<FieldValue>& values = logEvent.getValues();
EXPECT_EQ(1, values.size());
@@ -162,9 +168,11 @@
size_t size;
uint8_t* buf = stats_event_get_buffer(event, &size);
- LogEvent logEvent(buf, size, /*uid=*/ 1000);
+ LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
EXPECT_TRUE(logEvent.isValid());
EXPECT_EQ(100, logEvent.GetTagId());
+ EXPECT_EQ(1000, logEvent.GetUid());
+ EXPECT_EQ(1001, logEvent.GetPid());
const vector<FieldValue>& values = logEvent.getValues();
EXPECT_EQ(1, values.size());
@@ -196,9 +204,11 @@
size_t size;
uint8_t* buf = stats_event_get_buffer(event, &size);
- LogEvent logEvent(buf, size, /*uid=*/ 1000);
+ LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
EXPECT_TRUE(logEvent.isValid());
EXPECT_EQ(100, logEvent.GetTagId());
+ EXPECT_EQ(1000, logEvent.GetUid());
+ EXPECT_EQ(1001, logEvent.GetPid());
const vector<FieldValue>& values = logEvent.getValues();
EXPECT_EQ(8, values.size()); // 2 FieldValues per key-value pair
@@ -260,9 +270,11 @@
size_t size;
uint8_t* buf = stats_event_get_buffer(event, &size);
- LogEvent logEvent(buf, size, /*uid=*/ 1000);
+ LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
EXPECT_TRUE(logEvent.isValid());
EXPECT_EQ(100, logEvent.GetTagId());
+ EXPECT_EQ(1000, logEvent.GetUid());
+ EXPECT_EQ(1001, logEvent.GetPid());
const vector<FieldValue>& values = logEvent.getValues();
EXPECT_EQ(4, values.size()); // 2 per attribution node
diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index 47fc7e1..9cf1de9 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -19,9 +19,17 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD;
@@ -89,7 +97,15 @@
GESTURE_SWIPE_RIGHT,
GESTURE_SWIPE_RIGHT_AND_UP,
GESTURE_SWIPE_RIGHT_AND_LEFT,
- GESTURE_SWIPE_RIGHT_AND_DOWN
+ GESTURE_SWIPE_RIGHT_AND_DOWN,
+ GESTURE_2_FINGER_SWIPE_DOWN,
+ GESTURE_2_FINGER_SWIPE_LEFT,
+ GESTURE_2_FINGER_SWIPE_RIGHT,
+ GESTURE_2_FINGER_SWIPE_UP,
+ GESTURE_3_FINGER_SWIPE_DOWN,
+ GESTURE_3_FINGER_SWIPE_LEFT,
+ GESTURE_3_FINGER_SWIPE_RIGHT,
+ GESTURE_3_FINGER_SWIPE_UP
})
@Retention(RetentionPolicy.SOURCE)
public @interface GestureId {}
@@ -167,6 +183,14 @@
case GESTURE_SWIPE_UP_AND_LEFT: return "GESTURE_SWIPE_UP_AND_LEFT";
case GESTURE_SWIPE_UP_AND_DOWN: return "GESTURE_SWIPE_UP_AND_DOWN";
case GESTURE_SWIPE_UP_AND_RIGHT: return "GESTURE_SWIPE_UP_AND_RIGHT";
+ case GESTURE_2_FINGER_SWIPE_DOWN: return "GESTURE_2_FINGER_SWIPE_DOWN";
+ case GESTURE_2_FINGER_SWIPE_LEFT: return "GESTURE_2_FINGER_SWIPE_LEFT";
+ case GESTURE_2_FINGER_SWIPE_RIGHT: return "GESTURE_2_FINGER_SWIPE_RIGHT";
+ case GESTURE_2_FINGER_SWIPE_UP: return "GESTURE_2_FINGER_SWIPE_UP";
+ case GESTURE_3_FINGER_SWIPE_DOWN: return "GESTURE_3_FINGER_SWIPE_DOWN";
+ case GESTURE_3_FINGER_SWIPE_LEFT: return "GESTURE_3_FINGER_SWIPE_LEFT";
+ case GESTURE_3_FINGER_SWIPE_RIGHT: return "GESTURE_3_FINGER_SWIPE_RIGHT";
+ case GESTURE_3_FINGER_SWIPE_UP: return "GESTURE_3_FINGER_SWIPE_UP";
default: return Integer.toHexString(eventType);
}
}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 27cd285..2165fb3 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -349,6 +349,46 @@
public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24;
/**
+ * The user has performed a two-finger swipe up gesture on the touch screen.
+ */
+ public static final int GESTURE_2_FINGER_SWIPE_UP = 25;
+
+ /**
+ * The user has performed a two-finger swipe down gesture on the touch screen.
+ */
+ public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26;
+
+ /**
+ * The user has performed a two-finger swipe left gesture on the touch screen.
+ */
+ public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27;
+
+ /**
+ * The user has performed a two-finger swipe right gesture on the touch screen.
+ */
+ public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28;
+
+ /**
+ * The user has performed a three-finger swipe up gesture on the touch screen.
+ */
+ public static final int GESTURE_3_FINGER_SWIPE_UP = 29;
+
+ /**
+ * The user has performed a three-finger swipe down gesture on the touch screen.
+ */
+ public static final int GESTURE_3_FINGER_SWIPE_DOWN = 30;
+
+ /**
+ * The user has performed a three-finger swipe left gesture on the touch screen.
+ */
+ public static final int GESTURE_3_FINGER_SWIPE_LEFT = 31;
+
+ /**
+ * The user has performed a three-finger swipe right gesture on the touch screen.
+ */
+ public static final int GESTURE_3_FINGER_SWIPE_RIGHT = 32;
+
+ /**
* The {@link Intent} that must be declared as handled by the service.
*/
public static final String SERVICE_INTERFACE =
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index dd9a2bc..58bff7f 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -158,6 +158,24 @@
}
};
+ /** @hide */
+ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+ public static ITaskOrganizerController getTaskOrganizerController() {
+ return ITaskOrganizerControllerSingleton.get();
+ }
+
+ private static final Singleton<ITaskOrganizerController> ITaskOrganizerControllerSingleton =
+ new Singleton<ITaskOrganizerController>() {
+ @Override
+ protected ITaskOrganizerController create() {
+ try {
+ return getService().getTaskOrganizerController();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+ };
+
/**
* Sets the windowing mode for a specific task. Only works on tasks of type
* {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index c09aa1f..71cb4a4 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -618,36 +618,22 @@
return hasSystemFeature(name, 0);
}
- private boolean hasSystemFeatureUncached(String name, int version) {
- try {
- return mPM.hasSystemFeature(name, version);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- // Make this cache relatively large. There are many system features and
- // none are ever invalidated. MPTS tests suggests that the cache should
- // hold at least 150 entries.
- private static final int SYS_FEATURE_CACHE_SIZE = 256;
- private static final String CACHE_KEY_SYS_FEATURE_PROPERTY = "cache_key.has_system_feature";
-
- private class SystemFeatureQuery {
+ private class HasSystemFeatureQuery {
public final String name;
public final int version;
- public SystemFeatureQuery(String n, int v) {
+ public HasSystemFeatureQuery(String n, int v) {
name = n;
version = v;
}
@Override
public String toString() {
- return String.format("SystemFeatureQuery(name=\"%s\", version=%d)",
+ return String.format("HasSystemFeatureQuery(name=\"%s\", version=%d)",
name, version);
}
@Override
public boolean equals(Object o) {
- if (o instanceof SystemFeatureQuery) {
- SystemFeatureQuery r = (SystemFeatureQuery) o;
+ if (o instanceof HasSystemFeatureQuery) {
+ HasSystemFeatureQuery r = (HasSystemFeatureQuery) o;
return Objects.equals(name, r.name) && version == r.version;
} else {
return false;
@@ -655,33 +641,41 @@
}
@Override
public int hashCode() {
- return Objects.hashCode(name) + version;
+ return Objects.hashCode(name) * 13 + version;
}
}
- private final PropertyInvalidatedCache<SystemFeatureQuery, Boolean> mSysFeatureCache =
- new PropertyInvalidatedCache<SystemFeatureQuery, Boolean>(
- SYS_FEATURE_CACHE_SIZE,
- CACHE_KEY_SYS_FEATURE_PROPERTY) {
+ // Make this cache relatively large. There are many system features and
+ // none are ever invalidated. MPTS tests suggests that the cache should
+ // hold at least 150 entries.
+ private final static PropertyInvalidatedCache<HasSystemFeatureQuery, Boolean>
+ mHasSystemFeatureCache =
+ new PropertyInvalidatedCache<HasSystemFeatureQuery, Boolean>(
+ 256, "cache_key.has_system_feature") {
@Override
- protected Boolean recompute(SystemFeatureQuery query) {
- return hasSystemFeatureUncached(query.name, query.version);
+ protected Boolean recompute(HasSystemFeatureQuery query) {
+ try {
+ return ActivityThread.currentActivityThread().getPackageManager().
+ hasSystemFeature(query.name, query.version);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
};
@Override
public boolean hasSystemFeature(String name, int version) {
- return mSysFeatureCache.query(new SystemFeatureQuery(name, version)).booleanValue();
+ return mHasSystemFeatureCache.query(new HasSystemFeatureQuery(name, version));
}
/** @hide */
- public void disableSysFeatureCache() {
- mSysFeatureCache.disableLocal();
+ public void disableHasSystemFeatureCache() {
+ mHasSystemFeatureCache.disableLocal();
}
/** @hide */
- public static void invalidateSysFeatureCache() {
- PropertyInvalidatedCache.invalidateCache(CACHE_KEY_SYS_FEATURE_PROPERTY);
+ public static void invalidateHasSystemFeatureCache() {
+ mHasSystemFeatureCache.invalidateCache();
}
@Override
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 85fa7c1..503f5c5 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -29,6 +29,7 @@
import android.app.IRequestFinishCallback;
import android.app.IServiceConnection;
import android.app.IStopUserCallback;
+import android.app.ITaskOrganizerController;
import android.app.ITaskStackListener;
import android.app.IUiAutomationConnection;
import android.app.IUidObserver;
@@ -71,7 +72,6 @@
import android.view.ITaskOrganizer;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationAdapter;
-import android.view.WindowContainerTransaction;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
@@ -123,8 +123,6 @@
int requestCode, int flags, in ProfilerInfo profilerInfo, in Bundle options,
IBinder permissionToken, boolean ignoreTargetSecurity, int userId);
- void registerTaskOrganizer(in ITaskOrganizer organizer, int windowingMode);
-
boolean isActivityStartAllowedOnDisplay(int displayId, in Intent intent, in String resolvedType,
int userId);
@@ -224,7 +222,6 @@
void setTaskResizeable(int taskId, int resizeableMode);
void toggleFreeformWindowingMode(in IBinder token);
void resizeTask(int taskId, in Rect bounds, int resizeMode);
- void applyContainerTransaction(in WindowContainerTransaction t);
void moveStackToDisplay(int stackId, int displayId);
void removeStack(int stackId);
@@ -364,6 +361,11 @@
in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds);
/**
+ * Returns an interface enabling the management of task organizers.
+ */
+ ITaskOrganizerController getTaskOrganizerController();
+
+ /**
* Sets whether we are currently in an interactive split screen resize operation where we
* are changing the docked stack size.
*/
diff --git a/core/java/android/app/ITaskOrganizerController.aidl b/core/java/android/app/ITaskOrganizerController.aidl
new file mode 100644
index 0000000..168f782
--- /dev/null
+++ b/core/java/android/app/ITaskOrganizerController.aidl
@@ -0,0 +1,51 @@
+/**
+ * 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.app;
+
+import android.app.ActivityManager;
+import android.view.ITaskOrganizer;
+import android.view.IWindowContainer;
+import android.view.WindowContainerTransaction;
+
+/** @hide */
+interface ITaskOrganizerController {
+
+ /**
+ * Register a TaskOrganizer to manage tasks as they enter the given windowing mode.
+ * If there was already a TaskOrganizer for this windowing mode it will be evicted
+ * and receive taskVanished callbacks in the process.
+ */
+ void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode);
+
+ /** Apply multiple WindowContainer operations at once. */
+ void applyContainerTransaction(in WindowContainerTransaction t);
+
+ /** Creates a persistent root task in WM for a particular windowing-mode. */
+ ActivityManager.RunningTaskInfo createRootTask(int displayId, int windowingMode);
+
+ /** Deletes a persistent root task in WM */
+ boolean deleteRootTask(IWindowContainer task);
+
+ /** Get the root task which contains the current ime target */
+ IWindowContainer getImeTarget(int display);
+
+ /**
+ * Set's the root task to launch new tasks into on a display. {@code null} means no launch root
+ * and thus new tasks just end up directly on the display.
+ */
+ void setLaunchRoot(int displayId, in IWindowContainer root);
+}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 59c17192..65a5f6b 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -365,11 +365,9 @@
registerService(Context.TETHERING_SERVICE, TetheringManager.class,
new CachedServiceFetcher<TetheringManager>() {
@Override
- public TetheringManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getService(Context.TETHERING_SERVICE);
- if (b == null) return null;
-
- return new TetheringManager(ctx, b);
+ public TetheringManager createService(ContextImpl ctx) {
+ return new TetheringManager(
+ ctx, () -> ServiceManager.getService(Context.TETHERING_SERVICE));
}});
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index fe9c640..662ca6e 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -16,6 +16,8 @@
package android.app;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -25,6 +27,7 @@
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
+import android.view.IWindowContainer;
/**
* Stores information about a particular Task.
@@ -138,6 +141,19 @@
@UnsupportedAppUsage
public final Configuration configuration = new Configuration();
+ /**
+ * Used as an opaque identifier for this task.
+ * @hide
+ */
+ @NonNull
+ public IWindowContainer token;
+
+ /**
+ * The activity type of the top activity in this task.
+ * @hide
+ */
+ public @WindowConfiguration.ActivityType int topActivityType;
+
TaskInfo() {
// Do nothing
}
@@ -160,6 +176,11 @@
}
}
+ /** @hide */
+ public boolean isResizable() {
+ return resizeMode != RESIZE_MODE_UNRESIZEABLE;
+ }
+
/**
* Reads the TaskInfo from a parcel.
*/
@@ -186,6 +207,8 @@
supportsSplitScreenMultiWindow = source.readBoolean();
resizeMode = source.readInt();
configuration.readFromParcel(source);
+ token = IWindowContainer.Stub.asInterface(source.readStrongBinder());
+ topActivityType = source.readInt();
}
/**
@@ -221,6 +244,8 @@
dest.writeBoolean(supportsSplitScreenMultiWindow);
dest.writeInt(resizeMode);
configuration.writeToParcel(dest, flags);
+ dest.writeStrongInterface(token);
+ dest.writeInt(topActivityType);
}
@Override
@@ -234,6 +259,8 @@
+ " numActivities=" + numActivities
+ " lastActiveTime=" + lastActiveTime
+ " supportsSplitScreenMultiWindow=" + supportsSplitScreenMultiWindow
- + " resizeMode=" + resizeMode;
+ + " resizeMode=" + resizeMode
+ + " token=" + token
+ + " topActivityType=" + topActivityType;
}
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index e24b040..b73d311 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1766,6 +1766,45 @@
}
/**
+ * Removes the active device for the grouping of @ActiveDeviceUse specified
+ *
+ * @param profiles represents the purpose for which we are setting this as the active device.
+ * Possible values are:
+ * {@link BluetoothAdapter#ACTIVE_DEVICE_AUDIO},
+ * {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL},
+ * {@link BluetoothAdapter#ACTIVE_DEVICE_ALL}
+ * @return false on immediate error, true otherwise
+ * @throws IllegalArgumentException if device is null or profiles is not one of
+ * {@link ActiveDeviceUse}
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean removeActiveDevice(@ActiveDeviceUse int profiles) {
+ if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL
+ && profiles != ACTIVE_DEVICE_ALL) {
+ Log.e(TAG, "Invalid profiles param value in removeActiveDevice");
+ throw new IllegalArgumentException("Profiles must be one of "
+ + "BluetoothAdapter.ACTIVE_DEVICE_AUDIO, "
+ + "BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL, or "
+ + "BluetoothAdapter.ACTIVE_DEVICE_ALL");
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ return mService.removeActiveDevice(profiles);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+
+ return false;
+ }
+
+ /**
+ * Sets device as the active devices for the profiles passed into the function
*
* @param device is the remote bluetooth device
* @param profiles represents the purpose for which we are setting this as the active device.
@@ -1774,18 +1813,26 @@
* {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL},
* {@link BluetoothAdapter#ACTIVE_DEVICE_ALL}
* @return false on immediate error, true otherwise
+ * @throws IllegalArgumentException if device is null or profiles is not one of
+ * {@link ActiveDeviceUse}
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setActiveDevice(@Nullable BluetoothDevice device,
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean setActiveDevice(@NonNull BluetoothDevice device,
@ActiveDeviceUse int profiles) {
+ if (device == null) {
+ Log.e(TAG, "setActiveDevice: Null device passed as parameter");
+ throw new IllegalArgumentException("device cannot be null");
+ }
if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL
&& profiles != ACTIVE_DEVICE_ALL) {
Log.e(TAG, "Invalid profiles param value in setActiveDevice");
- return false;
+ throw new IllegalArgumentException("Profiles must be one of "
+ + "BluetoothAdapter.ACTIVE_DEVICE_AUDIO, "
+ + "BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL, or "
+ + "BluetoothAdapter.ACTIVE_DEVICE_ALL");
}
-
try {
mServiceLock.readLock().lock();
if (mService != null) {
@@ -2162,6 +2209,33 @@
}
/**
+ * Fetches a list of the most recently connected bluetooth devices ordered by how recently they
+ * were connected with most recently first and least recently last
+ *
+ * @return {@link List} of bonded {@link BluetoothDevice} ordered by how recently they were
+ * connected
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ public @NonNull List<BluetoothDevice> getMostRecentlyConnectedDevices() {
+ if (getState() != STATE_ON) {
+ return new ArrayList<>();
+ }
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) {
+ return mService.getMostRecentlyConnectedDevices();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return new ArrayList<>();
+ }
+
+ /**
* Return the set of {@link BluetoothDevice} objects that are bonded
* (paired) to the local adapter.
* <p>If Bluetooth state is not {@link #STATE_ON}, this API
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 12dc814c..5b60b85 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -23,6 +23,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -179,9 +180,10 @@
* <p>Always contains the extra field {@link #EXTRA_DEVICE}.
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
+ @SuppressLint("ActionValue")
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_ALIAS_CHANGED =
- "android.bluetooth.action.ALIAS_CHANGED";
+ "android.bluetooth.device.action.ALIAS_CHANGED";
/**
* Broadcast Action: Indicates a change in the bond state of a remote
@@ -1091,7 +1093,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH)
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean setAlias(@NonNull String alias) {
final IBluetooth service = sService;
if (service == null) {
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index a923be6..b5959c0 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -706,7 +706,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index ce9693d..fa12c08 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3322,15 +3322,19 @@
// of dependent changes that would conflict throughout the automerger graph. Having this
// temporarily helps with the process of going through with all these dependent changes across
// the entire tree.
+ // STOPSHIP (b/148055573) : remove this before R is released.
/**
* @hide
* Register a NetworkAgent with ConnectivityService.
* @return Network corresponding to NetworkAgent.
+ * @deprecated use the version that takes a NetworkScore and a provider ID.
*/
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+ @Deprecated
public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
NetworkCapabilities nc, int score, NetworkAgentConfig config) {
- return registerNetworkAgent(messenger, ni, lp, nc, score, config, NetworkProvider.ID_NONE);
+ final NetworkScore ns = new NetworkScore.Builder().setLegacyScore(score).build();
+ return registerNetworkAgent(messenger, ni, lp, nc, ns, config, NetworkProvider.ID_NONE);
}
/**
@@ -3340,7 +3344,7 @@
*/
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
- NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) {
+ NetworkCapabilities nc, NetworkScore score, NetworkAgentConfig config, int providerId) {
try {
return mService.registerNetworkAgent(messenger, ni, lp, nc, score, config, providerId);
} catch (RemoteException e) {
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 3e9e7fa..0dc66b5 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -26,6 +26,7 @@
import android.net.NetworkInfo;
import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
+import android.net.NetworkScore;
import android.net.NetworkState;
import android.net.ISocketKeepaliveCallback;
import android.net.ProxyInfo;
@@ -154,7 +155,7 @@
void declareNetworkRequestUnfulfillable(in NetworkRequest request);
Network registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
- in NetworkCapabilities nc, int score, in NetworkAgentConfig config,
+ in NetworkCapabilities nc, in NetworkScore score, in NetworkAgentConfig config,
in int factorySerialNumber);
NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 7cc569a..ddf8dbb 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -117,13 +117,6 @@
public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3;
/**
- * Centralize the place where base network score, and network score scaling, will be
- * stored, so as we can consistently compare apple and oranges, or wifi, ethernet and LTE
- * @hide
- */
- public static final int WIFI_BASE_SCORE = 60;
-
- /**
* Sent by the NetworkAgent to ConnectivityService to pass the current
* network score.
* obj = network score Integer
@@ -272,7 +265,13 @@
*/
public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17;
+ // STOPSHIP (b/148055573) : remove this before R is released.
+ private static NetworkScore makeNetworkScore(int score) {
+ return new NetworkScore.Builder().setLegacyScore(score).build();
+ }
+
/** @hide TODO: remove and replace usage with the public constructor. */
+ // STOPSHIP (b/148055573) : remove this before R is released.
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score) {
this(looper, context, logTag, ni, nc, lp, score, null, NetworkProvider.ID_NONE);
@@ -280,6 +279,7 @@
}
/** @hide TODO: remove and replace usage with the public constructor. */
+ // STOPSHIP (b/148055573) : remove this before R is released.
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config) {
this(looper, context, logTag, ni, nc, lp, score, config, NetworkProvider.ID_NONE);
@@ -287,6 +287,7 @@
}
/** @hide TODO: remove and replace usage with the public constructor. */
+ // STOPSHIP (b/148055573) : remove this before R is released.
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, int providerId) {
this(looper, context, logTag, ni, nc, lp, score, null, providerId);
@@ -294,10 +295,12 @@
}
/** @hide TODO: remove and replace usage with the public constructor. */
+ // STOPSHIP (b/148055573) : remove this before R is released.
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config,
int providerId) {
- this(looper, context, logTag, nc, lp, score, config, providerId, ni, true /* legacy */);
+ this(looper, context, logTag, nc, lp, makeNetworkScore(score), config, providerId, ni,
+ true /* legacy */);
register();
}
@@ -323,8 +326,9 @@
* @param provider the {@link NetworkProvider} managing this agent.
*/
public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag,
- @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
- @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) {
+ @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp,
+ @NonNull NetworkScore score, @NonNull NetworkAgentConfig config,
+ @Nullable NetworkProvider provider) {
this(looper, context, logTag, nc, lp, score, config,
provider == null ? NetworkProvider.ID_NONE : provider.getProviderId(),
getLegacyNetworkInfo(config), false /* legacy */);
@@ -334,12 +338,12 @@
public final Context context;
public final NetworkCapabilities capabilities;
public final LinkProperties properties;
- public final int score;
+ public final NetworkScore score;
public final NetworkAgentConfig config;
public final NetworkInfo info;
InitialConfiguration(@NonNull Context context, @NonNull NetworkCapabilities capabilities,
- @NonNull LinkProperties properties, int score, @NonNull NetworkAgentConfig config,
- @NonNull NetworkInfo info) {
+ @NonNull LinkProperties properties, @NonNull NetworkScore score,
+ @NonNull NetworkAgentConfig config, @NonNull NetworkInfo info) {
this.context = context;
this.capabilities = capabilities;
this.properties = properties;
@@ -351,7 +355,7 @@
private volatile InitialConfiguration mInitialConfiguration;
private NetworkAgent(@NonNull Looper looper, @NonNull Context context, @NonNull String logTag,
- @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
+ @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, NetworkScore score,
@NonNull NetworkAgentConfig config, int providerId, @NonNull NetworkInfo ni,
boolean legacy) {
mHandler = new NetworkAgentHandler(looper);
@@ -646,22 +650,8 @@
* Must be called by the agent to update the score of this network.
* @param score the new score.
*/
- public void sendNetworkScore(int score) {
- if (score < 0) {
- throw new IllegalArgumentException("Score must be >= 0");
- }
- final NetworkScore ns = new NetworkScore();
- ns.putIntExtension(NetworkScore.LEGACY_SCORE, score);
- updateScore(ns);
- }
-
- /**
- * Must be called by the agent when it has a new {@link NetworkScore} for this network.
- * @param ns the new score.
- * @hide TODO: unhide the NetworkScore class, and rename to sendNetworkScore.
- */
- public void updateScore(@NonNull NetworkScore ns) {
- queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, new NetworkScore(ns));
+ public void sendNetworkScore(@NonNull final NetworkScore score) {
+ queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, score);
}
/**
diff --git a/core/java/android/net/NetworkScore.aidl b/core/java/android/net/NetworkScore.aidl
new file mode 100644
index 0000000..be9a98b
--- /dev/null
+++ b/core/java/android/net/NetworkScore.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.net;
+
+parcelable NetworkScore;
diff --git a/core/java/android/net/NetworkScore.java b/core/java/android/net/NetworkScore.java
index 13f2994..ae17378 100644
--- a/core/java/android/net/NetworkScore.java
+++ b/core/java/android/net/NetworkScore.java
@@ -15,12 +15,18 @@
*/
package android.net;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -28,57 +34,392 @@
*
* A NetworkScore object represents the characteristics of a network that affects how good the
* network is considered for a particular use.
+ *
+ * This class is not thread-safe.
* @hide
*/
+@TestApi
+@SystemApi
public final class NetworkScore implements Parcelable {
+ /** An object containing scoring-relevant metrics for a network. */
+ public static class Metrics {
+ /** Value meaning the latency is unknown. */
+ public static final int LATENCY_UNKNOWN = -1;
- // The key of bundle which is used to get the legacy network score of NetworkAgentInfo.
- // TODO: Remove this when the transition to NetworkScore is over.
- public static final String LEGACY_SCORE = "LEGACY_SCORE";
+ /** Value meaning the bandwidth is unknown. */
+ public static final int BANDWIDTH_UNKNOWN = -1;
+
+ /**
+ * Round-trip delay in milliseconds to the relevant destination for this Metrics object.
+ *
+ * LATENCY_UNKNOWN if unknown.
+ */
+ @IntRange(from = LATENCY_UNKNOWN)
+ public final int latencyMs;
+
+ /**
+ * Downlink in kB/s with the relevant destination for this Metrics object.
+ *
+ * BANDWIDTH_UNKNOWN if unknown. If directional bandwidth is unknown, up and downlink
+ * bandwidth can have the same value.
+ */
+ @IntRange(from = BANDWIDTH_UNKNOWN)
+ public final int downlinkBandwidthKBps;
+
+ /**
+ * Uplink in kB/s with the relevant destination for this Metrics object.
+ *
+ * BANDWIDTH_UNKNOWN if unknown. If directional bandwidth is unknown, up and downlink
+ * bandwidth can have the same value.
+ */
+ @IntRange(from = BANDWIDTH_UNKNOWN)
+ public final int uplinkBandwidthKBps;
+
+ /** Constructor */
+ public Metrics(@IntRange(from = LATENCY_UNKNOWN) final int latency,
+ @IntRange(from = BANDWIDTH_UNKNOWN) final int downlinkBandwidth,
+ @IntRange(from = BANDWIDTH_UNKNOWN) final int uplinkBandwidth) {
+ latencyMs = latency;
+ downlinkBandwidthKBps = downlinkBandwidth;
+ uplinkBandwidthKBps = uplinkBandwidth;
+ }
+
+ /** toString */
+ public String toString() {
+ return "latency = " + latencyMs + " downlinkBandwidth = " + downlinkBandwidthKBps
+ + "uplinkBandwidth = " + uplinkBandwidthKBps;
+ }
+
+ @NonNull
+ public static final Metrics EMPTY =
+ new Metrics(LATENCY_UNKNOWN, BANDWIDTH_UNKNOWN, BANDWIDTH_UNKNOWN);
+ }
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = "POLICY_", value = {
+ POLICY_LOCKDOWN_VPN,
+ POLICY_VPN,
+ POLICY_IGNORE_ON_WIFI,
+ POLICY_DEFAULT_SUBSCRIPTION
+ })
+ public @interface Policy {
+ }
+
+ /**
+ * This network is a VPN with lockdown enabled.
+ *
+ * If a network with this bit is present in the list of candidates and not connected,
+ * no network can satisfy the request.
+ */
+ public static final int POLICY_LOCKDOWN_VPN = 1 << 0;
+
+ /**
+ * This network is a VPN.
+ *
+ * If a network with this bit is present and the request UID is included in the UID ranges
+ * of this network, it outscores all other networks without this bit.
+ */
+ public static final int POLICY_VPN = 1 << 1;
+
+ /**
+ * This network should not be used if a previously validated WiFi network is available.
+ *
+ * If a network with this bit is present and a previously validated WiFi is present too, the
+ * network with this bit is outscored by it. This stays true if the WiFi network
+ * becomes unvalidated : this network will not be considered.
+ */
+ public static final int POLICY_IGNORE_ON_WIFI = 1 << 2;
+
+ /**
+ * This network is the default subscription for this transport.
+ *
+ * If a network with this bit is present, other networks of the same transport without this
+ * bit are outscored by it. A device with two active subscriptions and a setting
+ * to decide the default one would have this policy bit on the network for the default
+ * subscription so that when both are active at the same time, the device chooses the
+ * network associated with the default subscription rather than the one with the best link
+ * quality (which will happen if policy doesn't dictate otherwise).
+ */
+ public static final int POLICY_DEFAULT_SUBSCRIPTION = 1 << 3;
+
+ /**
+ * Policy bits for this network. Filled in by the NetworkAgent.
+ */
+ private final int mPolicy;
+
+ /**
+ * Predicted metrics to the gateway (it contains latency and bandwidth to the gateway).
+ * This is filled by the NetworkAgent with the theoretical values of the link if available,
+ * although they may fill it with predictions from historical data if available.
+ * Note that while this member cannot be null, any and all of its members can be unknown.
+ */
@NonNull
- private final Bundle mExtensions;
+ private final Metrics mLinkLayerMetrics;
- public NetworkScore() {
- mExtensions = new Bundle();
- }
+ /**
+ * Predicted metrics to representative servers.
+ * This is filled by connectivity with (if available) a best-effort estimate of the performance
+ * information to servers the user connects to in similar circumstances, and predicted from
+ * actual measurements if possible.
+ * Note that while this member cannot be null, any and all of its members can be unknown.
+ */
+ @NonNull
+ private final Metrics mEndToEndMetrics;
- public NetworkScore(@NonNull NetworkScore source) {
- mExtensions = new Bundle(source.mExtensions);
+ /** Value meaning the signal strength is unknown. */
+ public static final int UNKNOWN_SIGNAL_STRENGTH = -1;
+
+ /** The smallest meaningful signal strength. */
+ public static final int MIN_SIGNAL_STRENGTH = 0;
+
+ /** The largest meaningful signal strength. */
+ public static final int MAX_SIGNAL_STRENGTH = 1000;
+
+ /**
+ * User-visible measure of the strength of the signal normalized 1~1000.
+ * This represents a measure of the signal strength for this network.
+ * If unknown, this has value UNKNOWN_SIGNAL_STRENGTH.
+ */
+ // A good way to populate this value is to fill it with the number of bars displayed in
+ // the system UI, scaled 0 to 1000. This is what the user sees and it makes sense to them.
+ // Cellular for example could quantize the ASU value (see SignalStrength#getAsuValue) into
+ // this, while WiFi could scale the RSSI (see WifiManager#calculateSignalLevel).
+ @IntRange(from = UNKNOWN_SIGNAL_STRENGTH, to = MAX_SIGNAL_STRENGTH)
+ private final int mSignalStrength;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = "RANGE_", value = {
+ RANGE_UNKNOWN, RANGE_CLOSE, RANGE_SHORT, RANGE_MEDIUM, RANGE_LONG
+ })
+ public @interface Range {
}
/**
- * Put the value of parcelable inside the bundle by key.
+ * The range of this network is not known.
+ * This can be used by VPN the range of which depends on the range of the underlying network.
*/
- public void putExtension(@Nullable String key, @Nullable Parcelable value) {
- mExtensions.putParcelable(key, value);
+ public static final int RANGE_UNKNOWN = 0;
+
+ /**
+ * This network typically only operates at close range, like an NFC beacon.
+ */
+ public static final int RANGE_CLOSE = 1;
+
+ /**
+ * This network typically operates at a range of a few meters to a few dozen meters, like WiFi.
+ */
+ public static final int RANGE_SHORT = 2;
+
+ /**
+ * This network typically operates at a range of a few dozen to a few hundred meters, like CBRS.
+ */
+ public static final int RANGE_MEDIUM = 3;
+
+ /**
+ * This network typically offers continuous connectivity up to many kilometers away, like LTE.
+ */
+ public static final int RANGE_LONG = 4;
+
+ /**
+ * The typical range of this networking technology.
+ *
+ * This is one of the RANGE_* constants and is filled by the NetworkAgent.
+ * This may be useful when evaluating how desirable a network is, because for two networks that
+ * have equivalent performance and cost, the one that won't lose IP continuity when the user
+ * moves is probably preferable.
+ * Agents should fill this with the largest typical range this technology provides. See the
+ * descriptions of the individual constants for guidance.
+ *
+ * If unknown, this is set to RANGE_UNKNOWN.
+ */
+ @Range private final int mRange;
+
+ /**
+ * A prediction of whether this network is likely to be unusable in a few seconds.
+ *
+ * NetworkAgents set this to true to mean they are aware that usability is limited due to
+ * low signal strength, congestion, or other reasons, and indicates that the system should
+ * only use this network as a last resort. An example would be a WiFi network when the device
+ * is about to move outside of range.
+ *
+ * This is filled by the NetworkAgent. Agents that don't know whether this network is likely
+ * to be unusable soon should set this to false.
+ */
+ private final boolean mExiting;
+
+ /**
+ * The legacy score, as a migration strategy from Q to R.
+ * STOPSHIP : remove this before R ships.
+ */
+ private final int mLegacyScore;
+
+ /**
+ * Create a new NetworkScore object.
+ */
+ private NetworkScore(@Policy final int policy, @Nullable final Metrics l2Perf,
+ @Nullable final Metrics e2ePerf,
+ @IntRange(from = UNKNOWN_SIGNAL_STRENGTH, to = MAX_SIGNAL_STRENGTH)
+ final int signalStrength,
+ @Range final int range, final boolean exiting, final int legacyScore) {
+ mPolicy = policy;
+ mLinkLayerMetrics = null != l2Perf ? l2Perf : Metrics.EMPTY;
+ mEndToEndMetrics = null != e2ePerf ? e2ePerf : Metrics.EMPTY;
+ mSignalStrength = signalStrength;
+ mRange = range;
+ mExiting = exiting;
+ mLegacyScore = legacyScore;
}
/**
- * Put the value of int inside the bundle by key.
+ * Utility function to return a copy of this with a different exiting value.
*/
- public void putIntExtension(@Nullable String key, int value) {
- mExtensions.putInt(key, value);
+ @NonNull public NetworkScore withExiting(final boolean exiting) {
+ return new NetworkScore(mPolicy, mLinkLayerMetrics, mEndToEndMetrics,
+ mSignalStrength, mRange, exiting, mLegacyScore);
}
/**
- * Get the value of non primitive type by key.
+ * Utility function to return a copy of this with a different signal strength.
*/
- public <T extends Parcelable> T getExtension(@Nullable String key) {
- return mExtensions.getParcelable(key);
+ @NonNull public NetworkScore withSignalStrength(
+ @IntRange(from = UNKNOWN_SIGNAL_STRENGTH) final int signalStrength) {
+ return new NetworkScore(mPolicy, mLinkLayerMetrics, mEndToEndMetrics,
+ signalStrength, mRange, mExiting, mLegacyScore);
}
/**
- * Get the value of int by key.
+ * Returns whether this network has a particular policy flag.
+ * @param policy the policy, as one of the POLICY_* constants.
*/
- public int getIntExtension(@Nullable String key) {
- return mExtensions.getInt(key);
+ public boolean hasPolicy(@Policy final int policy) {
+ return 0 != (mPolicy & policy);
}
/**
- * Remove the entry by given key.
+ * Returns the Metrics representing the performance of the link layer.
+ *
+ * This contains the theoretical performance of the link, if available.
+ * Note that while this function cannot return null, any and/or all of the members of the
+ * returned object can be null if unknown.
*/
- public void removeExtension(@Nullable String key) {
- mExtensions.remove(key);
+ @NonNull public Metrics getLinkLayerMetrics() {
+ return mLinkLayerMetrics;
+ }
+
+ /**
+ * Returns the Metrics representing the end-to-end performance of the network.
+ *
+ * This contains the end-to-end performance of the link, if available.
+ * Note that while this function cannot return null, any and/or all of the members of the
+ * returned object can be null if unknown.
+ */
+ @NonNull public Metrics getEndToEndMetrics() {
+ return mEndToEndMetrics;
+ }
+
+ /**
+ * Returns the signal strength of this network normalized 0~1000, or UNKNOWN_SIGNAL_STRENGTH.
+ */
+ @IntRange(from = UNKNOWN_SIGNAL_STRENGTH, to = MAX_SIGNAL_STRENGTH)
+ public int getSignalStrength() {
+ return mSignalStrength;
+ }
+
+ /**
+ * Returns the typical range of this network technology as one of the RANGE_* constants.
+ */
+ @Range public int getRange() {
+ return mRange;
+ }
+
+ /** Returns a prediction of whether this network is likely to be unusable in a few seconds. */
+ public boolean isExiting() {
+ return mExiting;
+ }
+
+ /**
+ * Get the legacy score.
+ * @hide
+ */
+ public int getLegacyScore() {
+ return mLegacyScore;
+ }
+
+ /** Builder for NetworkScore. */
+ public static class Builder {
+ private int mPolicy = 0;
+ @NonNull
+ private Metrics mLinkLayerMetrics = new Metrics(Metrics.LATENCY_UNKNOWN,
+ Metrics.BANDWIDTH_UNKNOWN, Metrics.BANDWIDTH_UNKNOWN);
+ @NonNull
+ private Metrics mEndToMetrics = new Metrics(Metrics.LATENCY_UNKNOWN,
+ Metrics.BANDWIDTH_UNKNOWN, Metrics.BANDWIDTH_UNKNOWN);
+ private int mSignalStrength = UNKNOWN_SIGNAL_STRENGTH;
+ private int mRange = RANGE_UNKNOWN;
+ private boolean mExiting = false;
+ private int mLegacyScore = 0;
+ @NonNull private Bundle mExtensions = new Bundle();
+
+ /** Create a new builder. */
+ public Builder() { }
+
+ /** Add a policy flag. */
+ @NonNull public Builder addPolicy(@Policy final int policy) {
+ mPolicy |= policy;
+ return this;
+ }
+
+ /** Clear a policy flag */
+ @NonNull public Builder clearPolicy(@Policy final int policy) {
+ mPolicy &= ~policy;
+ return this;
+ }
+
+ /** Set the link layer metrics. */
+ @NonNull public Builder setLinkLayerMetrics(@NonNull final Metrics linkLayerMetrics) {
+ mLinkLayerMetrics = linkLayerMetrics;
+ return this;
+ }
+
+ /** Set the end-to-end metrics. */
+ @NonNull public Builder setEndToEndMetrics(@NonNull final Metrics endToEndMetrics) {
+ mEndToMetrics = endToEndMetrics;
+ return this;
+ }
+
+ /** Set the signal strength. */
+ @NonNull public Builder setSignalStrength(
+ @IntRange(from = UNKNOWN_SIGNAL_STRENGTH, to = MAX_SIGNAL_STRENGTH)
+ final int signalStrength) {
+ mSignalStrength = signalStrength;
+ return this;
+ }
+
+ /** Set the range. */
+ @NonNull public Builder setRange(@Range final int range) {
+ mRange = range;
+ return this;
+ }
+
+ /** Set whether this network is exiting. */
+ @NonNull public Builder setExiting(final boolean exiting) {
+ mExiting = exiting;
+ return this;
+ }
+
+ /** Add a parcelable extension. */
+ @NonNull public Builder setLegacyScore(final int legacyScore) {
+ mLegacyScore = legacyScore;
+ return this;
+ }
+
+ /** Build the NetworkScore object represented by this builder. */
+ @NonNull public NetworkScore build() {
+ return new NetworkScore(mPolicy, mLinkLayerMetrics, mEndToMetrics,
+ mSignalStrength, mRange, mExiting, mLegacyScore);
+ }
}
@Override
@@ -88,9 +429,17 @@
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- synchronized (this) {
- dest.writeBundle(mExtensions);
- }
+ dest.writeInt(mPolicy);
+ dest.writeInt(mLinkLayerMetrics.latencyMs);
+ dest.writeInt(mLinkLayerMetrics.downlinkBandwidthKBps);
+ dest.writeInt(mLinkLayerMetrics.uplinkBandwidthKBps);
+ dest.writeInt(mEndToEndMetrics.latencyMs);
+ dest.writeInt(mEndToEndMetrics.downlinkBandwidthKBps);
+ dest.writeInt(mEndToEndMetrics.uplinkBandwidthKBps);
+ dest.writeInt(mSignalStrength);
+ dest.writeInt(mRange);
+ dest.writeBoolean(mExiting);
+ dest.writeInt(mLegacyScore);
}
public static final @NonNull Creator<NetworkScore> CREATOR = new Creator<NetworkScore>() {
@@ -106,57 +455,52 @@
};
private NetworkScore(@NonNull Parcel in) {
- mExtensions = in.readBundle();
+ mPolicy = in.readInt();
+ mLinkLayerMetrics = new Metrics(in.readInt(), in.readInt(), in.readInt());
+ mEndToEndMetrics = new Metrics(in.readInt(), in.readInt(), in.readInt());
+ mSignalStrength = in.readInt();
+ mRange = in.readInt();
+ mExiting = in.readBoolean();
+ mLegacyScore = in.readInt();
}
- // TODO: Modify this method once new fields are added into this class.
@Override
public boolean equals(@Nullable Object obj) {
if (!(obj instanceof NetworkScore)) {
return false;
}
final NetworkScore other = (NetworkScore) obj;
- return bundlesEqual(mExtensions, other.mExtensions);
+ return mPolicy == other.mPolicy
+ && mLinkLayerMetrics.latencyMs == other.mLinkLayerMetrics.latencyMs
+ && mLinkLayerMetrics.uplinkBandwidthKBps
+ == other.mLinkLayerMetrics.uplinkBandwidthKBps
+ && mEndToEndMetrics.latencyMs == other.mEndToEndMetrics.latencyMs
+ && mEndToEndMetrics.uplinkBandwidthKBps
+ == other.mEndToEndMetrics.uplinkBandwidthKBps
+ && mSignalStrength == other.mSignalStrength
+ && mRange == other.mRange
+ && mExiting == other.mExiting
+ && mLegacyScore == other.mLegacyScore;
}
@Override
public int hashCode() {
- int result = 29;
- for (String key : mExtensions.keySet()) {
- final Object value = mExtensions.get(key);
- // The key may be null, so call Objects.hash() is safer.
- result += 31 * value.hashCode() + 37 * Objects.hash(key);
- }
- return result;
- }
-
- // mExtensions won't be null since the constructor will create it.
- private boolean bundlesEqual(@NonNull Bundle bundle1, @NonNull Bundle bundle2) {
- if (bundle1 == bundle2) {
- return true;
- }
-
- // This is unlikely but it's fine to add this clause here.
- if (null == bundle1 || null == bundle2) {
- return false;
- }
-
- if (bundle1.size() != bundle2.size()) {
- return false;
- }
-
- for (String key : bundle1.keySet()) {
- final Object value1 = bundle1.get(key);
- final Object value2 = bundle2.get(key);
- if (!Objects.equals(value1, value2)) {
- return false;
- }
- }
- return true;
+ return Objects.hash(mPolicy,
+ mLinkLayerMetrics.latencyMs, mLinkLayerMetrics.uplinkBandwidthKBps,
+ mEndToEndMetrics.latencyMs, mEndToEndMetrics.uplinkBandwidthKBps,
+ mSignalStrength, mRange, mExiting, mLegacyScore);
}
/** Convert to a string */
public String toString() {
- return "NetworkScore[" + mExtensions.toString() + "]";
+ return "NetworkScore ["
+ + "Policy = " + mPolicy
+ + " LinkLayerMetrics = " + mLinkLayerMetrics
+ + " EndToEndMetrics = " + mEndToEndMetrics
+ + " SignalStrength = " + mSignalStrength
+ + " Range = " + mRange
+ + " Exiting = " + mExiting
+ + " LegacyScore = " + mLegacyScore
+ + "]";
}
}
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 3e16640..27782fa 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -391,11 +391,23 @@
* Replaces flags
* @param flags any combination of flags.
* @return the same Builder instance.
+ * @hide
*/
public @NonNull Builder replaceFlags(int flags) {
mFlags = flags;
return this;
}
+
+ /**
+ * Set flags
+ * @param flags combination of flags to be set.
+ * @param mask Bit range that should be changed.
+ * @return the same Builder instance.
+ */
+ public @NonNull Builder setFlags(int flags, int mask) {
+ mFlags = (mFlags & ~mask) | (flags & mask);
+ return this;
+ }
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 605c585..b62142c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2417,6 +2417,10 @@
public static class NameValueTable implements BaseColumns {
public static final String NAME = "name";
public static final String VALUE = "value";
+ // A flag indicating whether the current value of a setting should be preserved during
+ // restore.
+ /** @hide */
+ public static final String IS_PRESERVED_IN_RESTORE = "is_preserved_in_restore";
protected static boolean putString(ContentResolver resolver, Uri uri,
String name, String value) {
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 6334d9d..368c94c 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -31,6 +31,7 @@
import android.content.Intent;
import android.graphics.Rect;
import android.os.Build;
+import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IBinder;
@@ -39,6 +40,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.service.autofill.Dataset;
+import android.service.autofill.FillEventHistory;
import android.service.autofill.augmented.PresentationParams.SystemPopupPresentationParams;
import android.util.Log;
import android.util.Pair;
@@ -319,6 +321,31 @@
pw.print(getClass().getName()); pw.println(": nothing to dump");
}
+ /**
+ * Gets the inline augmented autofill events that happened after the last
+ * {@link #onFillRequest(FillRequest, CancellationSignal, FillController, FillCallback)} call.
+ *
+ * <p>The history is not persisted over reboots, and it's cleared every time the service
+ * replies to a
+ * {@link #onFillRequest(FillRequest, CancellationSignal, FillController, FillCallback)}
+ * by calling {@link FillCallback#onSuccess(FillResponse)}. Hence, the service should call
+ * {@link #getFillEventHistory() before finishing the {@link FillCallback}.
+ *
+ * <p>Also note that the events from the dropdown suggestion UI is not stored in the history
+ * since the service owns the UI.
+ *
+ * @return The history or {@code null} if there are no events.
+ */
+ @Nullable public final FillEventHistory getFillEventHistory() {
+ final AutofillManager afm = getSystemService(AutofillManager.class);
+
+ if (afm == null) {
+ return null;
+ } else {
+ return afm.getFillEventHistory();
+ }
+ }
+
/** @hide */
static final class AutofillProxy {
@@ -487,9 +514,10 @@
}
}
- public void onInlineSuggestionsDataReady(@NonNull List<Dataset> inlineSuggestionsData) {
+ public void onInlineSuggestionsDataReady(@NonNull List<Dataset> inlineSuggestionsData,
+ @Nullable Bundle clientState) {
try {
- mCallback.onSuccess(inlineSuggestionsData.toArray(new Dataset[]{}));
+ mCallback.onSuccess(inlineSuggestionsData.toArray(new Dataset[]{}), clientState);
} catch (RemoteException e) {
Log.e(TAG, "Error calling back with the inline suggestions data: " + e);
}
@@ -511,7 +539,8 @@
}
}
try {
- mCallback.onSuccess(/* mInlineSuggestionsData= */null);
+ mCallback.onSuccess(/* inlineSuggestionsData= */null, /* clientState=*/
+ null);
} catch (RemoteException e) {
Log.e(TAG, "Error reporting success: " + e);
}
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index b767799..d0ffd7b 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -60,7 +60,7 @@
List<Dataset> inlineSuggestions = response.getInlineSuggestions();
if (inlineSuggestions != null && !inlineSuggestions.isEmpty()) {
- mProxy.onInlineSuggestionsDataReady(inlineSuggestions);
+ mProxy.onInlineSuggestionsDataReady(inlineSuggestions, response.getClientState());
return;
}
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index e8e6ff0..68ba63a 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.os.Bundle;
import android.service.autofill.Dataset;
import com.android.internal.util.DataClass;
@@ -50,6 +51,13 @@
@DataClass.PluralOf("inlineSuggestion")
private @Nullable List<Dataset> mInlineSuggestions;
+ /**
+ * The client state that {@link AugmentedAutofillService} implementation can put anything in to
+ * identify the request and the response when calling
+ * {@link AugmentedAutofillService#getFillEventHistory()}.
+ */
+ private @Nullable Bundle mClientState;
+
private static FillWindow defaultFillWindow() {
return null;
}
@@ -58,6 +66,10 @@
return null;
}
+ private static Bundle defaultClientState() {
+ return null;
+ }
+
/** @hide */
abstract static class BaseBuilder {
@@ -82,9 +94,11 @@
@DataClass.Generated.Member
/* package-private */ FillResponse(
@Nullable FillWindow fillWindow,
- @Nullable List<Dataset> inlineSuggestions) {
+ @Nullable List<Dataset> inlineSuggestions,
+ @Nullable Bundle clientState) {
this.mFillWindow = fillWindow;
this.mInlineSuggestions = inlineSuggestions;
+ this.mClientState = clientState;
// onConstructed(); // You can define this method to get a callback
}
@@ -111,6 +125,18 @@
}
/**
+ * The client state that {@link AugmentedAutofillService} implementation can put anything in to
+ * identify the request and the response when calling
+ * {@link AugmentedAutofillService#getFillEventHistory()}.
+ *
+ * @hide
+ */
+ @DataClass.Generated.Member
+ public @Nullable Bundle getClientState() {
+ return mClientState;
+ }
+
+ /**
* A builder for {@link FillResponse}
*/
@SuppressWarnings("WeakerAccess")
@@ -119,6 +145,7 @@
private @Nullable FillWindow mFillWindow;
private @Nullable List<Dataset> mInlineSuggestions;
+ private @Nullable Bundle mClientState;
private long mBuilderFieldsSet = 0L;
@@ -157,10 +184,23 @@
return this;
}
+ /**
+ * The client state that {@link AugmentedAutofillService} implementation can put anything in to
+ * identify the request and the response when calling
+ * {@link AugmentedAutofillService#getFillEventHistory()}.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setClientState(@Nullable Bundle value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mClientState = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull FillResponse build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x4; // Mark builder used
+ mBuilderFieldsSet |= 0x8; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mFillWindow = defaultFillWindow();
@@ -168,14 +208,18 @@
if ((mBuilderFieldsSet & 0x2) == 0) {
mInlineSuggestions = defaultInlineSuggestions();
}
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mClientState = defaultClientState();
+ }
FillResponse o = new FillResponse(
mFillWindow,
- mInlineSuggestions);
+ mInlineSuggestions,
+ mClientState);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x4) != 0) {
+ if ((mBuilderFieldsSet & 0x8) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -183,10 +227,10 @@
}
@DataClass.Generated(
- time = 1577476012370L,
+ time = 1580335256422L,
codegenVersion = "1.0.14",
sourceFile = "frameworks/base/core/java/android/service/autofill/augmented/FillResponse.java",
- inputSignatures = "private @android.annotation.Nullable android.service.autofill.augmented.FillWindow mFillWindow\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineSuggestion\") @android.annotation.Nullable java.util.List<android.service.autofill.Dataset> mInlineSuggestions\nprivate static android.service.autofill.augmented.FillWindow defaultFillWindow()\nprivate static java.util.List<android.service.autofill.Dataset> defaultInlineSuggestions()\nclass FillResponse extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genHiddenGetters=true)\nabstract android.service.autofill.augmented.FillResponse.Builder addInlineSuggestion(android.service.autofill.Dataset)\nclass BaseBuilder extends java.lang.Object implements []")
+ inputSignatures = "private @android.annotation.Nullable android.service.autofill.augmented.FillWindow mFillWindow\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineSuggestion\") @android.annotation.Nullable java.util.List<android.service.autofill.Dataset> mInlineSuggestions\nprivate @android.annotation.Nullable android.os.Bundle mClientState\nprivate static android.service.autofill.augmented.FillWindow defaultFillWindow()\nprivate static java.util.List<android.service.autofill.Dataset> defaultInlineSuggestions()\nprivate static android.os.Bundle defaultClientState()\nclass FillResponse extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genHiddenGetters=true)\nabstract android.service.autofill.augmented.FillResponse.Builder addInlineSuggestion(android.service.autofill.Dataset)\nclass BaseBuilder extends java.lang.Object implements []")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/autofill/augmented/IFillCallback.aidl b/core/java/android/service/autofill/augmented/IFillCallback.aidl
index 3ccb311..31e77f3 100644
--- a/core/java/android/service/autofill/augmented/IFillCallback.aidl
+++ b/core/java/android/service/autofill/augmented/IFillCallback.aidl
@@ -16,6 +16,7 @@
package android.service.autofill.augmented;
+import android.os.Bundle;
import android.os.ICancellationSignal;
import android.service.autofill.Dataset;
@@ -27,7 +28,7 @@
*/
interface IFillCallback {
void onCancellable(in ICancellationSignal cancellation);
- void onSuccess(in @nullable Dataset[] mInlineSuggestionsData);
+ void onSuccess(in @nullable Dataset[] inlineSuggestionsData, in @nullable Bundle clientState);
boolean isCompleted();
void cancel();
}
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 023f000..531ade0 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -168,9 +168,11 @@
public static final int LISTEN_SIGNAL_STRENGTHS = 0x00000100;
/**
- * Listen for always reported changes of the network signal strengths (cellular),
+ * Listen for changes of the network signal strengths (cellular) always reported from modem,
* even in some situations such as the screen of the device is off.
*
+ * @see #onSignalStrengthsChanged
+ *
* @hide
*/
@RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java
index e5bbdf4..5875761 100644
--- a/core/java/android/timezone/CountryTimeZones.java
+++ b/core/java/android/timezone/CountryTimeZones.java
@@ -56,7 +56,7 @@
*/
@NonNull
public String getTimeZoneId() {
- return mDelegate.timeZoneId;
+ return mDelegate.getTimeZoneId();
}
/**
@@ -187,7 +187,7 @@
* would be a good choice <em>generally</em> when there's no other information available.
*/
public boolean isDefaultTimeZoneBoosted() {
- return mDelegate.getDefaultTimeZoneBoost();
+ return mDelegate.isDefaultTimeZoneBoosted();
}
/**
@@ -220,7 +220,8 @@
mDelegate.lookupByOffsetWithBias(
totalOffsetMillis, isDst, dstOffsetMillis, whenMillis, bias);
return delegateOffsetResult == null ? null :
- new OffsetResult(delegateOffsetResult.mTimeZone, delegateOffsetResult.mOneMatch);
+ new OffsetResult(
+ delegateOffsetResult.getTimeZone(), delegateOffsetResult.isOnlyMatch());
}
/**
diff --git a/core/java/android/timezone/TzDataSetVersion.java b/core/java/android/timezone/TzDataSetVersion.java
index 605155e..efe50a0 100644
--- a/core/java/android/timezone/TzDataSetVersion.java
+++ b/core/java/android/timezone/TzDataSetVersion.java
@@ -111,23 +111,23 @@
/** Returns the major version number. See {@link TzDataSetVersion}. */
public int getFormatMajorVersion() {
- return mDelegate.formatMajorVersion;
+ return mDelegate.getFormatMajorVersion();
}
/** Returns the minor version number. See {@link TzDataSetVersion}. */
public int getFormatMinorVersion() {
- return mDelegate.formatMinorVersion;
+ return mDelegate.getFormatMinorVersion();
}
/** Returns the tzdb version string. See {@link TzDataSetVersion}. */
@NonNull
public String getRulesVersion() {
- return mDelegate.rulesVersion;
+ return mDelegate.getRulesVersion();
}
/** Returns the Android revision. See {@link TzDataSetVersion}. */
public int getRevision() {
- return mDelegate.revision;
+ return mDelegate.getRevision();
}
@Override
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 06fccaf..5fdac81 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -47,6 +47,9 @@
/** @hide */
public static final String BACKUP_NO_KV_DATA_CHANGE_CALLS =
"backup_enable_no_data_notification_calls";
+ /** @hide */
+ public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED =
+ "settings_do_not_restore_preserved";
private static final Map<String, String> DEFAULT_FLAGS;
@@ -68,6 +71,11 @@
// Disabled until backup transports support it.
DEFAULT_FLAGS.put(BACKUP_NO_KV_DATA_CHANGE_CALLS, "false");
+ // Disabled by default until b/148278926 is resolved. This flags guards a feature
+ // introduced in R and will be removed in the next release (b/148367230).
+ DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "false");
+
+ DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
}
/**
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index c325874..36f4e53 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -81,7 +81,7 @@
}
CountryTimeZones.OffsetResult offsetResult = countryTimeZones.lookupByOffsetWithBias(
offsetMillis, isDst, null /* dstOffsetMillis */, whenMillis, bias);
- return offsetResult != null ? offsetResult.mTimeZone : null;
+ return offsetResult != null ? offsetResult.getTimeZone() : null;
}
/**
@@ -109,8 +109,8 @@
List<String> timeZoneIds = new ArrayList<>();
for (TimeZoneMapping timeZoneMapping : countryTimeZones.getTimeZoneMappings()) {
- if (timeZoneMapping.showInPicker) {
- timeZoneIds.add(timeZoneMapping.timeZoneId);
+ if (timeZoneMapping.isShownInPicker()) {
+ timeZoneIds.add(timeZoneMapping.getTimeZoneId());
}
}
return Collections.unmodifiableList(timeZoneIds);
diff --git a/core/java/android/view/ITaskOrganizer.aidl b/core/java/android/view/ITaskOrganizer.aidl
index e92aafe..5ccdd30 100644
--- a/core/java/android/view/ITaskOrganizer.aidl
+++ b/core/java/android/view/ITaskOrganizer.aidl
@@ -26,8 +26,7 @@
* {@hide}
*/
oneway interface ITaskOrganizer {
- void taskAppeared(in IWindowContainer container,
- in ActivityManager.RunningTaskInfo taskInfo);
+ void taskAppeared(in ActivityManager.RunningTaskInfo taskInfo);
void taskVanished(in IWindowContainer container);
/**
@@ -35,4 +34,19 @@
* ActivityTaskManagerService#applyTaskOrganizerTransaction
*/
void transactionReady(int id, in SurfaceControl.Transaction t);
+
+ /**
+ * Will fire when core attributes of a Task's info change. Relevant properties include the
+ * {@link WindowConfiguration.ActivityType} and whether it is resizable.
+ *
+ * This is used, for example, during split-screen. The flow for starting is: Something sends an
+ * Intent with windowingmode. Then WM finds a matching root task and launches the new task into
+ * it. This causes the root task's info to change because now it has a task when it didn't
+ * before. The default Divider implementation interprets this as a request to enter
+ * split-screen mode and will move all other Tasks into the secondary root task. When WM
+ * applies this change, it triggers an info change in the secondary root task because it now
+ * has children. The Divider impl looks at the info and can see that the secondary root task
+ * has adopted an ActivityType of HOME and proceeds to show the minimized dock UX.
+ */
+ void onTaskInfoChanged(in ActivityManager.RunningTaskInfo info);
}
\ No newline at end of file
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 23ba097..f226369 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -17,6 +17,7 @@
package android.view;
import static android.view.InsetsState.ITYPE_IME;
+import static android.view.InsetsState.toPublicType;
import android.annotation.Nullable;
import android.inputmethodservice.InputMethodService;
@@ -44,6 +45,12 @@
*/
private boolean mShowOnNextImeRender;
+ /**
+ * Tracks whether we have an outstanding request from the IME to show, but weren't able to
+ * execute it because we didn't have control yet.
+ */
+ private boolean mImeRequestedShow;
+
public ImeInsetsSourceConsumer(
InsetsState state, Supplier<Transaction> transactionSupplier,
InsetsController controller) {
@@ -81,13 +88,14 @@
public void onWindowFocusLost() {
super.onWindowFocusLost();
getImm().unregisterImeConsumer(this);
+ mImeRequestedShow = false;
}
@Override
- public void setControl(@Nullable InsetsSourceControl control) {
- super.setControl(control);
- if (control == null) {
- hide();
+ public void show(boolean fromIme) {
+ super.show(fromIme);
+ if (fromIme) {
+ mImeRequestedShow = true;
}
}
@@ -99,7 +107,11 @@
public @ShowResult int requestShow(boolean fromIme) {
// TODO: ResultReceiver for IME.
// TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag.
- if (fromIme) {
+
+ // If we had a request before to show from IME (tracked with mImeRequestedShow), reaching
+ // this code here means that we now got control, so we can start the animation immediately.
+ if (fromIme || mImeRequestedShow) {
+ mImeRequestedShow = false;
return ShowResult.SHOW_IMMEDIATELY;
}
@@ -115,6 +127,15 @@
getImm().notifyImeHidden();
}
+ @Override
+ public void setControl(@Nullable InsetsSourceControl control, int[] showTypes,
+ int[] hideTypes) {
+ super.setControl(control, showTypes, hideTypes);
+ if (control == null) {
+ hide();
+ }
+ }
+
private boolean isDummyOrEmptyEditor(EditorInfo info) {
// TODO(b/123044812): Handle dummy input gracefully in IME Insets API
return info == null || (info.fieldId <= 0 && info.inputType <= 0);
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 5c24047..0653b06 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -16,8 +16,6 @@
package android.view;
-import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
-import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
import static android.view.InsetsState.ISIDE_BOTTOM;
import static android.view.InsetsState.ISIDE_FLOATING;
import static android.view.InsetsState.ISIDE_LEFT;
@@ -188,7 +186,7 @@
@Override
public void finish(boolean shown) {
- if (mCancelled) {
+ if (mCancelled || mFinished) {
return;
}
setInsetsAndAlpha(shown ? mShownInsets : mHiddenInsets, 1f /* alpha */, 1f /* fraction */);
@@ -210,6 +208,10 @@
mListener.onCancelled();
}
+ public boolean isCancelled() {
+ return mCancelled;
+ }
+
InsetsAnimation getAnimation() {
return mAnimation;
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 6f76497..22d6f37 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -39,6 +39,7 @@
import android.util.Pair;
import android.util.Property;
import android.util.SparseArray;
+import android.view.InputDevice.MotionRange;
import android.view.InsetsSourceConsumer.ShowResult;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
@@ -69,7 +70,7 @@
private static final int ANIMATION_DURATION_HIDE_MS = 340;
private static final int PENDING_CONTROL_TIMEOUT_MS = 2000;
- static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+ public static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
/**
* Layout mode during insets animation: The views should be laid out as if the changing inset
@@ -77,7 +78,7 @@
* be called as if the changing insets types are shown, which will result in the views being
* laid out as if the insets are fully shown.
*/
- static final int LAYOUT_INSETS_DURING_ANIMATION_SHOWN = 0;
+ public static final int LAYOUT_INSETS_DURING_ANIMATION_SHOWN = 0;
/**
* Layout mode during insets animation: The views should be laid out as if the changing inset
@@ -85,7 +86,7 @@
* be called as if the changing insets types are hidden, which will result in the views being
* laid out as if the insets are fully hidden.
*/
- static final int LAYOUT_INSETS_DURING_ANIMATION_HIDDEN = 1;
+ public static final int LAYOUT_INSETS_DURING_ANIMATION_HIDDEN = 1;
/**
* Determines the behavior of how the views should be laid out during an insets animation that
@@ -148,7 +149,7 @@
@Override
public void set(WindowInsetsAnimationController controller, Insets value) {
controller.setInsetsAndAlpha(
- value, 1f /* alpha */, (((DefaultAnimationControlListener)
+ value, 1f /* alpha */, (((InternalAnimationControlListener)
((InsetsAnimationControlImpl) controller).getListener())
.getRawFraction()));
}
@@ -165,7 +166,7 @@
private ObjectAnimator mAnimator;
protected boolean mShow;
- InternalAnimationControlListener(boolean show) {
+ public InternalAnimationControlListener(boolean show) {
mShow = show;
}
@@ -213,7 +214,10 @@
return (float) mAnimator.getCurrentPlayTime() / mAnimator.getDuration();
}
- protected long getDurationMs() {
+ /**
+ * To get the animation duration in MS.
+ */
+ public long getDurationMs() {
if (mAnimator != null) {
return mAnimator.getDuration();
}
@@ -273,7 +277,7 @@
private final String TAG = "InsetsControllerImpl";
private final InsetsState mState = new InsetsState();
- private final InsetsState mTmpState = new InsetsState();
+ private final InsetsState mLastDispachedState = new InsetsState();
private final Rect mFrame = new Rect();
private final BiFunction<InsetsController, Integer, InsetsSourceConsumer> mConsumerCreator;
@@ -367,15 +371,20 @@
return mState;
}
- boolean onStateChanged(InsetsState state) {
- if (mState.equals(state)) {
+ public InsetsState getLastDispatchedState() {
+ return mLastDispachedState;
+ }
+
+ @VisibleForTesting
+ public boolean onStateChanged(InsetsState state) {
+ if (mState.equals(state) && mLastDispachedState.equals(state)) {
return false;
}
mState.set(state);
- mTmpState.set(state, true /* copySources */);
+ mLastDispachedState.set(state, true /* copySources */);
applyLocalVisibilityOverride();
mViewRoot.notifyInsetsChanged();
- if (!mState.equals(mTmpState)) {
+ if (!mState.equals(mLastDispachedState)) {
sendStateToWindowManager();
}
return true;
@@ -419,6 +428,9 @@
}
}
+ int[] showTypes = new int[1];
+ int[] hideTypes = new int[1];
+
// Ensure to update all existing source consumers
for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
@@ -426,15 +438,23 @@
// control may be null, but we still need to update the control to null if it got
// revoked.
- consumer.setControl(control);
+ consumer.setControl(control, showTypes, hideTypes);
}
// Ensure to create source consumers if not available yet.
for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
final InsetsSourceControl control = mTmpControlArray.valueAt(i);
- getSourceConsumer(control.getType()).setControl(control);
+ InsetsSourceConsumer consumer = getSourceConsumer(control.getType());
+ consumer.setControl(control, showTypes, hideTypes);
+
}
mTmpControlArray.clear();
+ if (showTypes[0] != 0) {
+ applyAnimation(showTypes[0], true /* show */, false /* fromIme */);
+ }
+ if (hideTypes[0] != 0) {
+ applyAnimation(hideTypes[0], false /* show */, false /* fromIme */);
+ }
}
@Override
@@ -465,7 +485,7 @@
@InternalInsetsType int internalType = internalTypes.valueAt(i);
@AnimationType int animationType = getAnimationType(internalType);
InsetsSourceConsumer consumer = getSourceConsumer(internalType);
- if (mState.getSource(internalType).isVisible() && animationType == ANIMATION_TYPE_NONE
+ if (consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE
|| animationType == ANIMATION_TYPE_SHOW) {
// no-op: already shown or animating in (because window visibility is
// applied before starting animation).
@@ -488,7 +508,7 @@
@InternalInsetsType int internalType = internalTypes.valueAt(i);
@AnimationType int animationType = getAnimationType(internalType);
InsetsSourceConsumer consumer = getSourceConsumer(internalType);
- if (!mState.getSource(internalType).isVisible() && animationType == ANIMATION_TYPE_NONE
+ if (!consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE
|| animationType == ANIMATION_TYPE_HIDE) {
// no-op: already hidden or animating out.
continue;
@@ -535,7 +555,7 @@
final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
Pair<Integer, Boolean> typesReadyPair = collectSourceControls(
- fromIme, internalTypes, controls);
+ fromIme, internalTypes, controls, animationType);
int typesReady = typesReadyPair.first;
boolean imeReady = typesReadyPair.second;
if (!imeReady) {
@@ -562,17 +582,20 @@
* @return Pair of (types ready to animate, IME ready to animate).
*/
private Pair<Integer, Boolean> collectSourceControls(boolean fromIme,
- ArraySet<Integer> internalTypes, SparseArray<InsetsSourceControl> controls) {
+ ArraySet<Integer> internalTypes, SparseArray<InsetsSourceControl> controls,
+ @AnimationType int animationType) {
int typesReady = 0;
boolean imeReady = true;
for (int i = internalTypes.size() - 1; i >= 0; i--) {
InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
- boolean setVisible = !consumer.isRequestedVisible();
- if (setVisible) {
+ boolean show = animationType == ANIMATION_TYPE_SHOW
+ || animationType == ANIMATION_TYPE_USER;
+ boolean canRun = false;
+ if (show) {
// Show request
switch(consumer.requestShow(fromIme)) {
case ShowResult.SHOW_IMMEDIATELY:
- typesReady |= InsetsState.toPublicType(consumer.getType());
+ canRun = true;
break;
case ShowResult.IME_SHOW_DELAYED:
imeReady = false;
@@ -589,11 +612,22 @@
if (!fromIme) {
consumer.notifyHidden();
}
- typesReady |= InsetsState.toPublicType(consumer.getType());
+ canRun = true;
+ }
+ if (!canRun) {
+ continue;
}
final InsetsSourceControl control = consumer.getControl();
if (control != null) {
controls.put(consumer.getType(), control);
+ typesReady |= toPublicType(consumer.getType());
+ } else if (animationType == ANIMATION_TYPE_SHOW) {
+
+ // We don't have a control at the moment. However, we still want to update requested
+ // visibility state such that in case we get control, we can apply show animation.
+ consumer.show(fromIme);
+ } else if (animationType == ANIMATION_TYPE_HIDE) {
+ consumer.hide();
}
}
return new Pair<>(typesReady, imeReady);
@@ -808,7 +842,7 @@
private void showDirectly(@InsetsType int types) {
final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
for (int i = internalTypes.size() - 1; i >= 0; i--) {
- getSourceConsumer(internalTypes.valueAt(i)).show();
+ getSourceConsumer(internalTypes.valueAt(i)).show(false /* fromIme */);
}
}
@@ -840,6 +874,9 @@
@Override
public boolean onPreDraw() {
mViewRoot.mView.getViewTreeObserver().removeOnPreDrawListener(this);
+ if (controller.isCancelled()) {
+ return true;
+ }
mViewRoot.mView.dispatchWindowInsetsAnimationStart(animation, bounds);
listener.onReady(controller, types);
return true;
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 37034ee..d4961ea 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -53,6 +53,9 @@
mType = other.mType;
mFrame = new Rect(other.mFrame);
mVisible = other.mVisible;
+ mVisibleFrame = other.mVisibleFrame != null
+ ? new Rect(other.mVisibleFrame)
+ : null;
}
public void setFrame(Rect frame) {
@@ -165,6 +168,7 @@
public int hashCode() {
int result = mType;
result = 31 * result + mFrame.hashCode();
+ result = 31 * result + (mVisibleFrame != null ? mVisibleFrame.hashCode() : 0);
result = 31 * result + (mVisible ? 1 : 0);
return result;
}
@@ -189,6 +193,15 @@
dest.writeBoolean(mVisible);
}
+ @Override
+ public String toString() {
+ return "InsetsSource: {"
+ + "mType=" + InsetsState.typeToString(mType)
+ + ", mFrame=" + mFrame.toShortString()
+ + ", mVisible" + mVisible
+ + "}";
+ }
+
public static final @android.annotation.NonNull Creator<InsetsSource> CREATOR = new Creator<InsetsSource>() {
public InsetsSource createFromParcel(Parcel in) {
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 9901d05..e6497c0 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -17,11 +17,14 @@
package android.view;
import static android.view.InsetsController.ANIMATION_TYPE_NONE;
+import static android.view.InsetsState.toPublicType;
import android.annotation.IntDef;
import android.annotation.Nullable;
+import android.util.MutableShort;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
+import android.view.WindowInsets.Type.InsetsType;
import com.android.internal.annotations.VisibleForTesting;
@@ -71,18 +74,48 @@
mRequestedVisible = InsetsState.getDefaultVisibility(type);
}
- public void setControl(@Nullable InsetsSourceControl control) {
+ /**
+ * Updates the control delivered from the server.
+
+ * @param showTypes An integer array with a single entry that determines which types a show
+ * animation should be run after setting the control.
+ * @param hideTypes An integer array with a single entry that determines which types a hide
+ * animation should be run after setting the control.
+ */
+ public void setControl(@Nullable InsetsSourceControl control,
+ @InsetsType int[] showTypes, @InsetsType int[] hideTypes) {
if (mSourceControl == control) {
return;
}
mSourceControl = control;
- applyHiddenToControl();
+
+ // We are loosing control
+ if (mSourceControl == null) {
+ mController.notifyControlRevoked(this);
+
+ // Restore server visibility.
+ mState.getSource(getType()).setVisible(
+ mController.getLastDispatchedState().getSource(getType()).isVisible());
+ applyLocalVisibilityOverride();
+ return;
+ }
+
+ // We are gaining control, and need to run an animation since previous state didn't match
+ if (mRequestedVisible != mState.getSource(mType).isVisible()) {
+ if (mRequestedVisible) {
+ showTypes[0] |= toPublicType(getType());
+ } else {
+ hideTypes[0] |= toPublicType(getType());
+ }
+ return;
+ }
+
+ // We are gaining control, but don't need to run an animation. However make sure that the
+ // leash visibility is still up to date.
if (applyLocalVisibilityOverride()) {
mController.notifyVisibilityChanged();
}
- if (mSourceControl == null) {
- mController.notifyControlRevoked(this);
- }
+ applyHiddenToControl();
}
@VisibleForTesting
@@ -95,7 +128,7 @@
}
@VisibleForTesting
- public void show() {
+ public void show(boolean fromIme) {
setRequestedVisible(true);
}
@@ -172,17 +205,13 @@
* the moment.
*/
private void setRequestedVisible(boolean requestedVisible) {
- if (mRequestedVisible == requestedVisible) {
- return;
- }
mRequestedVisible = requestedVisible;
- applyLocalVisibilityOverride();
- mController.notifyVisibilityChanged();
+ if (applyLocalVisibilityOverride()) {
+ mController.notifyVisibilityChanged();
+ }
}
private void applyHiddenToControl() {
-
- // TODO: Handle case properly when animation is running already (it shouldn't!)
if (mSourceControl == null || mSourceControl.getLeash() == null) {
return;
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index bd19799..8648682 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -21,7 +21,6 @@
import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
import static android.view.ViewRootImpl.sNewInsetsMode;
-import static android.view.WindowInsets.Type.IME;
import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.SIZE;
import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
@@ -43,7 +42,6 @@
import android.util.SparseIntArray;
import android.view.WindowInsets.Type;
import android.view.WindowInsets.Type.InsetsType;
-import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
import java.io.PrintWriter;
@@ -366,7 +364,12 @@
return result;
}
- static @Type.InsetsType int toPublicType(@InternalInsetsType int type) {
+ /**
+ * Converting a internal type to the public type.
+ * @param type internal insets type, {@code InternalInsetsType}.
+ * @return public insets type, {@code Type.InsetsType}.
+ */
+ public static @Type.InsetsType int toPublicType(@InternalInsetsType int type) {
switch (type) {
case ITYPE_STATUS_BAR:
return Type.STATUS_BARS;
@@ -510,5 +513,13 @@
mSources.put(source.getType(), source);
}
}
+
+ @Override
+ public String toString() {
+ return "InsetsState: {"
+ + "mDisplayFrame=" + mDisplayFrame
+ + ", mSources=" + mSources
+ + "}";
+ }
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d70bf22f..ee7f6fb 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3654,8 +3654,7 @@
*
* @deprecated For floating windows, use {@link LayoutParams#setFitInsetsTypes(int)} with
* {@link Type#navigationBars()}. For non-floating windows that fill the screen, call
- * {@link Window#setOnContentApplyWindowInsetsListener} with {@code null} or a listener that
- * doesn't fit the navigation bar on the window content level.
+ * {@link Window#setDecorFitsSystemWindows(boolean)} with {@code false}.
*/
public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200;
@@ -3683,8 +3682,7 @@
*
* @deprecated For floating windows, use {@link LayoutParams#setFitInsetsTypes(int)} with
* {@link Type#statusBars()} ()}. For non-floating windows that fill the screen, call
- * {@link Window#setOnContentApplyWindowInsetsListener} with {@code null} or a listener that
- * doesn't fit the status bar on the window content level.
+ * {@link Window#setDecorFitsSystemWindows(boolean)} with {@code false}.
*/
@Deprecated
public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400;
@@ -4673,7 +4671,7 @@
private ArrayList<OnUnhandledKeyEventListener> mUnhandledKeyListeners;
- private WindowInsetsAnimationCallback mWindowInsetsAnimationCallback;
+ WindowInsetsAnimationCallback mWindowInsetsAnimationCallback;
/**
* This lives here since it's only valid for interactive views.
@@ -11537,7 +11535,7 @@
}
/**
- * @see #PFLAG4_OPTIONAL_FITS_SYSTEM_WINDOWS
+ * @see #PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS
* @hide
*/
public void makeFrameworkOptionalFitsSystemWindows() {
@@ -11545,6 +11543,13 @@
}
/**
+ * @hide
+ */
+ public boolean isFrameworkOptionalFitsSystemWindows() {
+ return (mPrivateFlags4 & PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS) != 0;
+ }
+
+ /**
* Returns the visibility status for this view.
*
* @return One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 4f03ca1..d416d42 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -7239,6 +7239,18 @@
public void dispatchWindowInsetsAnimationPrepare(
@NonNull InsetsAnimation animation) {
super.dispatchWindowInsetsAnimationPrepare(animation);
+
+ // If we are root-level content view that fits insets, set dispatch mode to stop to imitate
+ // consume behavior.
+ boolean isOptionalFitSystemWindows = (mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) != 0
+ || isFrameworkOptionalFitsSystemWindows();
+ if (isOptionalFitSystemWindows && mAttachInfo != null
+ && getListenerInfo().mWindowInsetsAnimationCallback == null
+ && mAttachInfo.mContentOnApplyWindowInsetsListener != null) {
+ mInsetsAnimationDispatchMode = DISPATCH_MODE_STOP;
+ return;
+ }
+
if (mInsetsAnimationDispatchMode == DISPATCH_MODE_STOP) {
return;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 51ea30b..2b4b71f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4887,8 +4887,14 @@
break;
case MSG_INSETS_CONTROL_CHANGED: {
SomeArgs args = (SomeArgs) msg.obj;
- mInsetsController.onControlsChanged((InsetsSourceControl[]) args.arg2);
+
+ // Deliver state change before control change, such that:
+ // a) When gaining control, controller can compare with server state to evaluate
+ // whether it needs to run animation.
+ // b) When loosing control, controller can restore server state by taking last
+ // dispatched state as truth.
mInsetsController.onStateChanged((InsetsState) args.arg1);
+ mInsetsController.onControlsChanged((InsetsSourceControl[]) args.arg2);
break;
}
case MSG_SHOW_INSETS: {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 0ef4e33..4b284db 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -697,12 +697,10 @@
}
/**
- * Listener for applying window insets on the content of a window in a custom way.
+ * Listener for applying window insets on the content of a window. Used only by the framework to
+ * fit content according to legacy SystemUI flags.
*
- * <p>Apps may choose to implement this interface if they want to apply custom policy
- * to the way that window insets are treated for fitting root-level content views.
- *
- * @see Window#setOnContentApplyWindowInsetsListener(OnContentApplyWindowInsetsListener)
+ * @hide
*/
public interface OnContentApplyWindowInsetsListener {
@@ -716,13 +714,15 @@
*
* @param insets The root level insets that are about to be dispatched
* @return A pair, with the first element containing the insets to apply as margin to the
- * root-level content views, and the second element determining what should be
- * dispatched to the content view.
+ * root-level content views, and the second element determining what should be
+ * dispatched to the content view.
*/
- @NonNull Pair<Insets, WindowInsets> onContentApplyWindowInsets(
+ @NonNull
+ Pair<Insets, WindowInsets> onContentApplyWindowInsets(
@NonNull WindowInsets insets);
}
+
public Window(Context context) {
mContext = context;
mFeatures = mLocalFeatures = getDefaultFeatures(context);
@@ -1311,33 +1311,21 @@
}
/**
- * Sets the listener to be invoked when fitting root-level content views.
+ * Sets whether the decor view should fit root-level content views for {@link WindowInsets}.
* <p>
- * By default, a listener that inspects the now deprecated {@link View#SYSTEM_UI_LAYOUT_FLAGS}
- * as well the {@link WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE} flag is installed and
- * fits content according to these flags.
+ * If set to {@code true}, the framework will inspect the now deprecated
+ * {@link View#SYSTEM_UI_LAYOUT_FLAGS} as well the
+ * {@link WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE} flag and fits content according
+ * to these flags.
* </p>
- * @param contentOnApplyWindowInsetsListener The listener to use for fitting root-level content
- * views, or {@code null} to disable any kind of
- * content fitting on the window level and letting the
- * {@link WindowInsets} pass through to the content
- * view.
- * @see OnContentApplyWindowInsetsListener
- */
- public void setOnContentApplyWindowInsetsListener(
- @Nullable OnContentApplyWindowInsetsListener contentOnApplyWindowInsetsListener) {
- }
-
- /**
- * Resets the listener set via {@link #setOnContentApplyWindowInsetsListener} to the default
- * state.
* <p>
- * By default, a listener that inspects the now deprecated {@link View#SYSTEM_UI_LAYOUT_FLAGS}
- * as well the {@link WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE} flag is installed and
- * fits content according to these flags.
+ * If set to {@code false}, the framework will not fit the content view to the insets and will
+ * just pass through the {@link WindowInsets} to the content view.
* </p>
+ * @param decorFitsSystemWindows Whether the decor view should fit root-level content views for
+ * insets.
*/
- public void resetOnContentApplyWindowInsetsListener() {
+ public void setDecorFitsSystemWindows(boolean decorFitsSystemWindows) {
}
/**
diff --git a/core/java/android/view/WindowContainerTransaction.java b/core/java/android/view/WindowContainerTransaction.java
index c55caa3..33f21f2 100644
--- a/core/java/android/view/WindowContainerTransaction.java
+++ b/core/java/android/view/WindowContainerTransaction.java
@@ -86,6 +86,17 @@
return this;
}
+ /**
+ * Set the smallestScreenWidth of a container.
+ */
+ public WindowContainerTransaction setSmallestScreenWidthDp(IWindowContainer container,
+ int widthDp) {
+ Change cfg = getOrCreateChange(container.asBinder());
+ cfg.mConfiguration.smallestScreenWidthDp = widthDp;
+ cfg.mConfigSetMask |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+ return this;
+ }
+
public Map<IBinder, Change> getChanges() {
return mChanges;
}
diff --git a/core/java/android/view/WindowInsetsAnimationCallback.java b/core/java/android/view/WindowInsetsAnimationCallback.java
index 1e04d02..4c8463b3 100644
--- a/core/java/android/view/WindowInsetsAnimationCallback.java
+++ b/core/java/android/view/WindowInsetsAnimationCallback.java
@@ -86,9 +86,9 @@
* following:
* <p>
* <ul>
- * <li>Application calls {@link WindowInsetsController#hideInputMethod()},
- * {@link WindowInsetsController#showInputMethod()},
- * {@link WindowInsetsController#controlInputMethodAnimation}</li>
+ * <li>Application calls {@link WindowInsetsController#hide(int)},
+ * {@link WindowInsetsController#show(int)},
+ * {@link WindowInsetsController#controlWindowInsetsAnimation}</li>
* <li>onPrepare is called on the view hierarchy listeners</li>
* <li>{@link View#onApplyWindowInsets} will be called with the end state of the
* animation</li>
@@ -106,12 +106,12 @@
* related methods.
* <p>
* Note: If the animation is application controlled by using
- * {@link WindowInsetsController#controlInputMethodAnimation}, the end state of the animation
+ * {@link WindowInsetsController#controlWindowInsetsAnimation}, the end state of the animation
* is undefined as the application may decide on the end state only by passing in the
* {@code shown} parameter when calling {@link WindowInsetsAnimationController#finish}. In this
* situation, the system will dispatch the insets in the opposite visibility state before the
* animation starts. Example: When controlling the input method with
- * {@link WindowInsetsController#controlInputMethodAnimation} and the input method is currently
+ * {@link WindowInsetsController#controlWindowInsetsAnimation} and the input method is currently
* showing, {@link View#onApplyWindowInsets} will receive a {@link WindowInsets} instance for
* which {@link WindowInsets#isVisible} will return {@code false} for {@link Type#ime}.
*
@@ -246,7 +246,7 @@
* be the same as the application passed into
* {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)},
* interpolated with the interpolator passed into
- * {@link WindowInsetsController#controlInputMethodAnimation}.
+ * {@link WindowInsetsController#controlWindowInsetsAnimation}.
* </p>
* <p>
* Note: For system-initiated animations, this will always return a valid value between 0
diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java
index f91254d..701bd31 100644
--- a/core/java/android/view/WindowInsetsAnimationControlListener.java
+++ b/core/java/android/view/WindowInsetsAnimationControlListener.java
@@ -39,7 +39,7 @@
* @param controller The controller to control the inset animation.
* @param types The {@link InsetsType}s it was able to gain control over. Note that this may be
* different than the types passed into
- * {@link WindowInsetsController#controlInputMethodAnimation} in case the window
+ * {@link WindowInsetsController#controlWindowInsetsAnimation} in case the window
* wasn't able to gain the controls because it wasn't the IME target or not
* currently the window that's controlling the system bars.
*/
diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java
index 2bf0d27..4a864be 100644
--- a/core/java/android/view/WindowInsetsAnimationController.java
+++ b/core/java/android/view/WindowInsetsAnimationController.java
@@ -23,6 +23,7 @@
import android.graphics.Insets;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimationCallback.AnimationBounds;
+import android.view.animation.Interpolator;
/**
* Controller for app-driven animation of system windows.
@@ -32,7 +33,7 @@
* synchronized, such that changes the system windows and the app's current frame
* are rendered at the same time.
* <p>
- * Control is obtained through {@link WindowInsetsController#controlInputMethodAnimation}.
+ * Control is obtained through {@link WindowInsetsController#controlWindowInsetsAnimation}.
*/
@SuppressLint("NotClosable")
public interface WindowInsetsAnimationController {
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 02323cf..f501de9 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -167,62 +167,6 @@
@NonNull WindowInsetsAnimationControlListener listener);
/**
- * Lets the application control the animation for showing the IME in a frame-by-frame manner by
- * modifying the position of the IME when it's causing insets.
- *
- * @param durationMillis Duration of the animation in
- * {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the
- * animation doesn't have a predetermined duration. This value will be
- * passed to {@link InsetsAnimation#getDurationMillis()}
- * @param interpolator The interpolator used for this animation, or {@code null} if this
- * animation doesn't follow an interpolation curve. This value will be
- * passed to {@link InsetsAnimation#getInterpolator()} and used to calculate
- * {@link InsetsAnimation#getInterpolatedFraction()}.
- * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
- * IME are ready to be controlled, among other callbacks.
- *
- * @see InsetsAnimation#getFraction()
- * @see InsetsAnimation#getInterpolatedFraction()
- * @see InsetsAnimation#getInterpolator()
- * @see InsetsAnimation#getDurationMillis()
- */
- default void controlInputMethodAnimation(long durationMillis,
- @Nullable Interpolator interpolator,
- @NonNull WindowInsetsAnimationControlListener listener) {
- controlWindowInsetsAnimation(ime(), durationMillis, interpolator, listener);
- }
-
- /**
- * Makes the IME appear on screen.
- * <p>
- * Note that if the window currently doesn't have control over the IME, because it doesn't have
- * focus, it will apply the change as soon as the window gains control. The app can listen to
- * the event by observing {@link View#onApplyWindowInsets} and checking visibility with
- * {@link WindowInsets#isVisible}.
- *
- * @see #controlInputMethodAnimation
- * @see #hideInputMethod()
- */
- default void showInputMethod() {
- show(ime());
- }
-
- /**
- * Makes the IME disappear on screen.
- * <p>
- * Note that if the window currently doesn't have control over IME, because it doesn't have
- * focus, it will apply the change as soon as the window gains control. The app can listen to
- * the event by observing {@link View#onApplyWindowInsets} and checking visibility with
- * {@link WindowInsets#isVisible}.
- *
- * @see #controlInputMethodAnimation
- * @see #showInputMethod()
- */
- default void hideInputMethod() {
- hide(ime());
- }
-
- /**
* Controls the appearance of system bars.
* <p>
* For example, the following statement adds {@link #APPEARANCE_LIGHT_STATUS_BARS}:
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index a6450a1..dad7671 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -80,6 +80,7 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
+import android.view.View.OnApplyWindowInsetsListener;
import android.view.WindowInsets.Side;
import android.view.WindowInsets.Side.InsetsSide;
import android.view.WindowInsets.Type;
@@ -2193,8 +2194,9 @@
* value for {@link #softInputMode} will be ignored; the window will
* not resize, but will stay fullscreen.
*
- * @deprecated Use {@link Window#setOnContentApplyWindowInsetsListener} instead with a
- * listener that fits {@link Type#ime()} instead.
+ * @deprecated Call {@link Window#setDecorFitsSystemWindows(boolean)} with {@code false} and
+ * install an {@link OnApplyWindowInsetsListener} on your root content view that fits insets
+ * of type {@link Type#ime()}.
*/
@Deprecated
public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index db009f6..c6ed624 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -67,9 +67,10 @@
private static volatile boolean mCrashing = false;
- /*
+ /**
* Native heap allocations will now have a non-zero tag in the most significant byte.
- * See {@linktourl https://source.android.com/devices/tech/debug/tagged-pointers}.
+ * See
+ * <a href="https://source.android.com/devices/tech/debug/tagged-pointers">https://source.android.com/devices/tech/debug/tagged-pointers</a>.
*/
@ChangeId
@EnabledAfter(targetSdkVersion = VersionCodes.Q)
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index f13a638..46d7f4e 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -320,7 +320,8 @@
/** @see ViewRootImpl#mActivityConfigCallback */
private ActivityConfigCallback mActivityConfigCallback;
- private OnContentApplyWindowInsetsListener mPendingOnContentApplyWindowInsetsListener;
+ private OnContentApplyWindowInsetsListener mPendingOnContentApplyWindowInsetsListener
+ = createDefaultContentWindowInsetsListener();
static class WindowManagerHolder {
static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
@@ -2109,14 +2110,9 @@
/** Notify when decor view is attached to window and {@link ViewRootImpl} is available. */
void onViewRootImplSet(ViewRootImpl viewRoot) {
viewRoot.setActivityConfigCallback(mActivityConfigCallback);
- if (mPendingOnContentApplyWindowInsetsListener != null) {
- viewRoot.setOnContentApplyWindowInsetsListener(
- mPendingOnContentApplyWindowInsetsListener);
- mPendingOnContentApplyWindowInsetsListener = null;
- } else {
- viewRoot.setOnContentApplyWindowInsetsListener(
- createDefaultContentWindowInsetsListener());
- }
+ viewRoot.setOnContentApplyWindowInsetsListener(
+ mPendingOnContentApplyWindowInsetsListener);
+ mPendingOnContentApplyWindowInsetsListener = null;
}
private OnContentApplyWindowInsetsListener createDefaultContentWindowInsetsListener() {
@@ -2125,8 +2121,9 @@
return new Pair<>(Insets.NONE, insets);
}
- boolean includeIme = (getAttributes().softInputMode & SOFT_INPUT_MASK_ADJUST)
- == SOFT_INPUT_ADJUST_RESIZE;
+ boolean includeIme =
+ (getViewRootImpl().mWindowAttributes.softInputMode & SOFT_INPUT_MASK_ADJUST)
+ == SOFT_INPUT_ADJUST_RESIZE;
Insets insetsToApply;
if (ViewRootImpl.sNewInsetsMode == 0) {
insetsToApply = insets.getSystemWindowInsets();
@@ -3902,17 +3899,15 @@
}
@Override
- public void setOnContentApplyWindowInsetsListener(OnContentApplyWindowInsetsListener listener) {
+ public void setDecorFitsSystemWindows(boolean decorFitsSystemWindows) {
ViewRootImpl impl = getViewRootImpl();
+ OnContentApplyWindowInsetsListener listener = decorFitsSystemWindows
+ ? createDefaultContentWindowInsetsListener()
+ : null;
if (impl != null) {
impl.setOnContentApplyWindowInsetsListener(listener);
} else {
mPendingOnContentApplyWindowInsetsListener = listener;
}
}
-
- @Override
- public void resetOnContentApplyWindowInsetsListener() {
- setOnContentApplyWindowInsetsListener(createDefaultContentWindowInsetsListener());
- }
}
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 408a7a8..a87e080 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -21,7 +21,9 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.text.TextUtils;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Objects;
/**
* Simple static methods to be called at the start of your own methods to verify
@@ -497,6 +499,64 @@
}
/**
+ * Ensures that the given byte array is not {@code null}, and contains at least one element.
+ *
+ * @param value an array of elements.
+ * @param valueName the name of the argument to use if the check fails.
+
+ * @return the validated array
+ *
+ * @throws NullPointerException if the {@code value} was {@code null}
+ * @throws IllegalArgumentException if the {@code value} was empty
+ */
+ @NonNull
+ public static byte[] checkByteArrayNotEmpty(final byte[] value, final String valueName) {
+ if (value == null) {
+ throw new NullPointerException(valueName + " must not be null");
+ }
+ if (value.length == 0) {
+ throw new IllegalArgumentException(valueName + " is empty");
+ }
+ return value;
+ }
+
+ /**
+ * Ensures that argument {@code value} is one of {@code supportedValues}.
+ *
+ * @param supportedValues an array of string values
+ * @param value a string value
+ *
+ * @return the validated value
+ *
+ * @throws NullPointerException if either {@code value} or {@code supportedValues} is null
+ * @throws IllegalArgumentException if the {@code value} is not in {@code supportedValues}
+ */
+ @NonNull
+ public static String checkArgumentIsSupported(final String[] supportedValues,
+ final String value) {
+ checkNotNull(value);
+ checkNotNull(supportedValues);
+
+ if (!contains(supportedValues, value)) {
+ throw new IllegalArgumentException(value + "is not supported "
+ + Arrays.toString(supportedValues));
+ }
+ return value;
+ }
+
+ private static boolean contains(String[] values, String value) {
+ if (values == null) {
+ return false;
+ }
+ for (int i = 0; i < values.length; ++i) {
+ if (Objects.equals(value, values[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Ensures that all elements in the argument floating point array are within the inclusive range
*
* <p>While this can be used to range check against +/- infinity, note that all NaN numbers
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 790ac08..924cd81 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -621,6 +621,8 @@
char jitprithreadweightOptBuf[sizeof("-Xjitprithreadweight:")-1 + PROPERTY_VALUE_MAX];
char jittransitionweightOptBuf[sizeof("-Xjittransitionweight:")-1 + PROPERTY_VALUE_MAX];
char hotstartupsamplesOptsBuf[sizeof("-Xps-hot-startup-method-samples:")-1 + PROPERTY_VALUE_MAX];
+ char saveResolvedClassesDelayMsOptsBuf[
+ sizeof("-Xps-save-resolved-classes-delay-ms:")-1 + PROPERTY_VALUE_MAX];
char madviseRandomOptsBuf[sizeof("-XX:MadviseRandomAccess:")-1 + PROPERTY_VALUE_MAX];
char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX];
char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX];
@@ -816,6 +818,9 @@
parseRuntimeOption("dalvik.vm.hot-startup-method-samples", hotstartupsamplesOptsBuf,
"-Xps-hot-startup-method-samples:");
+ parseRuntimeOption("dalvik.vm.ps-resolved-classes-delay-ms", saveResolvedClassesDelayMsOptsBuf,
+ "-Xps-save-resolved-classes-delay-ms:");
+
property_get("ro.config.low_ram", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
addOption("-XX:LowMemoryMode");
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 1fcc8ac..cfd3c09 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -51,6 +51,7 @@
"/dev/socket/webview_zygote",
"/dev/socket/heapprofd",
"/sys/kernel/debug/tracing/trace_marker",
+ "/sys/kernel/tracing/trace_marker",
"/system/framework/framework-res.apk",
"/dev/urandom",
"/dev/ion",
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c0743e5..7c8f62c 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -150,9 +150,9 @@
// Will be removed soon.
optional PinnedStackControllerProto pinned_stack_controller = 5 [deprecated=true];
/* non app windows */
- repeated WindowTokenProto above_app_windows = 6;
- repeated WindowTokenProto below_app_windows = 7;
- repeated WindowTokenProto ime_windows = 8;
+ repeated WindowTokenProto above_app_windows = 6 [deprecated=true];
+ repeated WindowTokenProto below_app_windows = 7 [deprecated=true];
+ repeated WindowTokenProto ime_windows = 8 [deprecated=true];
optional int32 dpi = 9;
optional .android.view.DisplayInfoProto display_info = 10;
optional int32 rotation = 11;
@@ -165,8 +165,33 @@
repeated IdentifierProto closing_apps = 18;
repeated IdentifierProto changing_apps = 19;
repeated WindowTokenProto overlay_windows = 20;
+ optional DisplayAreaProto root_display_area = 21;
}
+/* represents DisplayArea object */
+message DisplayAreaProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional WindowContainerProto window_container = 1;
+ optional string name = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
+ repeated DisplayAreaChildProto children = 3;
+}
+
+/* represents a generic child of a DisplayArea */
+message DisplayAreaChildProto {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ /* At most one of the following should be present: */
+
+ /* represents a DisplayArea child */
+ optional DisplayAreaProto display_area = 1;
+ /* represents a WindowToken child */
+ optional WindowTokenProto window = 2;
+ /* represents an unknown child - the class name is recorded */
+ repeated string unknown = 3;
+}
+
+
/* represents DisplayFrames */
message DisplayFramesProto {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 181a32d..3a1b63d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4858,19 +4858,6 @@
<permission android:name="android.permission.ACCESS_SHARED_LIBRARIES"
android:protectionLevel="signature|installer" />
- <!-- Allows an app to log compat change usage.
- @hide <p>Not for use by third-party applications.</p> -->
- <permission android:name="android.permission.LOG_COMPAT_CHANGE"
- android:protectionLevel="signature" />
- <!-- Allows an app to read compat change config.
- @hide <p>Not for use by third-party applications.</p> -->
- <permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"
- android:protectionLevel="signature" />
- <!-- Allows an app to override compat change config.
- @hide <p>Not for use by third-party applications.</p> -->
- <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"
- android:protectionLevel="signature" />
-
<!-- Allows input events to be monitored. Very dangerous! @hide -->
<permission android:name="android.permission.MONITOR_INPUT"
android:protectionLevel="signature" />
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index 5cb7852..ea0a0fd 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -214,6 +214,9 @@
}
// Add listener2 on main thread.
mPm.addThermalStatusListener(mListener2);
+ verify(mListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+ .times(1)).onThermalStatusChanged(status);
+ reset(mListener2);
status = PowerManager.THERMAL_STATUS_MODERATE;
mUiDevice.executeShellCommand("cmd thermalservice override-status "
+ Integer.toString(status));
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 169716f..bdb8021 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -109,13 +109,14 @@
InsetsSourceConsumer topConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mInsetsState,
() -> mMockTransaction, mMockController);
topConsumer.setControl(
- new InsetsSourceControl(ITYPE_STATUS_BAR, mTopLeash, new Point(0, 0)));
+ new InsetsSourceControl(ITYPE_STATUS_BAR, mTopLeash, new Point(0, 0)),
+ new int[1], new int[1]);
InsetsSourceConsumer navConsumer = new InsetsSourceConsumer(ITYPE_NAVIGATION_BAR,
mInsetsState, () -> mMockTransaction, mMockController);
- navConsumer.hide();
navConsumer.setControl(new InsetsSourceControl(ITYPE_NAVIGATION_BAR, mNavLeash,
- new Point(400, 0)));
+ new Point(400, 0)), new int[1], new int[1]);
+ navConsumer.hide();
SparseArray<InsetsSourceControl> controls = new SparseArray<>();
controls.put(ITYPE_STATUS_BAR, topConsumer.getControl());
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index e68c4b8..2c9dba1 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -121,9 +121,20 @@
if (type == ITYPE_IME) {
return new InsetsSourceConsumer(type, controller.getState(),
Transaction::new, controller) {
+
+ private boolean mImeRequestedShow;
+
+ @Override
+ public void show(boolean fromIme) {
+ super.show(fromIme);
+ if (fromIme) {
+ mImeRequestedShow = true;
+ }
+ }
+
@Override
public int requestShow(boolean fromController) {
- if (fromController) {
+ if (fromController || mImeRequestedShow) {
return SHOW_IMMEDIATELY;
} else {
return IME_SHOW_DELAYED;
@@ -399,6 +410,84 @@
}
@Test
+ public void testRestoreStartsAnimation() {
+ InsetsSourceControl control =
+ new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
+ mController.onControlsChanged(new InsetsSourceControl[]{control});
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mController.hide(Type.statusBars());
+ mController.cancelExistingAnimation();
+ assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
+ assertFalse(mController.getState().getSource(ITYPE_STATUS_BAR).isVisible());
+
+ // Loosing control
+ InsetsState state = new InsetsState(mController.getState());
+ state.setSourceVisible(ITYPE_STATUS_BAR, true);
+ mController.onStateChanged(state);
+ mController.onControlsChanged(new InsetsSourceControl[0]);
+ assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
+ assertTrue(mController.getState().getSource(ITYPE_STATUS_BAR).isVisible());
+
+ // Gaining control
+ mController.onControlsChanged(new InsetsSourceControl[]{control});
+ assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
+ mController.cancelExistingAnimation();
+ assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
+ assertFalse(mController.getState().getSource(ITYPE_STATUS_BAR).isVisible());
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ @Test
+ public void testStartImeAnimationAfterGettingControl() {
+ InsetsSourceControl control =
+ new InsetsSourceControl(ITYPE_IME, mLeash, new Point());
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+
+ mController.show(ime());
+ assertFalse(mController.getState().getSource(ITYPE_IME).isVisible());
+
+ // Pretend IME is calling
+ mController.show(ime(), true /* fromIme */);
+
+ // Gaining control shortly after
+ mController.onControlsChanged(new InsetsSourceControl[]{control});
+
+ assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_IME));
+ mController.cancelExistingAnimation();
+ assertTrue(mController.getSourceConsumer(ITYPE_IME).isRequestedVisible());
+ assertTrue(mController.getState().getSource(ITYPE_IME).isVisible());
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ @Test
+ public void testStartImeAnimationAfterGettingControl_imeLater() {
+ InsetsSourceControl control =
+ new InsetsSourceControl(ITYPE_IME, mLeash, new Point());
+
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+
+ mController.show(ime());
+ assertFalse(mController.getState().getSource(ITYPE_IME).isVisible());
+
+ // Gaining control shortly after
+ mController.onControlsChanged(new InsetsSourceControl[]{control});
+
+ // Pretend IME is calling
+ mController.show(ime(), true /* fromIme */);
+
+ assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_IME));
+ mController.cancelExistingAnimation();
+ assertTrue(mController.getSourceConsumer(ITYPE_IME).isRequestedVisible());
+ assertTrue(mController.getState().getSource(ITYPE_IME).isVisible());
+ });
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ @Test
public void testAnimationEndState_controller() throws Exception {
InsetsSourceControl control =
new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
@@ -432,7 +521,7 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
WindowInsetsAnimationControlListener listener =
mock(WindowInsetsAnimationControlListener.class);
- mController.controlInputMethodAnimation(0, new LinearInterpolator(), listener);
+ mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener);
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -456,7 +545,7 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
WindowInsetsAnimationControlListener listener =
mock(WindowInsetsAnimationControlListener.class);
- mController.controlInputMethodAnimation(0, new LinearInterpolator(), listener);
+ mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener);
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -476,7 +565,7 @@
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
WindowInsetsAnimationControlListener listener =
mock(WindowInsetsAnimationControlListener.class);
- mController.controlInputMethodAnimation(0, new LinearInterpolator(), listener);
+ mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener);
// Ready gets deferred until next predraw
mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 492c036..5e9e2f0 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -18,6 +18,8 @@
import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.WindowInsets.Type.statusBars;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
@@ -90,34 +92,44 @@
});
instrumentation.waitForIdleSync();
- mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()));
+ mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()),
+ new int[1], new int[1]);
}
@Test
public void testHide() {
- mConsumer.hide();
- assertFalse("Consumer should not be visible", mConsumer.isRequestedVisible());
- verify(mSpyInsetsSource).setVisible(eq(false));
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mConsumer.hide();
+ assertFalse("Consumer should not be visible", mConsumer.isRequestedVisible());
+ verify(mSpyInsetsSource).setVisible(eq(false));
+ });
+
}
@Test
public void testShow() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ // Insets source starts out visible
+ mConsumer.hide();
+ mConsumer.show(false /* fromIme */);
+ assertTrue("Consumer should be visible", mConsumer.isRequestedVisible());
+ verify(mSpyInsetsSource).setVisible(eq(false));
+ verify(mSpyInsetsSource).setVisible(eq(true));
+ });
- // Insets source starts out visible
- mConsumer.hide();
- mConsumer.show();
- assertTrue("Consumer should be visible", mConsumer.isRequestedVisible());
- verify(mSpyInsetsSource).setVisible(eq(false));
- verify(mSpyInsetsSource).setVisible(eq(true));
}
@Test
public void testRestore() {
- mConsumer.setControl(null);
- reset(mMockTransaction);
- mConsumer.hide();
- verifyZeroInteractions(mMockTransaction);
- mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()));
- verify(mMockTransaction).hide(eq(mLeash));
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+ mConsumer.setControl(null, new int[1], new int[1]);
+ reset(mMockTransaction);
+ mConsumer.hide();
+ verifyZeroInteractions(mMockTransaction);
+ int[] hideTypes = new int[1];
+ mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()),
+ new int[1], hideTypes);
+ assertEquals(statusBars(), hideTypes[0]);
+ });
}
}
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 20be8c2..79ac2b7 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -213,6 +213,7 @@
@Test
public void testParcelUnparcel() {
mState.getSource(ITYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_IME).setVisibleFrame(new Rect(0, 0, 50, 10));
mState.getSource(ITYPE_IME).setVisible(true);
mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
Parcel p = Parcel.obtain();
@@ -224,6 +225,16 @@
}
@Test
+ public void testCopy() {
+ mState.getSource(ITYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_IME).setVisibleFrame(new Rect(0, 0, 50, 10));
+ mState.getSource(ITYPE_IME).setVisible(true);
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState2.set(mState, true);
+ assertEquals(mState, mState2);
+ }
+
+ @Test
public void testGetDefaultVisibility() {
assertTrue(InsetsState.getDefaultVisibility(ITYPE_STATUS_BAR));
assertTrue(InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR));
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 91c5fbe..13dcb4c 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1573,12 +1573,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "1004585481": {
- "message": "%s forcing orientation to %d for display id=%d",
- "level": "VERBOSE",
- "group": "WM_DEBUG_ORIENTATION",
- "at": "com\/android\/server\/wm\/DisplayContent.java"
- },
"1051545910": {
"message": "Exit animation finished in %s: remove=%b",
"level": "VERBOSE",
@@ -1717,6 +1711,12 @@
"group": "WM_DEBUG_IME",
"at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
},
+ "1389009035": {
+ "message": "NonAppWindowContainer cannot set orientation: %s",
+ "level": "WARN",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayContent.java"
+ },
"1401700824": {
"message": "Window drawn win=%s",
"level": "DEBUG",
@@ -1891,6 +1891,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
+ "1674747211": {
+ "message": "%s forcing orientation to %d for display id=%d",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_ORIENTATION",
+ "at": "com\/android\/server\/wm\/DisplayArea.java"
+ },
"1677260366": {
"message": "Finish starting %s: first real window is shown, no animation",
"level": "VERBOSE",
diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
index 551bdc6..234f42d 100644
--- a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
+++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
@@ -39,14 +39,13 @@
* ATraceMemoryDump calculates memory category first by looking at the "type" string passed to
* dumpStringValue and then by looking at "backingType" passed to setMemoryBacking.
* Only GPU Texture memory is tracked separately and everything else is grouped as one
- * "GPU Memory" category.
+ * "Misc Memory" category.
*/
static std::unordered_map<const char*, const char*> sResourceMap = {
{"malloc", "HWUI CPU Memory"}, // taken from setMemoryBacking(backingType)
{"gl_texture", "HWUI Texture Memory"}, // taken from setMemoryBacking(backingType)
- {"Texture",
- "HWUI Texture Memory"}, // taken from dumpStringValue(value, valueName="type")
- // Uncomment categories below to split "GPU Memory" into more brackets for debugging.
+ {"Texture", "HWUI Texture Memory"}, // taken from dumpStringValue(value, valueName="type")
+ // Uncomment categories below to split "Misc Memory" into more brackets for debugging.
/*{"vk_buffer", "vk_buffer"},
{"gl_renderbuffer", "gl_renderbuffer"},
{"gl_buffer", "gl_buffer"},
@@ -169,8 +168,8 @@
mLastDumpValue = 0;
mLastPurgeableDumpValue = INVALID_MEMORY_SIZE;
mLastDumpName = dumpName;
- // Categories not listed in sResourceMap are reported as "GPU memory"
- mCategory = "HWUI GPU Memory";
+ // Categories not listed in sResourceMap are reported as "Misc Memory"
+ mCategory = "HWUI Misc Memory";
}
} /* namespace skiapipeline */
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 5993e17..699b96a 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -143,10 +143,11 @@
ATRACE_CALL();
if (surface) {
- mNativeSurface = new ReliableSurface{std::move(surface)};
+ mNativeSurface = std::make_unique<ReliableSurface>(std::move(surface));
+ mNativeSurface->init();
if (enableTimeout) {
// TODO: Fix error handling & re-shorten timeout
- ANativeWindow_setDequeueTimeout(mNativeSurface.get(), 4000_ms);
+ ANativeWindow_setDequeueTimeout(mNativeSurface->getNativeWindow(), 4000_ms);
}
} else {
mNativeSurface = nullptr;
@@ -161,8 +162,9 @@
}
ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB;
- bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode,
- mRenderAheadCapacity);
+ bool hasSurface = mRenderPipeline->setSurface(
+ mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior, colorMode,
+ mRenderAheadCapacity);
mFrameNumber = -1;
@@ -203,7 +205,8 @@
void CanvasContext::allocateBuffers() {
if (mNativeSurface) {
- mNativeSurface->allocateBuffers();
+ ANativeWindow* anw = mNativeSurface->getNativeWindow();
+ ANativeWindow_allocateBuffers(anw);
}
}
@@ -428,7 +431,7 @@
presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) +
(frameIntervalNanos * (renderAhead + 1));
}
- native_window_set_buffers_timestamp(mNativeSurface.get(), presentTime);
+ native_window_set_buffers_timestamp(mNativeSurface->getNativeWindow(), presentTime);
}
void CanvasContext::draw() {
@@ -489,16 +492,18 @@
swap.swapCompletedTime = systemTime(SYSTEM_TIME_MONOTONIC);
swap.vsyncTime = mRenderThread.timeLord().latestVsync();
if (didDraw) {
- nsecs_t dequeueStart = ANativeWindow_getLastDequeueStartTime(mNativeSurface.get());
+ nsecs_t dequeueStart =
+ ANativeWindow_getLastDequeueStartTime(mNativeSurface->getNativeWindow());
if (dequeueStart < mCurrentFrameInfo->get(FrameInfoIndex::SyncStart)) {
// Ignoring dequeue duration as it happened prior to frame render start
// and thus is not part of the frame.
swap.dequeueDuration = 0;
} else {
swap.dequeueDuration =
- ANativeWindow_getLastDequeueDuration(mNativeSurface.get());
+ ANativeWindow_getLastDequeueDuration(mNativeSurface->getNativeWindow());
}
- swap.queueDuration = ANativeWindow_getLastQueueDuration(mNativeSurface.get());
+ swap.queueDuration =
+ ANativeWindow_getLastQueueDuration(mNativeSurface->getNativeWindow());
} else {
swap.dequeueDuration = 0;
swap.queueDuration = 0;
@@ -567,14 +572,16 @@
}
SkISize CanvasContext::getNextFrameSize() const {
- ReliableSurface* surface = mNativeSurface.get();
- if (surface) {
- SkISize size;
- size.fWidth = ANativeWindow_getWidth(surface);
- size.fHeight = ANativeWindow_getHeight(surface);
- return size;
+ static constexpr SkISize defaultFrameSize = {INT32_MAX, INT32_MAX};
+ if (mNativeSurface == nullptr) {
+ return defaultFrameSize;
}
- return {INT32_MAX, INT32_MAX};
+ ANativeWindow* anw = mNativeSurface->getNativeWindow();
+
+ SkISize size;
+ size.fWidth = ANativeWindow_getWidth(anw);
+ size.fHeight = ANativeWindow_getHeight(anw);
+ return size;
}
void CanvasContext::prepareAndDraw(RenderNode* node) {
@@ -702,11 +709,9 @@
if (!mNativeSurface) return false;
if (mHaveNewSurface) return true;
- int width = -1;
- int height = -1;
- ReliableSurface* surface = mNativeSurface.get();
- surface->query(NATIVE_WINDOW_WIDTH, &width);
- surface->query(NATIVE_WINDOW_HEIGHT, &height);
+ ANativeWindow* anw = mNativeSurface->getNativeWindow();
+ const int width = ANativeWindow_getWidth(anw);
+ const int height = ANativeWindow_getHeight(anw);
return width != mLastFrameWidth || height != mLastFrameHeight;
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 4490f80..0967b20 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -220,7 +220,7 @@
int32_t mLastFrameHeight = 0;
RenderThread& mRenderThread;
- sp<ReliableSurface> mNativeSurface;
+ std::unique_ptr<ReliableSurface> mNativeSurface;
// stopped indicates the CanvasContext will reject actual redraw operations,
// and defer repaint until it is un-stopped
bool mStopped = false;
diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp
index 864780f..e92500f 100644
--- a/libs/hwui/renderthread/ReliableSurface.cpp
+++ b/libs/hwui/renderthread/ReliableSurface.cpp
@@ -26,64 +26,38 @@
// to propagate this error back to the caller
constexpr bool DISABLE_BUFFER_PREFETCH = true;
-// TODO: Make surface less protected
-// This exists because perform is a varargs, and ANativeWindow has no va_list perform.
-// So wrapping/chaining that is hard. Telling the compiler to ignore protected is easy, so we do
-// that instead
-struct SurfaceExposer : Surface {
- // Make warnings happy
- SurfaceExposer() = delete;
-
- using Surface::cancelBuffer;
- using Surface::dequeueBuffer;
- using Surface::lockBuffer_DEPRECATED;
- using Surface::perform;
- using Surface::queueBuffer;
- using Surface::setBufferCount;
- using Surface::setSwapInterval;
-};
-
-#define callProtected(surface, func, ...) ((*surface).*&SurfaceExposer::func)(__VA_ARGS__)
-
ReliableSurface::ReliableSurface(sp<Surface>&& surface) : mSurface(std::move(surface)) {
LOG_ALWAYS_FATAL_IF(!mSurface, "Error, unable to wrap a nullptr");
-
- ANativeWindow::setSwapInterval = hook_setSwapInterval;
- ANativeWindow::dequeueBuffer = hook_dequeueBuffer;
- ANativeWindow::cancelBuffer = hook_cancelBuffer;
- ANativeWindow::queueBuffer = hook_queueBuffer;
- ANativeWindow::query = hook_query;
- ANativeWindow::perform = hook_perform;
-
- ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
- ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;
- ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;
- ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;
}
ReliableSurface::~ReliableSurface() {
clearReservedBuffer();
+ // Clear out the interceptors for proper hygiene.
+ // As a concrete example, if the underlying ANativeWindow is associated with
+ // an EGLSurface that is still in use, then if we don't clear out the
+ // interceptors then we walk into undefined behavior.
+ ANativeWindow_setCancelBufferInterceptor(mSurface.get(), nullptr, nullptr);
+ ANativeWindow_setDequeueBufferInterceptor(mSurface.get(), nullptr, nullptr);
+ ANativeWindow_setQueueBufferInterceptor(mSurface.get(), nullptr, nullptr);
+ ANativeWindow_setPerformInterceptor(mSurface.get(), nullptr, nullptr);
}
-void ReliableSurface::perform(int operation, va_list args) {
- std::lock_guard _lock{mMutex};
+void ReliableSurface::init() {
+ int result = ANativeWindow_setCancelBufferInterceptor(mSurface.get(), hook_cancelBuffer, this);
+ LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set cancelBuffer interceptor: error = %d",
+ result);
- switch (operation) {
- case NATIVE_WINDOW_SET_USAGE:
- mUsage = va_arg(args, uint32_t);
- break;
- case NATIVE_WINDOW_SET_USAGE64:
- mUsage = va_arg(args, uint64_t);
- break;
- case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
- /* width */ va_arg(args, uint32_t);
- /* height */ va_arg(args, uint32_t);
- mFormat = va_arg(args, PixelFormat);
- break;
- case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
- mFormat = va_arg(args, PixelFormat);
- break;
- }
+ result = ANativeWindow_setDequeueBufferInterceptor(mSurface.get(), hook_dequeueBuffer, this);
+ LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set dequeueBuffer interceptor: error = %d",
+ result);
+
+ result = ANativeWindow_setQueueBufferInterceptor(mSurface.get(), hook_queueBuffer, this);
+ LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set queueBuffer interceptor: error = %d",
+ result);
+
+ result = ANativeWindow_setPerformInterceptor(mSurface.get(), hook_perform, this);
+ LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set perform interceptor: error = %d",
+ result);
}
int ReliableSurface::reserveNext() {
@@ -111,7 +85,9 @@
int fenceFd = -1;
ANativeWindowBuffer* buffer = nullptr;
- int result = callProtected(mSurface, dequeueBuffer, &buffer, &fenceFd);
+
+ // Note that this calls back into our own hooked method.
+ int result = ANativeWindow_dequeueBuffer(mSurface.get(), &buffer, &fenceFd);
{
std::lock_guard _lock{mMutex};
@@ -138,61 +114,13 @@
mHasDequeuedBuffer = false;
}
if (buffer) {
- callProtected(mSurface, cancelBuffer, buffer, releaseFd);
+ // Note that clearReservedBuffer may be reentrant here, so
+ // mReservedBuffer must be cleared once we reach here to avoid recursing
+ // forever.
+ ANativeWindow_cancelBuffer(mSurface.get(), buffer, releaseFd);
}
}
-int ReliableSurface::cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd) {
- clearReservedBuffer();
- if (isFallbackBuffer(buffer)) {
- if (fenceFd > 0) {
- close(fenceFd);
- }
- return OK;
- }
- int result = callProtected(mSurface, cancelBuffer, buffer, fenceFd);
- return result;
-}
-
-int ReliableSurface::dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd) {
- {
- std::lock_guard _lock{mMutex};
- if (mReservedBuffer) {
- *buffer = mReservedBuffer;
- *fenceFd = mReservedFenceFd.release();
- mReservedBuffer = nullptr;
- return OK;
- }
- }
-
-
- int result = callProtected(mSurface, dequeueBuffer, buffer, fenceFd);
- if (result != OK) {
- ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result);
- *buffer = acquireFallbackBuffer(result);
- *fenceFd = -1;
- return *buffer ? OK : INVALID_OPERATION;
- } else {
- std::lock_guard _lock{mMutex};
- mHasDequeuedBuffer = true;
- }
- return OK;
-}
-
-int ReliableSurface::queueBuffer(ANativeWindowBuffer* buffer, int fenceFd) {
- clearReservedBuffer();
-
- if (isFallbackBuffer(buffer)) {
- if (fenceFd > 0) {
- close(fenceFd);
- }
- return OK;
- }
-
- int result = callProtected(mSurface, queueBuffer, buffer, fenceFd);
- return result;
-}
-
bool ReliableSurface::isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const {
if (!mScratchBuffer || !windowBuffer) {
return false;
@@ -229,82 +157,95 @@
return AHardwareBuffer_to_ANativeWindowBuffer(newBuffer);
}
-Surface* ReliableSurface::getWrapped(const ANativeWindow* window) {
- return getSelf(window)->mSurface.get();
-}
+int ReliableSurface::hook_dequeueBuffer(ANativeWindow* window,
+ ANativeWindow_dequeueBufferFn dequeueBuffer, void* data,
+ ANativeWindowBuffer** buffer, int* fenceFd) {
+ ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
+ {
+ std::lock_guard _lock{rs->mMutex};
+ if (rs->mReservedBuffer) {
+ *buffer = rs->mReservedBuffer;
+ *fenceFd = rs->mReservedFenceFd.release();
+ rs->mReservedBuffer = nullptr;
+ return OK;
+ }
+ }
-int ReliableSurface::hook_setSwapInterval(ANativeWindow* window, int interval) {
- return callProtected(getWrapped(window), setSwapInterval, interval);
-}
-
-int ReliableSurface::hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
- int* fenceFd) {
- return getSelf(window)->dequeueBuffer(buffer, fenceFd);
-}
-
-int ReliableSurface::hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
- int fenceFd) {
- return getSelf(window)->cancelBuffer(buffer, fenceFd);
-}
-
-int ReliableSurface::hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
- int fenceFd) {
- return getSelf(window)->queueBuffer(buffer, fenceFd);
-}
-
-int ReliableSurface::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
- ANativeWindowBuffer** buffer) {
- ANativeWindowBuffer* buf;
- int fenceFd = -1;
- int result = window->dequeueBuffer(window, &buf, &fenceFd);
+ int result = dequeueBuffer(window, buffer, fenceFd);
if (result != OK) {
- return result;
+ ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result);
+ *buffer = rs->acquireFallbackBuffer(result);
+ *fenceFd = -1;
+ return *buffer ? OK : INVALID_OPERATION;
+ } else {
+ std::lock_guard _lock{rs->mMutex};
+ rs->mHasDequeuedBuffer = true;
}
- sp<Fence> fence(new Fence(fenceFd));
- int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED");
- if (waitResult != OK) {
- ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d", waitResult);
- window->cancelBuffer(window, buf, -1);
- return waitResult;
- }
- *buffer = buf;
- return result;
-}
-
-int ReliableSurface::hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
- ANativeWindowBuffer* buffer) {
- return window->cancelBuffer(window, buffer, -1);
-}
-
-int ReliableSurface::hook_lockBuffer_DEPRECATED(ANativeWindow* window,
- ANativeWindowBuffer* buffer) {
- // This method is a no-op in Surface as well
return OK;
}
-int ReliableSurface::hook_queueBuffer_DEPRECATED(ANativeWindow* window,
- ANativeWindowBuffer* buffer) {
- return window->queueBuffer(window, buffer, -1);
+int ReliableSurface::hook_cancelBuffer(ANativeWindow* window,
+ ANativeWindow_cancelBufferFn cancelBuffer, void* data,
+ ANativeWindowBuffer* buffer, int fenceFd) {
+ ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
+ rs->clearReservedBuffer();
+ if (rs->isFallbackBuffer(buffer)) {
+ if (fenceFd > 0) {
+ close(fenceFd);
+ }
+ return OK;
+ }
+ return cancelBuffer(window, buffer, fenceFd);
}
-int ReliableSurface::hook_query(const ANativeWindow* window, int what, int* value) {
- return getWrapped(window)->query(what, value);
+int ReliableSurface::hook_queueBuffer(ANativeWindow* window,
+ ANativeWindow_queueBufferFn queueBuffer, void* data,
+ ANativeWindowBuffer* buffer, int fenceFd) {
+ ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
+ rs->clearReservedBuffer();
+
+ if (rs->isFallbackBuffer(buffer)) {
+ if (fenceFd > 0) {
+ close(fenceFd);
+ }
+ return OK;
+ }
+
+ return queueBuffer(window, buffer, fenceFd);
}
-int ReliableSurface::hook_perform(ANativeWindow* window, int operation, ...) {
+int ReliableSurface::hook_perform(ANativeWindow* window, ANativeWindow_performFn perform,
+ void* data, int operation, va_list args) {
// Drop the reserved buffer if there is one since this (probably) mutated buffer dimensions
// TODO: Filter to things that only affect the reserved buffer
// TODO: Can we mutate the reserved buffer in some cases?
- getSelf(window)->clearReservedBuffer();
- va_list args;
- va_start(args, operation);
- int result = callProtected(getWrapped(window), perform, operation, args);
- va_end(args);
+ ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
+ rs->clearReservedBuffer();
- va_start(args, operation);
- getSelf(window)->perform(operation, args);
- va_end(args);
+ va_list argsCopy;
+ va_copy(argsCopy, args);
+ int result = perform(window, operation, argsCopy);
+ {
+ std::lock_guard _lock{rs->mMutex};
+
+ switch (operation) {
+ case ANATIVEWINDOW_PERFORM_SET_USAGE:
+ rs->mUsage = va_arg(args, uint32_t);
+ break;
+ case ANATIVEWINDOW_PERFORM_SET_USAGE64:
+ rs->mUsage = va_arg(args, uint64_t);
+ break;
+ case ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY:
+ /* width */ va_arg(args, uint32_t);
+ /* height */ va_arg(args, uint32_t);
+ rs->mFormat = va_arg(args, PixelFormat);
+ break;
+ case ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT:
+ rs->mFormat = va_arg(args, PixelFormat);
+ break;
+ }
+ }
return result;
}
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
index f768df3..3247253 100644
--- a/libs/hwui/renderthread/ReliableSurface.h
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -16,6 +16,7 @@
#pragma once
+#include <apex/window.h>
#include <gui/Surface.h>
#include <utils/Macros.h>
#include <utils/StrongPointer.h>
@@ -24,16 +25,21 @@
namespace android::uirenderer::renderthread {
-class ReliableSurface : public ANativeObjectBase<ANativeWindow, ReliableSurface, RefBase> {
+class ReliableSurface {
PREVENT_COPY_AND_ASSIGN(ReliableSurface);
public:
ReliableSurface(sp<Surface>&& surface);
~ReliableSurface();
- int reserveNext();
+ // Performs initialization that is not safe to do in the constructor.
+ // For instance, registering ANativeWindow interceptors with ReliableSurface
+ // passed as the data pointer is not safe.
+ void init();
- void allocateBuffers() { mSurface->allocateBuffers(); }
+ ANativeWindow* getNativeWindow() { return mSurface.get(); }
+
+ int reserveNext();
int query(int what, int* value) const { return mSurface->query(what, value); }
@@ -61,7 +67,7 @@
}
private:
- const sp<Surface> mSurface;
+ sp<Surface> mSurface;
mutable std::mutex mMutex;
@@ -78,27 +84,20 @@
ANativeWindowBuffer* acquireFallbackBuffer(int error);
void clearReservedBuffer();
- void perform(int operation, va_list args);
- int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd);
- int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
- int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd);
+ // ANativeWindow hooks. When an ANativeWindow_* method is called on the
+ // underlying ANativeWindow, these methods will intercept the original call.
+ // For example, an EGL driver would call into these hooks instead of the
+ // original methods.
+ static int hook_cancelBuffer(ANativeWindow* window, ANativeWindow_cancelBufferFn cancelBuffer,
+ void* data, ANativeWindowBuffer* buffer, int fenceFd);
+ static int hook_dequeueBuffer(ANativeWindow* window,
+ ANativeWindow_dequeueBufferFn dequeueBuffer, void* data,
+ ANativeWindowBuffer** buffer, int* fenceFd);
+ static int hook_queueBuffer(ANativeWindow* window, ANativeWindow_queueBufferFn queueBuffer,
+ void* data, ANativeWindowBuffer* buffer, int fenceFd);
- static Surface* getWrapped(const ANativeWindow*);
-
- // ANativeWindow hooks
- static int hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
- static int hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
- int* fenceFd);
- static int hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
-
- static int hook_perform(ANativeWindow* window, int operation, ...);
- static int hook_query(const ANativeWindow* window, int what, int* value);
- static int hook_setSwapInterval(ANativeWindow* window, int interval);
-
- static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
- static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer);
- static int hook_lockBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
- static int hook_queueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
+ static int hook_perform(ANativeWindow* window, ANativeWindow_performFn perform, void* data,
+ int operation, va_list args);
};
}; // namespace android::uirenderer::renderthread
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index 7a12cee..eb76c29 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -16,7 +16,6 @@
package android.location;
-import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -1214,23 +1213,6 @@
}
/**
- * Attaches an extra {@link Location} to this Location. This is useful for location providers
- * to set the {@link #EXTRA_NO_GPS_LOCATION} extra to provide coarse locations for clients.
- *
- * @param key the key associated with the Location extra
- * @param value the Location to attach
- * @hide
- */
- @TestApi
- @SystemApi
- public void setExtraLocation(@Nullable String key, @Nullable Location value) {
- if (mExtras == null) {
- mExtras = new Bundle();
- }
- mExtras.putParcelable(key, value);
- }
-
- /**
* Returns true if the Location came from a mock provider.
*
* @return true if this Location came from a mock provider, false otherwise
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index dd01243..0e88c75 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
@@ -197,6 +198,7 @@
final List<String> mFeatures;
@DeviceType
final int mDeviceType;
+ final boolean mIsSystem;
final Uri mIconUri;
final CharSequence mDescription;
@ConnectionState
@@ -213,6 +215,7 @@
mName = builder.mName;
mFeatures = builder.mFeatures;
mDeviceType = builder.mDeviceType;
+ mIsSystem = builder.mIsSystem;
mIconUri = builder.mIconUri;
mDescription = builder.mDescription;
mConnectionState = builder.mConnectionState;
@@ -229,6 +232,7 @@
mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mFeatures = in.createStringArrayList();
mDeviceType = in.readInt();
+ mIsSystem = in.readBoolean();
mIconUri = in.readParcelable(null);
mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mConnectionState = in.readInt();
@@ -287,6 +291,17 @@
}
/**
+ * Returns whether the route is a system route or not.
+ * <p>
+ * System routes are media routes directly controlled by the system
+ * such as phone speaker, wired headset, and Bluetooth devices.
+ * </p>
+ */
+ public boolean isSystemRoute() {
+ return mIsSystem;
+ }
+
+ /**
* Gets the URI of the icon representing this route.
* <p>
* This icon will be used in picker UIs if available.
@@ -360,6 +375,7 @@
* @hide
*/
@NonNull
+ @TestApi
public String getOriginalId() {
return mId;
}
@@ -423,6 +439,7 @@
&& Objects.equals(mName, other.mName)
&& Objects.equals(mFeatures, other.mFeatures)
&& (mDeviceType == other.mDeviceType)
+ && (mIsSystem == other.mIsSystem)
&& Objects.equals(mIconUri, other.mIconUri)
&& Objects.equals(mDescription, other.mDescription)
&& (mConnectionState == other.mConnectionState)
@@ -436,7 +453,7 @@
@Override
public int hashCode() {
// Note: mExtras is not included.
- return Objects.hash(mId, mName, mFeatures, mDeviceType, mIconUri, mDescription,
+ return Objects.hash(mId, mName, mFeatures, mDeviceType, mIsSystem, mIconUri, mDescription,
mConnectionState, mClientPackageName, mVolumeHandling, mVolumeMax, mVolume,
mProviderId);
}
@@ -473,6 +490,7 @@
TextUtils.writeToParcel(mName, dest, flags);
dest.writeStringList(mFeatures);
dest.writeInt(mDeviceType);
+ dest.writeBoolean(mIsSystem);
dest.writeParcelable(mIconUri, flags);
TextUtils.writeToParcel(mDescription, dest, flags);
dest.writeInt(mConnectionState);
@@ -494,6 +512,7 @@
@DeviceType
int mDeviceType = DEVICE_TYPE_UNKNOWN;
+ boolean mIsSystem;
Uri mIconUri;
CharSequence mDescription;
@ConnectionState
@@ -540,6 +559,7 @@
mName = routeInfo.mName;
mFeatures = new ArrayList<>(routeInfo.mFeatures);
mDeviceType = routeInfo.mDeviceType;
+ mIsSystem = routeInfo.mIsSystem;
mIconUri = routeInfo.mIconUri;
mDescription = routeInfo.mDescription;
mConnectionState = routeInfo.mConnectionState;
@@ -608,6 +628,16 @@
}
/**
+ * Sets whether the route is a system route or not.
+ * @hide
+ */
+ @NonNull
+ public Builder setSystemRoute(boolean isSystem) {
+ mIsSystem = isSystem;
+ return this;
+ }
+
+ /**
* Sets the URI of the icon representing this route.
* <p>
* This icon will be used in picker UIs if available.
diff --git a/media/java/android/media/MediaRoute2ProviderInfo.java b/media/java/android/media/MediaRoute2ProviderInfo.java
index c9a2ec7..afe002e 100644
--- a/media/java/android/media/MediaRoute2ProviderInfo.java
+++ b/media/java/android/media/MediaRoute2ProviderInfo.java
@@ -180,6 +180,23 @@
}
/**
+ * Sets whether the provider provides system routes or not
+ */
+ @NonNull
+ public Builder setSystemRouteProvider(boolean isSystem) {
+ int count = mRoutes.size();
+ for (int i = 0; i < count; i++) {
+ MediaRoute2Info route = mRoutes.valueAt(i);
+ if (route.isSystemRoute() != isSystem) {
+ mRoutes.setValueAt(i, new MediaRoute2Info.Builder(route)
+ .setSystemRoute(isSystem)
+ .build());
+ }
+ }
+ return this;
+ }
+
+ /**
* Adds a route to the provider
*/
@NonNull
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 7b9a44f..f751a22 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -230,7 +230,11 @@
/**
* Gets the unmodifiable list of {@link MediaRoute2Info routes} currently
* known to the media router.
+ * <p>
+ * {@link MediaRoute2Info#isSystemRoute() System routes} such as phone speaker,
+ * Bluetooth devices are always included in the list.
* Please note that the list can be changed before callbacks are invoked.
+ * </p>
*
* @return the list of routes that contains at least one of the route features in discovery
* preferences registered by the application
@@ -243,7 +247,8 @@
List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
for (MediaRoute2Info route : mRoutes.values()) {
- if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+ if (route.isSystemRoute()
+ || route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
filteredRoutes.add(route);
}
}
@@ -307,12 +312,18 @@
* with the given route.
*
* @param route the route you want to create a controller with.
+ * @throws IllegalArgumentException if the given route is
+ * {@link MediaRoute2Info#isSystemRoute() system route}
*
* @see RoutingControllerCallback#onControllerCreated
* @see RoutingControllerCallback#onControllerCreationFailed
*/
public void requestCreateController(@NonNull MediaRoute2Info route) {
Objects.requireNonNull(route, "route must not be null");
+ if (route.isSystemRoute()) {
+ throw new IllegalArgumentException("Can't create a route controller with "
+ + "a system route. Use getSystemController().");
+ }
// TODO: Check the given route exists
final int requestId;
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 61e2f77..662eeb1 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -161,14 +161,15 @@
public List<MediaRoute2Info> getAvailableRoutes(@NonNull String packageName) {
Objects.requireNonNull(packageName, "packageName must not be null");
+ List<MediaRoute2Info> routes = new ArrayList<>();
+
List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
if (preferredFeatures == null) {
- return Collections.emptyList();
+ preferredFeatures = Collections.emptyList();
}
- List<MediaRoute2Info> routes = new ArrayList<>();
synchronized (mRoutesLock) {
for (MediaRoute2Info route : mRoutes.values()) {
- if (route.hasAnyFeatures(preferredFeatures)) {
+ if (route.isSystemRoute() || route.hasAnyFeatures(preferredFeatures)) {
routes.add(route);
}
}
diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java b/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java
index 074188e..adf4d3d 100644
--- a/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java
+++ b/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java
@@ -157,9 +157,7 @@
Handler handler = eventHandler.handler();
if (handler != null) {
Message m = handler.obtainMessage(what, arg1, arg2, obj);
- if (what != AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER) {
- handler.removeMessages(what);
- }
+ // Do not remove previous messages, as we would lose notification of group changes
handler.sendMessage(m);
}
}
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 4a2044a..16259ab 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -196,7 +196,14 @@
public void testRouteFeatures() throws Exception {
Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_SPECIAL);
- assertEquals(1, routes.size());
+ int routeCount = 0;
+ for (MediaRoute2Info route : routes.values()) {
+ if (!route.isSystemRoute()) {
+ routeCount++;
+ }
+ }
+
+ assertEquals(1, routeCount);
assertNotNull(routes.get(ROUTE_ID_SPECIAL_FEATURE));
}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 97b861b..a8f1d2c 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -23,6 +23,8 @@
AChoreographer_postFrameCallbackDelayed; # introduced=24
AChoreographer_postFrameCallback64; # introduced=29
AChoreographer_postFrameCallbackDelayed64; # introduced=29
+ AChoreographer_registerRefreshRateCallback; # introduced=30
+ AChoreographer_unregisterRefreshRateCallback; # introduced=30
AConfiguration_copy;
AConfiguration_delete;
AConfiguration_diff;
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index ec445d4..38cf5ab 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -318,6 +318,16 @@
return false;
}
+ // Allow all directories on USB, including the root.
+ try {
+ RootInfo rootInfo = getRootFromDocId(docId);
+ if ((rootInfo.flags & Root.FLAG_REMOVABLE_USB) == Root.FLAG_REMOVABLE_USB) {
+ return false;
+ }
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Failed to determine rootInfo for docId");
+ }
+
final String path = getPathFromDocId(docId);
// Block the root of the storage
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 2b143e4..b31841d 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Gekoppel via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Beskikbaar via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tik om aan te meld"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Gekoppel, geen internet nie"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Daar kan nie by private DNS-bediener ingegaan word nie"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Beperkte verbinding"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Geen internet nie"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 2a93e017..4955ad8 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"በ <xliff:g id="NAME">%1$s</xliff:g> በኩል ተገናኝተዋል"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"በ%1$s በኩል የሚገኝ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ለመመዝገብ መታ ያድርጉ"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ተገናኝቷል፣ ምንም በይነመረብ የለም"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"የግል ዲኤንኤስ አገልጋይ ሊደረስበት አይችልም"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"የተገደበ ግንኙነት"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ምንም በይነመረብ የለም"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 5103345..7b26be4 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"تم الاتصال عبر <xliff:g id="NAME">%1$s</xliff:g>."</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"متوفرة عبر %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"انقر للاشتراك."</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"متصلة ولكن بلا إنترنت"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"لا يمكن الوصول إلى خادم أسماء نظام نطاقات خاص"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"اتصال محدود"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"لا يتوفر اتصال إنترنت."</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index fa26b9b..dcebe5b 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g>ৰ জৰিয়তে সংযুক্ত"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$sৰ মাধ্যমেৰে উপলব্ধ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ছাইন আপ কৰিবলৈ টিপক"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"সংযোজিত, ইণ্টাৰনেট নাই"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"ব্যক্তিগত DNS ছাৰ্ভাৰ এক্সেছ কৰিব নোৱাৰি"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"ইণ্টাৰনেট সংযোগ সীমিত"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ইণ্টাৰনেট সংযোগ নাই"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 2e80fdc..7f3db37 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ilə qoşulub"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s vasitəsilə əlçatandır"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Qeydiyyatdan keçmək üçün klikləyin"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Qoşuludur, internet yoxdur"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Özəl DNS serverinə giriş mümkün deyil"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Məhdud bağlantı"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"İnternet yoxdur"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index ae5c936..b789cb0 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Povezano preko: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Dostupna je preko pristupne tačke %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Dodirnite da biste se registrovali"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Veza je uspostavljena, nema interneta"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Pristup privatnom DNS serveru nije uspeo"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ograničena veza"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nema interneta"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index ad201d0..ce191eb 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Падключана праз праграму \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Даступна праз %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Націсніце, каб зарэгістравацца"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Падключана, без доступу да інтэрнэту"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Не ўдалося атрымаць доступ да прыватнага DNS-сервера"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Абмежаваныя магчымасці падключэння"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Не падключана да інтэрнэту"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 94f78ad..3e6f77d 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Установена е връзка през <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Мрежата е достъпна през „%1$s“"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Докоснете, за да се регистрирате"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Установена е връзка – няма достъп до интернет"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Не може да се осъществи достъп до частния DNS сървър"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ограничена връзка"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Няма връзка с интернет"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 51719160..7f6938a 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g>-এর মাধ্যমে কানেক্ট করা আছে"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s এর মাধ্যমে উপলব্ধ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"সাইন-আপ করতে ট্যাপ করুন"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"কানেক্ট, ইন্টারনেট নেই"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"ব্যক্তিগত ডিএনএস সার্ভার অ্যাক্সেস করা যাবে না"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"সীমিত কানেকশন"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ইন্টারনেট কানেকশন নেই"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index f74bcee..0563abd 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Povezano preko <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Dostupan preko %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Dodirnite za prijavu"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Povezano, nema interneta"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Nije moguće pristupiti privatnom DNS serveru"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ograničena veza"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nema internetske veze"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 1b23ecf..71b2c5d 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connectat mitjançant <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponible mitjançant %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toca per registrar-te"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connectada, sense Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"No es pot accedir al servidor DNS privat"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Connexió limitada"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sense connexió a Internet"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 22603cc..a381772 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Připojeno přes <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Dostupné prostřednictvím %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Klepnutím se zaregistrujete"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Připojeno, není k dispozici internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Nelze získat přístup k soukromému serveru DNS"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Omezené připojení"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nejste připojeni k internetu"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index bef1855..f6e8576 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Forbundet via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Tilgængelig via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tryk for at registrere dig"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Tilsluttet – intet internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Der er ikke adgang til den private DNS-server"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Begrænset forbindelse"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Intet internet"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index c8c97bd..99a0910 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Verbunden über <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Verfügbar über %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Zum Anmelden tippen"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Verbunden, kein Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Auf den privaten DNS-Server kann nicht zugegriffen werden"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Eingeschränkte Verbindung"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Kein Internet"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 371075c..d37ea44 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Συνδέθηκε μέσω <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Διαθέσιμο μέσω %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Πατήστε για εγγραφή"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Συνδέθηκε, χωρίς σύνδεση στο διαδίκτυο"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Δεν είναι δυνατή η πρόσβαση στον ιδιωτικό διακομιστή DNS."</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Περιορισμένη σύνδεση"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Δεν υπάρχει σύνδεση στο διαδίκτυο"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index b314d17..429cd3e 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connected, no Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Limited connection"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"No Internet"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index b314d17..429cd3e 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connected, no Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Limited connection"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"No Internet"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index b314d17..429cd3e 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connected, no Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Limited connection"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"No Internet"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index b314d17..429cd3e 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connected, no Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Limited connection"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"No Internet"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 95944dc..1aa6cdb 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connected, no internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Limited connection"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"No internet"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index c6dfdd3..7d28a32 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Conexión a través de <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponible a través de %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Presiona para registrarte"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conectado pero sin conexión a Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"No se puede acceder al servidor DNS privado"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Conexión limitada"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sin Internet"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index ba4a9ff..b35696f5 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Conectado a través de <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponible a través de %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toca para registrarte"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conexión sin Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"No se ha podido acceder al servidor DNS privado"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Conexión limitada"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sin Internet"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index df3b792..b6c112b 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Ühendatud võrgu <xliff:g id="NAME">%1$s</xliff:g> kaudu"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Saadaval üksuse %1$s kaudu"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Puudutage registreerumiseks"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Ühendatud, Interneti-ühendus puudub"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Privaatsele DNS-serverile ei pääse juurde"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Piiratud ühendus"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Interneti-ühendus puudub"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index fcb320f..4fd9add 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> bidez konektatuta"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s bidez erabilgarri"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Sakatu erregistratzeko"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Konektatuta; ezin da atzitu Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Ezin da atzitu DNS zerbitzari pribatua"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Konexio mugatua"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Ez dago Interneteko konexiorik"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 261a438..32a98be 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"متصل شده ازطریق <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"در دسترس از طریق %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"برای ثبتنام ضربه بزنید"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"متصل، بدون اینترنت"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"سرور DNS خصوصی قابل دسترسی نیست"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"اتصال محدود"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"عدم دسترسی به اینترنت"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 4ccf430..84c5372 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Yhdistetty (<xliff:g id="NAME">%1$s</xliff:g>)"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Käytettävissä seuraavan kautta: %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Rekisteröidy napauttamalla"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Yhdistetty, ei internetyhteyttä"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Ei pääsyä yksityiselle DNS-palvelimelle"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Rajallinen yhteys"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Ei internetyhteyttä"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 84a9797..995eab6 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connecté sur le réseau <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Accessible par %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toucher pour vous connecter"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connecté, aucun accès à Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Impossible d\'accéder au serveur DNS privé"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Connexion limitée"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Aucune connexion Internet"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 030a7f9..2afacab 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connecté via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponible via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Appuyez ici pour vous connecter"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connecté, aucun accès à Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Impossible d\'accéder au serveur DNS privé"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Connexion limitée"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Aucun accès à Internet"</string>
@@ -57,7 +58,7 @@
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Inscription terminée. Connexion…"</string>
<string name="speed_label_very_slow" msgid="8526005255731597666">"Très lente"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lente"</string>
- <string name="speed_label_okay" msgid="1253594383880810424">"Correct"</string>
+ <string name="speed_label_okay" msgid="1253594383880810424">"Correcte"</string>
<string name="speed_label_medium" msgid="9078405312828606976">"Moyenne"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Élevée"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Très rapide"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index af033cf..a106e60 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Wifi conectada a través de <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Dispoñible a través de %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toca para rexistrarte"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conexión sen Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Non se puido acceder ao servidor DNS privado"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Pouca conexión"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Non hai conexión a Internet"</string>
@@ -316,7 +317,7 @@
<string name="usb_audio_disable_routing" msgid="3367656923544254975">"Desactivar encamiñamento audio USB"</string>
<string name="usb_audio_disable_routing_summary" msgid="8768242894849534699">"Desactiva o encamiñamento automático a periféricos de audio USB"</string>
<string name="debug_layout" msgid="1659216803043339741">"Mostrar límites de deseño"</string>
- <string name="debug_layout_summary" msgid="8825829038287321978">"Mostra os límites dos clips, as marxes, etc."</string>
+ <string name="debug_layout_summary" msgid="8825829038287321978">"Mostra os límites dos clips, as marxes etc."</string>
<string name="force_rtl_layout_all_locales" msgid="8690762598501599796">"Forzar dirección do deseño RTL"</string>
<string name="force_rtl_layout_all_locales_summary" msgid="6663016859517239880">"Forza a dirección de pantalla a RTL (dereita a esquerda) para todas as configuración rexionais"</string>
<string name="force_msaa" msgid="4081288296137775550">"Forzar MSAA 4x"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 87fd876..5f2d5cd 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા કનેક્ટ થયેલ"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s દ્વારા ઉપલબ્ધ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"સાઇન અપ કરવા માટે ટૅપ કરો"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"કનેક્ટ કર્યું, કોઈ ઇન્ટરનેટ નથી"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"ખાનગી DNS સર્વર ઍક્સેસ કરી શકાતા નથી"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"મર્યાદિત કનેક્શન"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ઇન્ટરનેટ ઍક્સેસ નથી"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 02b5c96..d60eead 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> के ज़रिए कनेक्ट किया गया"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s के द्वारा उपलब्ध"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"साइन अप करने के लिए टैप करें"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"कनेक्ट हो गया है, लेकिन इंटरनेट नहीं है"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"निजी डीएनएस सर्वर को ऐक्सेस नहीं किया जा सकता"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"सीमित कनेक्शन"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"इंटरनेट कनेक्शन नहीं है"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 598cfe2..28e3460 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Povezan putem mreže <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Dostupno putem %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Dodirnite da biste se registrirali"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Povezano, bez interneta"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Nije moguće pristupiti privatnom DNS poslužitelju"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ograničena veza"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nema interneta"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index d970c73..22e03a7 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Kapcsolódva a következőn keresztül: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Elérhető a következőn keresztül: %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Koppintson a regisztrációhoz"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Csatlakozva, nincs internet-hozzáférés"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"A privát DNS-kiszolgálóhoz nem lehet hozzáférni"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Korlátozott kapcsolat"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nincs internetkapcsolat"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 9086934..ecb615a 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Միացված է <xliff:g id="NAME">%1$s</xliff:g>-ի միջոցով"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Հասանելի է %1$s-ի միջոցով"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Հպեք՝ գրանցվելու համար"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Միացված է, սակայն ինտերնետ կապ չկա"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Մասնավոր DNS սերվերն անհասանելի է"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Սահմանափակ կապ"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Ինտերնետ կապ չկա"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 3c1504c..cfbda04 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Tersambung melalui <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Tersedia melalui %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Ketuk untuk mendaftar"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Tersambung, tidak ada internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Server DNS pribadi tidak dapat diakses"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Koneksi terbatas"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Tidak ada internet"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 438e900..ba4c009 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Tenging í gegnum <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Í boði í gegnum %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Ýttu til að skrá þig"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Tengt, enginn netaðgangur"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Ekki næst í DNS-einkaþjón"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Takmörkuð tenging"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Engin nettenging"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 114b33b..50e5777 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Connesso tramite <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponibile tramite %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tocca per registrarti"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connesso, senza Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Non è possibile accedere al server DNS privato"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Connessione limitata"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nessuna connessione a Internet"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 62085a8..9f1e457 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"מחוברת באמצעות <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"זמינה דרך %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"יש להקיש כדי להירשם"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"מחובר. אין אינטרנט"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"לא ניתן לגשת לשרת DNS הפרטי"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"חיבור מוגבל"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"אין אינטרנט"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 28b98ee..47020ed 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> で接続しました"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s経由で使用可能"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"タップして登録してください"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"接続済み、インターネット接続なし"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"プライベート DNS サーバーにアクセスできません"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"接続が制限されています"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"インターネット未接続"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 6f5d0b3..116488d 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"დაკავშირებულია <xliff:g id="NAME">%1$s</xliff:g>-ით"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"ხელმისაწვდომია %1$s-ით"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"შეეხეთ რეგისტრაციისთვის"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"დაკავშირებულია, ინტერნეტის გარეშე"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"პირად DNS სერვერზე წვდომა შეუძლებელია"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"შეზღუდული კავშირი"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ინტერნეტ-კავშირი არ არის"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 3fe426e..3c4fdb7 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> арқылы жалғанған"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s арқылы қолжетімді"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Тіркелу үшін түртіңіз."</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Қосылған, интернет жоқ"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Жеке DNS серверіне кіру мүмкін емес."</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Шектеулі байланыс"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Интернетпен байланыс жоқ"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 24bfa35..87a4f19 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"ភ្ជាប់តាម <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"មានតាមរយៈ %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ចុចដើម្បីចុះឈ្មោះ"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"បានភ្ជាប់ ប៉ុន្តែគ្មានអ៊ីនធឺណិតទេ"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"មិនអាចចូលប្រើម៉ាស៊ីនមេ DNS ឯកជនបានទេ"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"ការតភ្ជាប់មានកម្រិត"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"គ្មានអ៊ីនធឺណិតទេ"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 0699bbc..144dddb 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ಆ್ಯಪ್ ಮೂಲಕ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s ಮೂಲಕ ಲಭ್ಯವಿದೆ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ಸೈನ್ ಅಪ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ, ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"ಖಾಸಗಿ DNS ಸರ್ವರ್ ಅನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"ಸೀಮಿತ ಸಂಪರ್ಕ"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 25e9cfe..df9f21d 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g>을(를) 통해 연결됨"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s을(를) 통해 사용 가능"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"탭하여 가입"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"연결됨, 인터넷 사용 불가"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"비공개 DNS 서버에 액세스할 수 없습니다."</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"제한된 연결"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"인터넷 연결 없음"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 3dfce1e..13c4144 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> аркылуу туташты"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s аркылуу жеткиликтүү"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Катталуу үчүн таптап коюңуз"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Туташып турат, Интернет жок"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Жеке DNS сервери жеткиликсиз"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Байланыш чектелген"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Интернет жок"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 48e5093..48224f4 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"ເຊື່ອມຕໍ່ຜ່ານ <xliff:g id="NAME">%1$s</xliff:g> ແລ້ວ"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"ມີໃຫ້ຜ່ານ %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ແຕະເພື່ອສະໝັກ"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ເຊື່ອມຕໍ່ແລ້ວ, ບໍ່ມີອິນເຕີເນັດ"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"ບໍ່ສາມາດເຂົ້າເຖິງເຊີບເວີ DNS ສ່ວນຕົວໄດ້"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"ການເຊື່ອມຕໍ່ຈຳກັດ"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ບໍ່ມີອິນເຕີເນັດ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 8b3fbad..d080334 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Prisijungta naudojant programą „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Pasiekiama naudojant „%1$s“"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Palieskite, kad prisiregistruotumėte"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Prisijungta, nėra interneto"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Privataus DNS serverio negalima pasiekti"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ribotas ryšys"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nėra interneto ryšio"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 4fc5b22..e13a50d 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Savienojums ar <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Pieejams, izmantojot %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Pieskarieties, lai reģistrētos"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Savienojums izveidots, nav piekļuves internetam"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Nevar piekļūt privātam DNS serverim."</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ierobežots savienojums"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nav piekļuves internetam"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 288e526..d027ffe 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Поврзано преку <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Достапно преку %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Допрете за да се регистрирате"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Поврзана, нема интернет"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Не може да се пристапи до приватниот DNS-сервер"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ограничена врска"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Нема интернет"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index e775297..e2a3d9f 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> മുഖേന കണക്റ്റ് ചെയ്തു"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s വഴി ലഭ്യം"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"സൈൻ അപ്പ് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"കണക്റ്റ് ചെയ്തു, ഇന്റർനെറ്റ് ഇല്ല"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"സ്വകാര്യ DNS സെർവർ ആക്സസ് ചെയ്യാനാവില്ല"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"പരിമിത കണക്ഷൻ"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ഇന്റർനെറ്റ് ഇല്ല"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index ba69f9b..dd08c9e 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g>-р холбогдсон"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s-р боломжтой"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Бүртгүүлэхийн тулд товшино уу"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Холбогдсон хэдий ч интернет алга"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Хувийн DNS серверт хандах боломжгүй байна"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Хязгаарлагдмал холболт"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Интернэт алга"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 1930cdf..fb7cd1f 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> द्वारे कनेक्ट केले"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s द्वारे उपलब्ध"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"साइन अप करण्यासाठी टॅप करा"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"कनेक्ट केले, इंटरनेट नाही"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"खाजगी DNS सर्व्हर ॲक्सेस करू शकत नाही"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"मर्यादित कनेक्शन"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"इंटरनेट नाही"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index e493188..72b15ff 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Disambungkan melalui <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Tersedia melalui %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Ketik untuk daftar"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Disambungkan, tiada Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Pelayan DNS peribadi tidak boleh diakses"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Sambungan terhad"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Tiada Internet"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 2c4b32c..24017ec 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s မှတစ်ဆင့်ရနိုင်သည်"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"အကောင့်ဖွင့်ရန် တို့ပါ"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ချိတ်ဆက်ထားသည်၊ အင်တာနက်မရှိ"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"သီးသန့် ဒီအန်အက်စ် (DNS) ဆာဗာကို သုံး၍မရပါ။"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"ချိတ်ဆက်မှု ကန့်သတ်ထားသည်"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"အင်တာနက် မရှိပါ"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 093c06f..aca13a7 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Tilkoblet via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Tilgjengelig via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Trykk for å registrere deg"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Tilkoblet – ingen Internett-tilgang"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Den private DNS-tjeneren kan ikke nås"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Begrenset tilkobling"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Ingen internettilkobling"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index fb8b737..b2c7518 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> मार्फत जडान गरिएको"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s मार्फत उपलब्ध"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"साइन अप गर्न ट्याप गर्नुहोस्"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"जडान गरियो तर इन्टरनेट छैन"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"निजी DNS सर्भरमाथि पहुँच प्राप्त गर्न सकिँदैन"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"सीमित जडान"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"इन्टरनेटमाथिको पहुँच छैन"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 267dab4..c74683b 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Verbonden via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Beschikbaar via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tik om aan te melden"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Verbonden, geen internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Geen toegang tot privé-DNS-server"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Beperkte verbinding"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Geen internet"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index d8ae3bf..704fc42 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ଦ୍ବାରା ସଂଯୋଗ କରାଯାଇଛି"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s ମାଧ୍ୟମରେ ଉପଲବ୍ଧ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ସାଇନ୍ ଅପ୍ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ସଂଯୁକ୍ତ, ଇଣ୍ଟର୍ନେଟ୍ ନାହିଁ"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"ବ୍ୟକ୍ତିଗତ DNS ସର୍ଭର୍ ଆକ୍ସେସ୍ କରିହେବ ନାହିଁ"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"ସୀମିତ ସଂଯୋଗ"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"କୌଣସି ଇଣ୍ଟରନେଟ୍ ନାହିଁ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 6a78486..bf5a32c 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s ਰਾਹੀਂ ਉਪਲਬਧ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ਸਾਈਨ-ਅੱਪ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ਕਨੈਕਟ ਕੀਤਾ, ਕੋਈ ਇੰਟਰਨੈੱਟ ਨਹੀਂ"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"ਨਿੱਜੀ ਡੋਮੇਨ ਨਾਮ ਪ੍ਰਣਾਲੀ (DNS) ਸਰਵਰ \'ਤੇ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"ਸੀਮਤ ਕਨੈਕਸ਼ਨ"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ਇੰਟਰਨੈੱਟ ਨਹੀਂ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 8c5547c..d907760 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Połączenie przez: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Dostępne przez %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Kliknij, by się zarejestrować"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Połączono, brak internetu"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Brak dostępu do prywatnego serwera DNS"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ograniczone połączenie"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Brak internetu"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 8c03616..e61c84e 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Conectado via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponível via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toque para se inscrever"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conectada, sem Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Não é possível acessar o servidor DNS privado"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Conexão limitada"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sem Internet"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 6aeff1c..8ceeb63 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Ligado via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponível através de %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toque para se inscrever"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Ligado, sem Internet."</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Não é possível aceder ao servidor DNS."</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ligação limitada"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sem Internet"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 8c03616..e61c84e 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Conectado via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponível via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toque para se inscrever"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conectada, sem Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Não é possível acessar o servidor DNS privado"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Conexão limitada"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sem Internet"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 387441f..5ad8dfd 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Conectat prin <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Disponibilă prin %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Atingeți pentru a vă înscrie"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conectată, fără internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Serverul DNS privat nu poate fi accesat"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Conexiune limitată"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Fără conexiune la internet"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 361e29f..104efdc 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Подключено через приложение \"<xliff:g id="NAME">%1$s</xliff:g>\"."</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Доступно через %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Нажмите, чтобы зарегистрироваться"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Подключено, без доступа к Интернету"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Доступа к частному DNS-серверу нет."</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Подключение к сети ограничено."</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Нет подключения к Интернету"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index faa848f..f5dad87 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> හරහා සම්බන්ධයි"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s හරහා ලබා ගැනීමට හැකිය"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ලියාපදිංචි වීමට තට්ටු කරන්න"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"සම්බන්ධයි, අන්තර්ජාලය නැත"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"පුද්ගලික DNS සේවාදායකයට ප්රවේශ වීමට නොහැකිය"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"සීමිත සම්බන්ධතාව"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"අන්තර්ජාලය නැත"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 2035d88c..527dafb 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Pripojené prostredníctvom siete <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"K dispozícii prostredníctvom %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Prihláste sa klepnutím"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Pripojené, žiadny internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"K súkromnému serveru DNS sa nepodarilo získať prístup"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Obmedzené pripojenie"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Žiadny internet"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 2889619..4816950 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Povezava vzpostavljena prek omrežja <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Na voljo prek: %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Dotaknite se, če se želite registrirati"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Vzpostavljena povezava, brez interneta"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Do zasebnega strežnika DNS ni mogoče dostopati"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Omejena povezava"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Brez internetne povezave"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index ccd4e30..12511dd3 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Lidhur përmes <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"E mundshme përmes %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Trokit për t\'u regjistruar"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"U lidh, por nuk ka internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Serveri privat DNS nuk mund të qaset"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Lidhje e kufizuar"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nuk ka internet"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 08e2bc8..4724be5 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Повезано преко: <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Доступна је преко приступне тачке %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Додирните да бисте се регистровали"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Веза је успостављена, нема интернета"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Приступ приватном DNS серверу није успео"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Ограничена веза"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Нема интернета"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index c0cdbc9..9d1bc42 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Anslutet via <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Tillgängligt via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tryck för att logga in"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Ansluten, inget internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Det går inte att komma åt den privata DNS-servern."</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Begränsad anslutning"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Inget internet"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index f00dea3..1e2489f 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Imeunganishwa kupitia <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Inapatikana kupitia %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Gusa ili ujisajili"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Imeunganishwa, hakuna intaneti"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Seva ya faragha ya DNS haiwezi kufikiwa"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Muunganisho hafifu"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Hakuna intaneti"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 52e0363..fcb801b 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> மூலம் இணைக்கப்பட்டது"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s வழியாகக் கிடைக்கிறது"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"பதிவு செய்யத் தட்டவும்"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"இணைக்கப்பட்டுள்ளது, ஆனால் இண்டர்நெட் இல்லை"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"தனிப்பட்ட DNS சேவையகத்தை அணுக இயலாது"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"வரம்பிற்கு உட்பட்ட இணைப்பு"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"இணைய இணைப்பு இல்லை"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index a39c4e1..7a80451 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ద్వారా కనెక్ట్ చేయబడింది"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s ద్వారా అందుబాటులో ఉంది"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"సైన్ అప్ చేయడానికి నొక్కండి"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"కనెక్ట్ చేయబడింది, ఇంటర్నెట్ లేదు"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"ప్రైవేట్ DNS సర్వర్ను యాక్సెస్ చేయడం సాధ్యపడదు"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"పరిమిత కనెక్షన్"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ఇంటర్నెట్ లేదు"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 635d77a..130e1c4 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"เชื่อมต่อแล้วผ่าน <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"พร้อมใช้งานผ่านทาง %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"แตะเพื่อลงชื่อสมัครใช้"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"เชื่อมต่อแล้ว ไม่พบอินเทอร์เน็ต"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"เข้าถึงเซิร์ฟเวอร์ DNS ไม่ได้"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"การเชื่อมต่อที่จำกัด"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ไม่มีอินเทอร์เน็ต"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 3f7f0ff..bc1a405 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Nakakonekta sa pamamagitan ng <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Available sa pamamagitan ng %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"I-tap para mag-sign up"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Nakakonekta, walang internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Hindi ma-access ang pribadong DNS server"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Limitadong koneksyon"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Walang internet"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 7ad6fcd..4e263ef 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ile bağlandı"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s üzerinden kullanılabilir"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Kaydolmak için dokunun"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Bağlı, internet yok"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Gizli DNS sunucusuna erişilemiyor"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Sınırlı bağlantı"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"İnternet yok"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index b5dd618..ad9f600 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Підключено через додаток <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Доступ через %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Торкніться, щоб увійти"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Під’єднано, але немає доступу до Інтернету"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Немає доступу до приватного DNS-сервера"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Обмежене з’єднання"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Немає Інтернету"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index ef9b2a1..b995da1 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> کے ذریعے منسلک"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"دستیاب بذریعہ %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"سائن اپ کے لیے تھپتھپائیں"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"منسلک، انٹرنیٹ نہیں ہے"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"نجی DNS سرور تک رسائی حاصل نہیں کی جا سکی"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"محدود کنکشن"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"انٹرنیٹ نہیں ہے"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index b202d64..f02b639 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> orqali ulandi"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"%1$s orqali ishlaydi"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Yozilish uchun bosing"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Ulangan, lekin internet aloqasi yo‘q"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Xususiy DNS server ishlamayapti"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Cheklangan aloqa"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Internet yo‘q"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 5bcff5a..8fcf1f2 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Đã kết nối qua <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Có sẵn qua %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Nhấn để đăng ký"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Đã kết nối, không có Internet"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Không thể truy cập máy chủ DNS riêng tư"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Kết nối giới hạn"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Không có Internet"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 418370b..755cbee 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"已通过<xliff:g id="NAME">%1$s</xliff:g>连接到网络"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"可通过%1$s连接"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"点按即可注册"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"已连接,但无法访问互联网"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"无法访问私人 DNS 服务器"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"网络连接受限"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"无法访问互联网"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 79b5579..e50acf5 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"已透過「<xliff:g id="NAME">%1$s</xliff:g>」連線"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"可透過 %1$s 連線"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"輕按即可登入"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"已連線,但沒有互聯網"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"無法存取私人 DNS 伺服器"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"連線受限"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"沒有互聯網連線"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 47ab764..7df6fed 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"透過「<xliff:g id="NAME">%1$s</xliff:g>」連線"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"可透過 %1$s 使用"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"輕觸即可註冊"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"已連線,沒有網際網路"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"無法存取私人 DNS 伺服器"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"連線能力受限"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"沒有網際網路連線"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 87f45de..46dffbe 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -42,7 +42,8 @@
<string name="connected_via_app" msgid="3532267661404276584">"Ixhumeke nge-<xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="available_via_passpoint" msgid="1716000261192603682">"Iyatholakala nge-%1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Thepha ukuze ubhalisele"</string>
- <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Kuxhunyiwe, ayikho i-inthanethi"</string>
+ <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+ <skip />
<string name="private_dns_broken" msgid="1984159464346556931">"Iseva eyimfihlo ye-DNS ayikwazi ukufinyelelwa"</string>
<string name="wifi_limited_connection" msgid="1184778285475204682">"Iqoqo elikhawulelwe"</string>
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Ayikho i-inthanethi"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a7df6db..dd21e5c 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -129,7 +129,7 @@
<string name="certinstaller_package" translatable="false">com.android.certinstaller</string>
<!-- Summary for Connected wifi network without internet -->
- <string name="wifi_connected_no_internet">Connected, no internet</string>
+ <string name="wifi_connected_no_internet">No internet</string>
<!-- Summary for connected network without internet due to private dns validation failed [CHAR LIMIT=NONE] -->
<string name="private_dns_broken">Private DNS server cannot be accessed</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index aac7fc3..d48aa24 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -402,7 +402,7 @@
private static List<String> extractTimeZoneIds(List<TimeZoneMapping> timeZoneMappings) {
final List<String> zoneIds = new ArrayList<>(timeZoneMappings.size());
for (TimeZoneMapping timeZoneMapping : timeZoneMappings) {
- zoneIds.add(timeZoneMapping.timeZoneId);
+ zoneIds.add(timeZoneMapping.getTimeZoneId());
}
return Collections.unmodifiableList(zoneIds);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index e008cd03..a4be46c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -40,15 +40,16 @@
final Executor mExecutor = Executors.newSingleThreadExecutor();
@VisibleForTesting
MediaRouter2Manager mRouterManager;
+ @VisibleForTesting
+ String mPackageName;
- private String mPackageName;
private MediaDevice mCurrentConnectedDevice;
public InfoMediaManager(Context context, String packageName, Notification notification) {
super(context, notification);
mRouterManager = MediaRouter2Manager.getInstance(context);
- if (packageName != null) {
+ if (!TextUtils.isEmpty(packageName)) {
mPackageName = packageName;
}
}
@@ -57,6 +58,7 @@
public void startScan() {
mMediaDevices.clear();
mRouterManager.registerCallback(mExecutor, mMediaRouterCallback);
+ refreshDevices();
}
@VisibleForTesting
@@ -79,21 +81,37 @@
return mCurrentConnectedDevice;
}
- class RouterManagerCallback extends MediaRouter2Manager.Callback {
-
- private void refreshDevices() {
- mMediaDevices.clear();
- mCurrentConnectedDevice = null;
- for (MediaRoute2Info route : mRouterManager.getAvailableRoutes(mPackageName)) {
- final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, route,
- mPackageName);
- if (TextUtils.equals(route.getClientPackageName(), mPackageName)) {
- mCurrentConnectedDevice = device;
- }
- mMediaDevices.add(device);
- }
- dispatchDeviceListAdded();
+ private void refreshDevices() {
+ mMediaDevices.clear();
+ mCurrentConnectedDevice = null;
+ if (TextUtils.isEmpty(mPackageName)) {
+ buildAllRoutes();
+ } else {
+ buildAvailableRoutes();
}
+ dispatchDeviceListAdded();
+ }
+
+ private void buildAllRoutes() {
+ for (MediaRoute2Info route : mRouterManager.getAllRoutes()) {
+ final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, route,
+ mPackageName);
+ mMediaDevices.add(device);
+ }
+ }
+
+ private void buildAvailableRoutes() {
+ for (MediaRoute2Info route : mRouterManager.getAvailableRoutes(mPackageName)) {
+ final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, route,
+ mPackageName);
+ if (TextUtils.equals(route.getClientPackageName(), mPackageName)) {
+ mCurrentConnectedDevice = device;
+ }
+ mMediaDevices.add(device);
+ }
+ }
+
+ class RouterManagerCallback extends MediaRouter2Manager.Callback {
@Override
public void onRoutesAdded(List<MediaRoute2Info> routes) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index e85a102..50196d2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -18,7 +18,6 @@
import android.app.Notification;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
-import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.IntDef;
@@ -162,10 +161,8 @@
mMediaDevices.clear();
mBluetoothMediaManager.registerCallback(mMediaDeviceCallback);
mBluetoothMediaManager.startScan();
- if (!TextUtils.isEmpty(mPackageName)) {
- mInfoMediaManager.registerCallback(mMediaDeviceCallback);
- mInfoMediaManager.startScan();
- }
+ mInfoMediaManager.registerCallback(mMediaDeviceCallback);
+ mInfoMediaManager.startScan();
}
private void addPhoneDeviceIfNecessary() {
@@ -208,10 +205,8 @@
public void stopScan() {
mBluetoothMediaManager.unregisterCallback(mMediaDeviceCallback);
mBluetoothMediaManager.stopScan();
- if (!TextUtils.isEmpty(mPackageName)) {
- mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
- mInfoMediaManager.stopScan();
- }
+ mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
+ mInfoMediaManager.stopScan();
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 84dde05..44e70f4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -654,7 +654,7 @@
}
}
}
- return oldMetering == mIsScoredNetworkMetered;
+ return oldMetering != mIsScoredNetworkMetered;
}
/**
@@ -1170,8 +1170,8 @@
} else { // In range, not disabled.
if (mConfig != null) { // Is saved network
// Last attempt to connect to this failed. Show reason why
- switch (mConfig.recentFailure.getAssociationStatus()) {
- case WifiConfiguration.RecentFailure.STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
+ switch (mConfig.getRecentFailureReason()) {
+ case WifiConfiguration.RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA:
summary.append(mContext.getString(
R.string.wifi_ap_unable_to_handle_new_sta));
break;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 67f6dd90..3726fb2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -74,7 +74,7 @@
}
@Test
- public void onRouteAdded_shouldAddMediaDevice() {
+ public void onRouteAdded_getAvailableRoutes_shouldAddMediaDevice() {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
@@ -95,6 +95,27 @@
}
@Test
+ public void onRouteAdded_buildAllRoutes_shouldAddMediaDevice() {
+ final MediaRoute2Info info = mock(MediaRoute2Info.class);
+ when(info.getId()).thenReturn(TEST_ID);
+ when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+ final List<MediaRoute2Info> routes = new ArrayList<>();
+ routes.add(info);
+ when(mRouterManager.getAllRoutes()).thenReturn(routes);
+
+ final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
+ assertThat(mediaDevice).isNull();
+
+ mInfoMediaManager.mPackageName = "";
+ mInfoMediaManager.mMediaRouterCallback.onRoutesAdded(routes);
+
+ final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
+ assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
+ assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
+ }
+
+ @Test
public void onControlCategoriesChanged_samePackageName_shouldAddMediaDevice() {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index cdf9728..3c52f54 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -45,6 +45,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.BackupUtils;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Slog;
import android.view.Display;
@@ -280,6 +281,16 @@
Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
Set<String> movedToSecure = getMovedToSecureSettings();
+ Set<String> preservedGlobalSettings = getSettingsToPreserveInRestore(
+ Settings.Global.CONTENT_URI);
+ Set<String> preservedSecureSettings = getSettingsToPreserveInRestore(
+ Settings.Secure.CONTENT_URI);
+ Set<String> preservedSystemSettings = getSettingsToPreserveInRestore(
+ Settings.System.CONTENT_URI);
+ Set<String> preservedSettings = new HashSet<>(preservedGlobalSettings);
+ preservedSettings.addAll(preservedSecureSettings);
+ preservedSettings.addAll(preservedSystemSettings);
+
byte[] restoredWifiSupplicantData = null;
byte[] restoredWifiIpConfigData = null;
@@ -300,7 +311,8 @@
case KEY_SYSTEM :
restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal,
movedToSecure, R.array.restore_blocked_system_settings,
- dynamicBlockList);
+ dynamicBlockList,
+ preservedSystemSettings);
mSettingsHelper.applyAudioSettings();
break;
@@ -311,7 +323,8 @@
movedToGlobal,
null,
R.array.restore_blocked_secure_settings,
- dynamicBlockList);
+ dynamicBlockList,
+ preservedSecureSettings);
break;
case KEY_GLOBAL :
@@ -321,7 +334,8 @@
null,
movedToSecure,
R.array.restore_blocked_global_settings,
- dynamicBlockList);
+ dynamicBlockList,
+ preservedGlobalSettings);
break;
case KEY_WIFI_SUPPLICANT :
@@ -368,7 +382,8 @@
restoreDeviceSpecificConfig(
restoredDeviceSpecificConfig,
R.array.restore_blocked_device_specific_settings,
- dynamicBlockList);
+ dynamicBlockList,
+ preservedSettings);
break;
default :
@@ -418,7 +433,7 @@
in.readFully(buffer, 0, nBytes);
restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal,
movedToSecure, R.array.restore_blocked_system_settings,
- Collections.emptySet());
+ Collections.emptySet(), Collections.emptySet());
// secure settings
nBytes = in.readInt();
@@ -432,7 +447,7 @@
movedToGlobal,
null,
R.array.restore_blocked_secure_settings,
- Collections.emptySet());
+ Collections.emptySet(), Collections.emptySet());
// Global only if sufficiently new
if (version >= FULL_BACKUP_ADDED_GLOBAL) {
@@ -443,7 +458,7 @@
movedToGlobal.clear(); // no redirection; this *is* the global namespace
restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI, movedToGlobal,
movedToSecure, R.array.restore_blocked_global_settings,
- Collections.emptySet());
+ Collections.emptySet(), Collections.emptySet());
}
// locale
@@ -608,6 +623,40 @@
}
/**
+ * Get names of the settings for which the current value should be preserved during restore.
+ */
+ private Set<String> getSettingsToPreserveInRestore(Uri settingsUri) {
+ if (!FeatureFlagUtils.isEnabled(getApplicationContext(),
+ FeatureFlagUtils.SETTINGS_DO_NOT_RESTORE_PRESERVED)) {
+ return Collections.emptySet();
+ }
+
+ Cursor cursor = getContentResolver().query(settingsUri, new String[] {
+ Settings.NameValueTable.NAME, Settings.NameValueTable.IS_PRESERVED_IN_RESTORE },
+ /* selection */ null, /* selectionArgs */ null, /* sortOrder */ null);
+
+ if (!cursor.moveToFirst()) {
+ Slog.i(TAG, "No settings to be preserved in restore");
+ return Collections.emptySet();
+ }
+
+ int nameIndex = cursor.getColumnIndex(Settings.NameValueTable.NAME);
+ int isPreservedIndex = cursor.getColumnIndex(
+ Settings.NameValueTable.IS_PRESERVED_IN_RESTORE);
+
+ Set<String> preservedSettings = new HashSet<>();
+ while (!cursor.isAfterLast()) {
+ if (Boolean.parseBoolean(cursor.getString(isPreservedIndex))) {
+ preservedSettings.add(getQualifiedKeyForSetting(cursor.getString(nameIndex),
+ settingsUri));
+ }
+ cursor.moveToNext();
+ }
+
+ return preservedSettings;
+ }
+
+ /**
* Serialize the owner info and other lock settings
*/
private byte[] getLockSettings(@UserIdInt int userId) {
@@ -650,7 +699,8 @@
HashSet<String> movedToGlobal,
Set<String> movedToSecure,
int blockedSettingsArrayId,
- Set<String> dynamicBlockList) {
+ Set<String> dynamicBlockList,
+ Set<String> settingsToPreserve) {
byte[] settings = new byte[data.getDataSize()];
try {
data.readEntityData(settings, 0, settings.length);
@@ -665,7 +715,8 @@
movedToGlobal,
movedToSecure,
blockedSettingsArrayId,
- dynamicBlockList);
+ dynamicBlockList,
+ settingsToPreserve);
}
private void restoreSettings(
@@ -675,7 +726,8 @@
HashSet<String> movedToGlobal,
Set<String> movedToSecure,
int blockedSettingsArrayId,
- Set<String> dynamicBlockList) {
+ Set<String> dynamicBlockList,
+ Set<String> settingsToPreserve) {
restoreSettings(
settings,
0,
@@ -684,10 +736,12 @@
movedToGlobal,
movedToSecure,
blockedSettingsArrayId,
- dynamicBlockList);
+ dynamicBlockList,
+ settingsToPreserve);
}
- private void restoreSettings(
+ @VisibleForTesting
+ void restoreSettings(
byte[] settings,
int pos,
int bytes,
@@ -695,31 +749,13 @@
HashSet<String> movedToGlobal,
Set<String> movedToSecure,
int blockedSettingsArrayId,
- Set<String> dynamicBlockList) {
+ Set<String> dynamicBlockList,
+ Set<String> settingsToPreserve) {
if (DEBUG) {
Log.i(TAG, "restoreSettings: " + contentUri);
}
- // Figure out the white list and redirects to the global table. We restore anything
- // in either the backup whitelist or the legacy-restore whitelist for this table.
- final String[] whitelist;
- Map<String, Validator> validators = null;
- if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
- whitelist = ArrayUtils.concatElements(String.class, SecureSettings.SETTINGS_TO_BACKUP,
- Settings.Secure.LEGACY_RESTORE_SETTINGS,
- DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
- validators = SecureSettingsValidators.VALIDATORS;
- } else if (contentUri.equals(Settings.System.CONTENT_URI)) {
- whitelist = ArrayUtils.concatElements(String.class, SystemSettings.SETTINGS_TO_BACKUP,
- Settings.System.LEGACY_RESTORE_SETTINGS);
- validators = SystemSettingsValidators.VALIDATORS;
- } else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
- whitelist = ArrayUtils.concatElements(String.class, GlobalSettings.SETTINGS_TO_BACKUP,
- Settings.Global.LEGACY_RESTORE_SETTINGS);
- validators = GlobalSettingsValidators.VALIDATORS;
- } else {
- throw new IllegalArgumentException("Unknown URI: " + contentUri);
- }
+ SettingsBackupWhitelist whitelist = getBackupWhitelist(contentUri);
// Restore only the white list data.
final ArrayMap<String, String> cachedEntries = new ArrayMap<>();
@@ -729,7 +765,7 @@
Set<String> blockedSettings = getBlockedSettings(blockedSettingsArrayId);
- for (String key : whitelist) {
+ for (String key : whitelist.mSettingsWhitelist) {
boolean isBlockedBySystem = blockedSettings != null && blockedSettings.contains(key);
if (isBlockedBySystem || isBlockedByDynamicList(dynamicBlockList, contentUri, key)) {
Log.i(
@@ -742,6 +778,12 @@
continue;
}
+ if (settingsToPreserve.contains(getQualifiedKeyForSetting(key, contentUri))) {
+ Log.i(TAG, "Skipping restore for setting " + key + " as it is marked as "
+ + "preserved");
+ continue;
+ }
+
String value = null;
boolean hasValueToRestore = false;
if (cachedEntries.indexOfKey(key) >= 0) {
@@ -775,7 +817,7 @@
}
// only restore the settings that have valid values
- if (!isValidSettingValue(key, value, validators)) {
+ if (!isValidSettingValue(key, value, whitelist.mSettingsValidators)) {
Log.w(TAG, "Attempted restore of " + key + " setting, but its value didn't pass"
+ " validation, value: " + value);
continue;
@@ -798,11 +840,42 @@
}
}
+ @VisibleForTesting
+ SettingsBackupWhitelist getBackupWhitelist(Uri contentUri) {
+ // Figure out the white list and redirects to the global table. We restore anything
+ // in either the backup whitelist or the legacy-restore whitelist for this table.
+ String[] whitelist;
+ Map<String, Validator> validators = null;
+ if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
+ whitelist = ArrayUtils.concatElements(String.class, SecureSettings.SETTINGS_TO_BACKUP,
+ Settings.Secure.LEGACY_RESTORE_SETTINGS,
+ DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
+ validators = SecureSettingsValidators.VALIDATORS;
+ } else if (contentUri.equals(Settings.System.CONTENT_URI)) {
+ whitelist = ArrayUtils.concatElements(String.class, SystemSettings.SETTINGS_TO_BACKUP,
+ Settings.System.LEGACY_RESTORE_SETTINGS);
+ validators = SystemSettingsValidators.VALIDATORS;
+ } else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
+ whitelist = ArrayUtils.concatElements(String.class, GlobalSettings.SETTINGS_TO_BACKUP,
+ Settings.Global.LEGACY_RESTORE_SETTINGS);
+ validators = GlobalSettingsValidators.VALIDATORS;
+ } else {
+ throw new IllegalArgumentException("Unknown URI: " + contentUri);
+ }
+
+ return new SettingsBackupWhitelist(whitelist, validators);
+ }
+
private boolean isBlockedByDynamicList(Set<String> dynamicBlockList, Uri areaUri, String key) {
String contentKey = Uri.withAppendedPath(areaUri, key).toString();
return dynamicBlockList.contains(contentKey);
}
+ @VisibleForTesting
+ static String getQualifiedKeyForSetting(String settingName, Uri settingUri) {
+ return Uri.withAppendedPath(settingUri, settingName).toString();
+ }
+
// There may be other sources of blocked settings, so I'm separating out this
// code to make it easy to modify in the future.
@VisibleForTesting
@@ -1089,7 +1162,7 @@
*/
@VisibleForTesting
boolean restoreDeviceSpecificConfig(byte[] data, int blockedSettingsArrayId,
- Set<String> dynamicBlocklist) {
+ Set<String> dynamicBlocklist, Set<String> preservedSettings) {
// We're using an AtomicInteger to wrap the position int and allow called methods to
// modify it.
AtomicInteger pos = new AtomicInteger(0);
@@ -1108,7 +1181,8 @@
null,
null,
blockedSettingsArrayId,
- dynamicBlocklist);
+ dynamicBlocklist,
+ preservedSettings);
updateWindowManagerIfNeeded(originalDensity);
@@ -1240,4 +1314,20 @@
| ((in[pos + 3] & 0xFF) << 0);
return result;
}
+
+ /**
+ * Store the whitelist of settings to be backed up and validators for them.
+ */
+ @VisibleForTesting
+ static class SettingsBackupWhitelist {
+ final String[] mSettingsWhitelist;
+ final Map<String, Validator> mSettingsValidators;
+
+
+ SettingsBackupWhitelist(String[] settingsWhitelist,
+ Map<String, Validator> settingsValidators) {
+ mSettingsWhitelist = settingsWhitelist;
+ mSettingsValidators = settingsValidators;
+ }
+ }
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 874e299..c969bfd 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -180,10 +180,17 @@
private static final int MUTATION_OPERATION_UPDATE = 3;
private static final int MUTATION_OPERATION_RESET = 4;
+ private static final String[] LEGACY_SQL_COLUMNS = new String[] {
+ Settings.NameValueTable._ID,
+ Settings.NameValueTable.NAME,
+ Settings.NameValueTable.VALUE,
+ };
+
private static final String[] ALL_COLUMNS = new String[] {
Settings.NameValueTable._ID,
Settings.NameValueTable.NAME,
- Settings.NameValueTable.VALUE
+ Settings.NameValueTable.VALUE,
+ Settings.NameValueTable.IS_PRESERVED_IN_RESTORE,
};
public static final int SETTINGS_TYPE_GLOBAL = SettingsState.SETTINGS_TYPE_GLOBAL;
@@ -2353,6 +2360,10 @@
case Settings.NameValueTable.VALUE: {
values[i] = setting.getValue();
} break;
+
+ case Settings.NameValueTable.IS_PRESERVED_IN_RESTORE: {
+ values[i] = String.valueOf(setting.isValuePreservedInRestore());
+ } break;
}
}
@@ -3097,7 +3108,7 @@
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(table);
- Cursor cursor = queryBuilder.query(database, ALL_COLUMNS,
+ Cursor cursor = queryBuilder.query(database, LEGACY_SQL_COLUMNS,
null, null, null, null, null);
if (cursor == null) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index db18213..9934e59 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -41,13 +41,13 @@
import android.util.Base64;
import android.util.Slog;
import android.util.SparseIntArray;
-import android.util.StatsLog;
import android.util.TimeUtils;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
import libcore.io.IoUtils;
@@ -429,8 +429,9 @@
mSettings.put(name, newState);
}
- StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newState.value, oldValue, tag,
- makeDefault, getUserIdFromKey(mKey), StatsLog.SETTING_CHANGED__REASON__UPDATED);
+ FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, value, newState.value,
+ oldValue, tag, makeDefault, getUserIdFromKey(mKey),
+ FrameworkStatsLog.SETTING_CHANGED__REASON__UPDATED);
addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState);
@@ -489,9 +490,9 @@
if (key.startsWith(prefix) && !keyValues.containsKey(key)) {
Setting oldState = mSettings.remove(key);
- StatsLog.write(StatsLog.SETTING_CHANGED, key, /* value= */ "", /* newValue= */ "",
- oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
- StatsLog.SETTING_CHANGED__REASON__DELETED);
+ FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, key,
+ /* value= */ "", /* newValue= */ "", oldState.value, /* tag */ "", false,
+ getUserIdFromKey(mKey), FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED);
addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState);
changedKeys.add(key); // key was removed
}
@@ -516,9 +517,9 @@
continue;
}
- StatsLog.write(StatsLog.SETTING_CHANGED, key, value, state.value, oldValue,
- /* tag */ null, /* make default */ false,
- getUserIdFromKey(mKey), StatsLog.SETTING_CHANGED__REASON__UPDATED);
+ FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, key, value, state.value,
+ oldValue, /* tag */ null, /* make default */ false,
+ getUserIdFromKey(mKey), FrameworkStatsLog.SETTING_CHANGED__REASON__UPDATED);
addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, state);
}
@@ -544,9 +545,9 @@
Setting oldState = mSettings.remove(name);
- StatsLog.write(StatsLog.SETTING_CHANGED, name, /* value= */ "", /* newValue= */ "",
- oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
- StatsLog.SETTING_CHANGED__REASON__DELETED);
+ FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, /* value= */ "",
+ /* newValue= */ "", oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
+ FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED);
updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value,
null, oldState.defaultValue, null);
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
index e650882..f5334fb 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
@@ -32,6 +32,8 @@
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
+import android.provider.settings.validators.SettingsValidators;
+import android.provider.settings.validators.Validator;
import android.test.mock.MockContentProvider;
import android.test.mock.MockContentResolver;
@@ -43,6 +45,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
@@ -55,12 +58,24 @@
/** Tests for the SettingsHelperTest */
@RunWith(AndroidJUnit4.class)
public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
-
+ private static final Uri TEST_URI = Uri.EMPTY;
private static final String TEST_DISPLAY_DENSITY_FORCED = "123";
+ private static final String OVERRIDDEN_TEST_SETTING = "overridden_setting";
+ private static final String PRESERVED_TEST_SETTING = "preserved_setting";
+ private static final Map<String, String> DEVICE_SPECIFIC_TEST_VALUES = new HashMap<>();
private static final Map<String, String> TEST_VALUES = new HashMap<>();
+ private static final Map<String, Validator> TEST_VALUES_VALIDATORS = new HashMap<>();
static {
- TEST_VALUES.put(Settings.Secure.DISPLAY_DENSITY_FORCED, TEST_DISPLAY_DENSITY_FORCED);
+ DEVICE_SPECIFIC_TEST_VALUES.put(Settings.Secure.DISPLAY_DENSITY_FORCED,
+ TEST_DISPLAY_DENSITY_FORCED);
+
+ TEST_VALUES.put(OVERRIDDEN_TEST_SETTING, "123");
+ TEST_VALUES.put(PRESERVED_TEST_SETTING, "124");
+
+ TEST_VALUES_VALIDATORS.put(OVERRIDDEN_TEST_SETTING,
+ SettingsValidators.ANY_STRING_VALIDATOR);
+ TEST_VALUES_VALIDATORS.put(PRESERVED_TEST_SETTING, SettingsValidators.ANY_STRING_VALIDATOR);
}
private TestFriendlySettingsBackupAgent mAgentUnderTest;
@@ -83,14 +98,15 @@
byte[] settingsBackup = mAgentUnderTest.getDeviceSpecificConfiguration();
- assertEquals("Not all values backed up.", TEST_VALUES.keySet(), helper.mReadEntries);
+ assertEquals("Not all values backed up.", DEVICE_SPECIFIC_TEST_VALUES.keySet(), helper.mReadEntries);
mAgentUnderTest.restoreDeviceSpecificConfig(
settingsBackup,
R.array.restore_blocked_device_specific_settings,
+ Collections.emptySet(),
Collections.emptySet());
- assertEquals("Not all values were restored.", TEST_VALUES, helper.mWrittenValues);
+ assertEquals("Not all values were restored.", DEVICE_SPECIFIC_TEST_VALUES, helper.mWrittenValues);
}
@Test
@@ -100,12 +116,13 @@
byte[] settingsBackup = mAgentUnderTest.getDeviceSpecificConfiguration();
- assertEquals("Not all values backed up.", TEST_VALUES.keySet(), helper.mReadEntries);
- mAgentUnderTest.setBlockedSettings(TEST_VALUES.keySet().toArray(new String[0]));
+ assertEquals("Not all values backed up.", DEVICE_SPECIFIC_TEST_VALUES.keySet(), helper.mReadEntries);
+ mAgentUnderTest.setBlockedSettings(DEVICE_SPECIFIC_TEST_VALUES.keySet().toArray(new String[0]));
mAgentUnderTest.restoreDeviceSpecificConfig(
settingsBackup,
R.array.restore_blocked_device_specific_settings,
+ Collections.emptySet(),
Collections.emptySet());
assertTrue("Not all values were blocked.", helper.mWrittenValues.isEmpty());
@@ -172,9 +189,50 @@
mAgentUnderTest.restoreDeviceSpecificConfig(
data,
R.array.restore_blocked_device_specific_settings,
+ Collections.emptySet(),
Collections.emptySet()));
}
+ @Test
+ public void testOnRestore_preservedSettingsAreNotRestored() {
+ SettingsBackupAgent.SettingsBackupWhitelist whitelist =
+ new SettingsBackupAgent.SettingsBackupWhitelist(
+ new String[] { OVERRIDDEN_TEST_SETTING, PRESERVED_TEST_SETTING },
+ TEST_VALUES_VALIDATORS);
+ mAgentUnderTest.setSettingsWhitelist(whitelist);
+ mAgentUnderTest.setBlockedSettings();
+ TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+ mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+ byte[] backupData = generateBackupData(TEST_VALUES);
+ mAgentUnderTest.restoreSettings(backupData, /* pos */ 0, backupData.length, TEST_URI, new HashSet<>(),
+ Collections.emptySet(), /* blockedSettingsArrayId */ 0, Collections.emptySet(),
+ new HashSet<>(Collections.singletonList(SettingsBackupAgent.getQualifiedKeyForSetting(PRESERVED_TEST_SETTING, TEST_URI))));
+
+ assertTrue(settingsHelper.mWrittenValues.containsKey(OVERRIDDEN_TEST_SETTING));
+ assertFalse(settingsHelper.mWrittenValues.containsKey(PRESERVED_TEST_SETTING));
+ }
+
+ private byte[] generateBackupData(Map<String, String> keyValueData) {
+ int totalBytes = 0;
+ for (String key : keyValueData.keySet()) {
+ totalBytes += 2 * Integer.BYTES + key.getBytes().length
+ + keyValueData.get(key).getBytes().length;
+ }
+
+ ByteBuffer buffer = ByteBuffer.allocate(totalBytes);
+ for (String key : keyValueData.keySet()) {
+ byte[] keyBytes = key.getBytes();
+ byte[] valueBytes = keyValueData.get(key).getBytes();
+ buffer.putInt(keyBytes.length);
+ buffer.put(keyBytes);
+ buffer.putInt(valueBytes.length);
+ buffer.put(valueBytes);
+ }
+
+ return buffer.array();
+ }
+
private byte[] generateUncorruptedHeader() throws IOException {
try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
mAgentUnderTest.writeHeader(os);
@@ -219,6 +277,7 @@
private static class TestFriendlySettingsBackupAgent extends SettingsBackupAgent {
private Boolean mForcedDeviceInfoRestoreAcceptability = null;
private String[] mBlockedSettings = null;
+ private SettingsBackupWhitelist mSettingsWhitelist = null;
void setForcedDeviceInfoRestoreAcceptability(boolean value) {
mForcedDeviceInfoRestoreAcceptability = value;
@@ -228,6 +287,10 @@
mBlockedSettings = blockedSettings;
}
+ void setSettingsWhitelist(SettingsBackupWhitelist settingsWhitelist) {
+ mSettingsWhitelist = settingsWhitelist;
+ }
+
@Override
protected Set<String> getBlockedSettings(int blockedSettingsArrayId) {
return mBlockedSettings == null
@@ -241,6 +304,15 @@
? super.isSourceAcceptable(data, pos)
: mForcedDeviceInfoRestoreAcceptability;
}
+
+ @Override
+ SettingsBackupWhitelist getBackupWhitelist(Uri contentUri) {
+ if (mSettingsWhitelist == null) {
+ return super.getBackupWhitelist(contentUri);
+ }
+
+ return mSettingsWhitelist;
+ }
}
/** The TestSettingsHelper tracks which values have been backed up and/or restored. */
@@ -257,7 +329,7 @@
@Override
public String onBackupValue(String key, String value) {
mReadEntries.add(key);
- String readValue = TEST_VALUES.get(key);
+ String readValue = DEVICE_SPECIFIC_TEST_VALUES.get(key);
assert readValue != null;
return readValue;
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 7d2b85d..84d9bb6 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -219,6 +219,9 @@
<!-- Permission required for CTS test - CrossProfileAppsHostSideTest -->
<uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES"/>
+ <!-- permissions required for CTS test - PhoneStateListenerTest -->
+ <uses-permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH" />
+
<!-- Permission required for CTS test - UiModeManagerTest -->
<uses-permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/>
diff --git a/packages/SystemUI/src/com/android/systemui/DejankUtils.java b/packages/SystemUI/src/com/android/systemui/DejankUtils.java
index 97578e1..3fce55f 100644
--- a/packages/SystemUI/src/com/android/systemui/DejankUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/DejankUtils.java
@@ -61,19 +61,21 @@
|| !isMainThread() || sTemporarilyIgnoreStrictMode) {
return null;
}
+ }
- try {
- String description = binder.getInterfaceDescriptor();
+ try {
+ String description = binder.getInterfaceDescriptor();
+ synchronized (sLock) {
if (sWhitelistedFrameworkClasses.contains(description)) {
return null;
}
- } catch (RemoteException e) {
- e.printStackTrace();
}
-
- StrictMode.noteSlowCall("IPC detected on critical path: " + sBlockingIpcs.peek());
- return null;
+ } catch (RemoteException e) {
+ e.printStackTrace();
}
+
+ StrictMode.noteSlowCall("IPC detected on critical path: " + sBlockingIpcs.peek());
+ return null;
}
@Override
@@ -126,9 +128,11 @@
if (STRICT_MODE_ENABLED && sBlockingIpcs.empty()) {
synchronized (sLock) {
sBlockingIpcs.push("detectBlockingIpcs");
- try {
- runnable.run();
- } finally {
+ }
+ try {
+ runnable.run();
+ } finally {
+ synchronized (sLock) {
sBlockingIpcs.pop();
}
}
@@ -177,9 +181,11 @@
if (STRICT_MODE_ENABLED && !sTemporarilyIgnoreStrictMode) {
synchronized (sLock) {
sTemporarilyIgnoreStrictMode = true;
- try {
- runnable.run();
- } finally {
+ }
+ try {
+ runnable.run();
+ } finally {
+ synchronized (sLock) {
sTemporarilyIgnoreStrictMode = false;
}
}
@@ -196,14 +202,16 @@
if (STRICT_MODE_ENABLED && !sTemporarilyIgnoreStrictMode) {
synchronized (sLock) {
sTemporarilyIgnoreStrictMode = true;
- final T val;
- try {
- val = supplier.get();
- } finally {
+ }
+ final T val;
+ try {
+ val = supplier.get();
+ } finally {
+ synchronized (sLock) {
sTemporarilyIgnoreStrictMode = false;
}
- return val;
}
+ return val;
} else {
return supplier.get();
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index dd1856a..c9c38d3 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -30,17 +30,17 @@
import android.widget.TextView;
import com.android.internal.R;
-import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.colorextraction.drawable.ScrimDrawable;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.plugins.GlobalActionsPanelPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -59,6 +59,7 @@
private final KeyguardStateController mKeyguardStateController;
private final DeviceProvisionedController mDeviceProvisionedController;
private final ExtensionController.Extension<GlobalActionsPanelPlugin> mPanelExtension;
+ private final BlurUtils mBlurUtils;
private GlobalActionsPanelPlugin mPlugin;
private final CommandQueue mCommandQueue;
private GlobalActionsDialog mGlobalActionsDialog;
@@ -68,13 +69,14 @@
@Inject
public GlobalActionsImpl(Context context, CommandQueue commandQueue,
- Lazy<GlobalActionsDialog> globalActionsDialogLazy) {
+ Lazy<GlobalActionsDialog> globalActionsDialogLazy, BlurUtils blurUtils) {
mContext = context;
mGlobalActionsDialogLazy = globalActionsDialogLazy;
mKeyguardStateController = Dependency.get(KeyguardStateController.class);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
mPluginManager = Dependency.get(PluginManager.class);
mCommandQueue = commandQueue;
+ mBlurUtils = blurUtils;
mCommandQueue.addCallback(this);
mPanelExtension = Dependency.get(ExtensionController.class)
.newExtension(GlobalActionsPanelPlugin.class)
@@ -110,7 +112,6 @@
@Override
public void showShutdownUi(boolean isReboot, String reason) {
ScrimDrawable background = new ScrimDrawable();
- background.setAlpha((int) (SHUTDOWN_SCRIM_ALPHA * 255));
Dialog d = new Dialog(mContext,
com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
@@ -160,8 +161,13 @@
reasonView.setText(rebootReasonMessage);
}
- GradientColors colors = Dependency.get(SysuiColorExtractor.class).getNeutralColors();
- background.setColor(colors.getMainColor(), false);
+ if (mBlurUtils.supportsBlursOnWindows()) {
+ background.setAlpha((int) (ScrimController.GRADIENT_SCRIM_ALPHA_BUSY * 255));
+ mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(),
+ mBlurUtils.radiusForRatio(1));
+ } else {
+ background.setAlpha((int) (SHUTDOWN_SCRIM_ALPHA * 255));
+ }
d.show();
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index e6082dd..e7e1ba8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -106,7 +106,7 @@
// Initialize screenshot notification smart actions provider.
mSmartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, false);
+ SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, true);
if (mSmartActionsEnabled) {
mSmartActionsProvider =
SystemUIFactory.getInstance()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 5cfb4b3..333b4a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -274,7 +274,7 @@
@Test
public void testRemoveBubble_withDismissedNotif() {
- mEntryListener.onNotificationAdded(mRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
@@ -317,7 +317,7 @@
assertFalse(mBubbleController.isStackExpanded());
// Mark it as a bubble and add it explicitly
- mEntryListener.onNotificationAdded(mRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
// We should have bubbles & their notifs should not be suppressed
@@ -347,8 +347,8 @@
@Test
public void testCollapseAfterChangingExpandedBubble() {
// Mark it as a bubble and add it explicitly
- mEntryListener.onNotificationAdded(mRow.getEntry());
- mEntryListener.onNotificationAdded(mRow2.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow2.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mRow2.getEntry());
@@ -390,7 +390,7 @@
@Test
public void testExpansionRemovesShowInShadeAndDot() {
// Mark it as a bubble and add it explicitly
- mEntryListener.onNotificationAdded(mRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
// We should have bubbles & their notifs should not be suppressed
@@ -416,7 +416,7 @@
@Test
public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() {
// Mark it as a bubble and add it explicitly
- mEntryListener.onNotificationAdded(mRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
// We should have bubbles & their notifs should not be suppressed
@@ -452,8 +452,8 @@
@Test
public void testRemoveLastExpandedCollapses() {
// Mark it as a bubble and add it explicitly
- mEntryListener.onNotificationAdded(mRow.getEntry());
- mEntryListener.onNotificationAdded(mRow2.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow2.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.updateBubble(mRow2.getEntry());
verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
@@ -496,7 +496,7 @@
Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */);
// Add the auto expand bubble
- mEntryListener.onNotificationAdded(mRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
// Expansion shouldn't change
@@ -514,7 +514,7 @@
Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, true /* enableFlag */);
// Add the auto expand bubble
- mEntryListener.onNotificationAdded(mRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
// Expansion should change
@@ -532,7 +532,7 @@
Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
// Add the suppress notif bubble
- mEntryListener.onNotificationAdded(mRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
// Notif should be suppressed because we were foreground
@@ -576,7 +576,7 @@
public void testExpandStackAndSelectBubble_removedFirst() {
final String key = mRow.getEntry().getKey();
- mEntryListener.onNotificationAdded(mRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
// Simulate notification cancellation.
@@ -588,7 +588,7 @@
@Test
public void testMarkNewNotificationAsShowInShade() {
- mEntryListener.onNotificationAdded(mRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
mRow.getEntry()));
@@ -598,7 +598,7 @@
@Test
public void testAddNotif_notBubble() {
- mEntryListener.onNotificationAdded(mNonBubbleNotifRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mNonBubbleNotifRow.getEntry());
mEntryListener.onPreEntryUpdated(mNonBubbleNotifRow.getEntry());
verify(mBubbleStateChangeListener, never()).onHasBubblesChanged(anyBoolean());
@@ -643,7 +643,7 @@
@Test
public void testRemoveBubble_succeeds_appCancel() {
- mEntryListener.onNotificationAdded(mRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
@@ -658,7 +658,7 @@
@Test
public void removeBubble_fails_clearAll() {
- mEntryListener.onNotificationAdded(mRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
@@ -681,7 +681,7 @@
@Test
public void removeBubble_fails_userDismissNotif() {
- mEntryListener.onNotificationAdded(mRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
@@ -704,7 +704,7 @@
@Test
public void removeBubble_succeeds_userDismissBubble_userDimissNotif() {
- mEntryListener.onNotificationAdded(mRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
mBubbleController.updateBubble(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
@@ -730,7 +730,7 @@
mock(BubbleController.NotificationSuppressionChangedListener.class);
mBubbleData.setSuppressionChangedListener(listener);
- mEntryListener.onNotificationAdded(mRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
@@ -753,7 +753,7 @@
mock(BubbleController.NotificationSuppressionChangedListener.class);
mBubbleData.setSuppressionChangedListener(listener);
- mEntryListener.onNotificationAdded(mRow.getEntry());
+ mEntryListener.onPendingEntryAdded(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
diff --git a/packages/Tethering/common/TetheringLib/jarjar-rules.txt b/packages/Tethering/common/TetheringLib/jarjar-rules.txt
index 35e0f88..1403bba 100644
--- a/packages/Tethering/common/TetheringLib/jarjar-rules.txt
+++ b/packages/Tethering/common/TetheringLib/jarjar-rules.txt
@@ -1 +1,2 @@
rule android.annotation.** com.android.networkstack.tethering.annotation.@1
+rule com.android.internal.annotations.** com.android.networkstack.tethering.annotation.@1
\ No newline at end of file
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 37ce1d57..53a358f 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -30,6 +30,9 @@
import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -37,6 +40,7 @@
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
+import java.util.function.Supplier;
/**
* This class provides the APIs to control the tethering service.
@@ -50,17 +54,23 @@
public class TetheringManager {
private static final String TAG = TetheringManager.class.getSimpleName();
private static final int DEFAULT_TIMEOUT_MS = 60_000;
+ private static final long CONNECTOR_POLL_INTERVAL_MILLIS = 200L;
- private static TetheringManager sInstance;
+ @GuardedBy("mConnectorWaitQueue")
+ @Nullable
+ private ITetheringConnector mConnector;
+ @GuardedBy("mConnectorWaitQueue")
+ @NonNull
+ private final List<ConnectorConsumer> mConnectorWaitQueue = new ArrayList<>();
+ private final Supplier<IBinder> mConnectorSupplier;
- private final ITetheringConnector mConnector;
private final TetheringCallbackInternal mCallback;
private final Context mContext;
private final ArrayMap<TetheringEventCallback, ITetheringEventCallback>
mTetheringEventCallbacks = new ArrayMap<>();
- private TetheringConfigurationParcel mTetheringConfiguration;
- private TetherStatesParcel mTetherStatesParcel;
+ private volatile TetheringConfigurationParcel mTetheringConfiguration;
+ private volatile TetherStatesParcel mTetherStatesParcel;
/**
* Broadcast Action: A tetherable connection has come or gone.
@@ -162,29 +172,139 @@
/**
* Create a TetheringManager object for interacting with the tethering service.
*
+ * @param context Context for the manager.
+ * @param connectorSupplier Supplier for the manager connector; may return null while the
+ * service is not connected.
* {@hide}
*/
- public TetheringManager(@NonNull final Context context, @NonNull final IBinder service) {
+ public TetheringManager(@NonNull final Context context,
+ @NonNull Supplier<IBinder> connectorSupplier) {
mContext = context;
- mConnector = ITetheringConnector.Stub.asInterface(service);
mCallback = new TetheringCallbackInternal();
+ mConnectorSupplier = connectorSupplier;
final String pkgName = mContext.getOpPackageName();
+
+ final IBinder connector = mConnectorSupplier.get();
+ // If the connector is available on start, do not start a polling thread. This introduces
+ // differences in the thread that sends the oneway binder calls to the service between the
+ // first few seconds after boot and later, but it avoids always having differences between
+ // the first usage of TetheringManager from a process and subsequent usages (so the
+ // difference is only on boot). On boot binder calls may be queued until the service comes
+ // up and be sent from a worker thread; later, they are always sent from the caller thread.
+ // Considering that it's just oneway binder calls, and ordering is preserved, this seems
+ // better than inconsistent behavior persisting after boot.
+ if (connector != null) {
+ mConnector = ITetheringConnector.Stub.asInterface(connector);
+ } else {
+ startPollingForConnector();
+ }
+
Log.i(TAG, "registerTetheringEventCallback:" + pkgName);
+ getConnector(c -> c.registerTetheringEventCallback(mCallback, pkgName));
+ }
+
+ private void startPollingForConnector() {
+ new Thread(() -> {
+ while (true) {
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ // Not much to do here, the system needs to wait for the connector
+ }
+
+ final IBinder connector = mConnectorSupplier.get();
+ if (connector != null) {
+ onTetheringConnected(ITetheringConnector.Stub.asInterface(connector));
+ return;
+ }
+ }
+ }).start();
+ }
+
+ private interface ConnectorConsumer {
+ void onConnectorAvailable(ITetheringConnector connector) throws RemoteException;
+ }
+
+ private void onTetheringConnected(ITetheringConnector connector) {
+ // Process the connector wait queue in order, including any items that are added
+ // while processing.
+ //
+ // 1. Copy the queue to a local variable under lock.
+ // 2. Drain the local queue with the lock released (otherwise, enqueuing future commands
+ // would block on the lock).
+ // 3. Acquire the lock again. If any new tasks were queued during step 2, goto 1.
+ // If not, set mConnector to non-null so future tasks are run immediately, not queued.
+ //
+ // For this to work, all calls to the tethering service must use getConnector(), which
+ // ensures that tasks are added to the queue with the lock held.
+ //
+ // Once mConnector is set to non-null, it will never be null again. If the network stack
+ // process crashes, no recovery is possible.
+ // TODO: evaluate whether it is possible to recover from network stack process crashes
+ // (though in most cases the system will have crashed when the network stack process
+ // crashes).
+ do {
+ final List<ConnectorConsumer> localWaitQueue;
+ synchronized (mConnectorWaitQueue) {
+ localWaitQueue = new ArrayList<>(mConnectorWaitQueue);
+ mConnectorWaitQueue.clear();
+ }
+
+ // Allow more tasks to be added at the end without blocking while draining the queue.
+ for (ConnectorConsumer task : localWaitQueue) {
+ try {
+ task.onConnectorAvailable(connector);
+ } catch (RemoteException e) {
+ // Most likely the network stack process crashed, which is likely to crash the
+ // system. Keep processing other requests but report the error loudly.
+ Log.wtf(TAG, "Error processing request for the tethering connector", e);
+ }
+ }
+
+ synchronized (mConnectorWaitQueue) {
+ if (mConnectorWaitQueue.size() == 0) {
+ mConnector = connector;
+ return;
+ }
+ }
+ } while (true);
+ }
+
+ /**
+ * Asynchronously get the ITetheringConnector to execute some operation.
+ *
+ * <p>If the connector is already available, the operation will be executed on the caller's
+ * thread. Otherwise it will be queued and executed on a worker thread. The operation should be
+ * limited to performing oneway binder calls to minimize differences due to threading.
+ */
+ private void getConnector(ConnectorConsumer consumer) {
+ final ITetheringConnector connector;
+ synchronized (mConnectorWaitQueue) {
+ connector = mConnector;
+ if (connector == null) {
+ mConnectorWaitQueue.add(consumer);
+ return;
+ }
+ }
+
try {
- mConnector.registerTetheringEventCallback(mCallback, pkgName);
+ consumer.onConnectorAvailable(connector);
} catch (RemoteException e) {
throw new IllegalStateException(e);
}
}
private interface RequestHelper {
- void runRequest(IIntResultListener listener);
+ void runRequest(ITetheringConnector connector, IIntResultListener listener);
}
+ // Used to dispatch legacy ConnectivityManager methods that expect tethering to be able to
+ // return results and perform operations synchronously.
+ // TODO: remove once there are no callers of these legacy methods.
private class RequestDispatcher {
private final ConditionVariable mWaiting;
- public int mRemoteResult;
+ public volatile int mRemoteResult;
private final IIntResultListener mListener = new IIntResultListener.Stub() {
@Override
@@ -199,7 +319,7 @@
}
int waitForResult(final RequestHelper request) {
- request.runRequest(mListener);
+ getConnector(c -> request.runRequest(c, mListener));
if (!mWaiting.block(DEFAULT_TIMEOUT_MS)) {
throw new IllegalStateException("Callback timeout");
}
@@ -222,7 +342,7 @@
}
private class TetheringCallbackInternal extends ITetheringEventCallback.Stub {
- private int mError = TETHER_ERROR_NO_ERROR;
+ private volatile int mError = TETHER_ERROR_NO_ERROR;
private final ConditionVariable mWaitForCallback = new ConditionVariable();
@Override
@@ -280,9 +400,9 @@
Log.i(TAG, "tether caller:" + callerPkg);
final RequestDispatcher dispatcher = new RequestDispatcher();
- return dispatcher.waitForResult(listener -> {
+ return dispatcher.waitForResult((connector, listener) -> {
try {
- mConnector.tether(iface, callerPkg, listener);
+ connector.tether(iface, callerPkg, listener);
} catch (RemoteException e) {
throw new IllegalStateException(e);
}
@@ -304,9 +424,9 @@
final RequestDispatcher dispatcher = new RequestDispatcher();
- return dispatcher.waitForResult(listener -> {
+ return dispatcher.waitForResult((connector, listener) -> {
try {
- mConnector.untether(iface, callerPkg, listener);
+ connector.untether(iface, callerPkg, listener);
} catch (RemoteException e) {
throw new IllegalStateException(e);
}
@@ -330,9 +450,9 @@
final RequestDispatcher dispatcher = new RequestDispatcher();
- return dispatcher.waitForResult(listener -> {
+ return dispatcher.waitForResult((connector, listener) -> {
try {
- mConnector.setUsbTethering(enable, callerPkg, listener);
+ connector.setUsbTethering(enable, callerPkg, listener);
} catch (RemoteException e) {
throw new IllegalStateException(e);
}
@@ -467,11 +587,7 @@
});
}
};
- try {
- mConnector.startTethering(request.getParcel(), callerPkg, listener);
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
+ getConnector(c -> c.startTethering(request.getParcel(), callerPkg, listener));
}
/**
@@ -509,15 +625,15 @@
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "stopTethering caller:" + callerPkg);
- final RequestDispatcher dispatcher = new RequestDispatcher();
-
- dispatcher.waitForResult(listener -> {
- try {
- mConnector.stopTethering(type, callerPkg, listener);
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
+ getConnector(c -> c.stopTethering(type, callerPkg, new IIntResultListener.Stub() {
+ @Override
+ public void onResult(int resultCode) {
+ // TODO: provide an API to obtain result
+ // This has never been possible as stopTethering has always been void and never
+ // taken a callback object. The only indication that callers have is if the call
+ // results in a TETHER_STATE_CHANGE broadcast.
}
- });
+ }));
}
/**
@@ -591,12 +707,8 @@
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "getLatestTetheringEntitlementResult caller:" + callerPkg);
- try {
- mConnector.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi,
- callerPkg);
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
+ getConnector(c -> c.requestLatestTetheringEntitlementResult(
+ type, receiver, showEntitlementUi, callerPkg));
}
/**
@@ -832,11 +944,7 @@
});
}
};
- try {
- mConnector.registerTetheringEventCallback(remoteCallback, callerPkg);
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
+ getConnector(c -> c.registerTetheringEventCallback(remoteCallback, callerPkg));
mTetheringEventCallbacks.put(callback, remoteCallback);
}
}
@@ -860,11 +968,8 @@
if (remoteCallback == null) {
throw new IllegalArgumentException("callback was not registered.");
}
- try {
- mConnector.unregisterTetheringEventCallback(remoteCallback, callerPkg);
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
- }
+
+ getConnector(c -> c.unregisterTetheringEventCallback(remoteCallback, callerPkg));
}
}
@@ -1002,9 +1107,9 @@
final String callerPkg = mContext.getOpPackageName();
final RequestDispatcher dispatcher = new RequestDispatcher();
- final int ret = dispatcher.waitForResult(listener -> {
+ final int ret = dispatcher.waitForResult((connector, listener) -> {
try {
- mConnector.isTetheringSupported(callerPkg, listener);
+ connector.isTetheringSupported(callerPkg, listener);
} catch (RemoteException e) {
throw new IllegalStateException(e);
}
@@ -1027,13 +1132,14 @@
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "stopAllTethering caller:" + callerPkg);
- final RequestDispatcher dispatcher = new RequestDispatcher();
- dispatcher.waitForResult(listener -> {
- try {
- mConnector.stopAllTethering(callerPkg, listener);
- } catch (RemoteException e) {
- throw new IllegalStateException(e);
+ getConnector(c -> c.stopAllTethering(callerPkg, new IIntResultListener.Stub() {
+ @Override
+ public void onResult(int resultCode) {
+ // TODO: add an API parameter to send result to caller.
+ // This has never been possible as stopAllTethering has always been void and never
+ // taken a callback object. The only indication that callers have is if the call
+ // results in a TETHER_STATE_CHANGE broadcast.
}
- });
+ }));
}
}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index 7dc5c5f..020b32a 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -77,8 +77,8 @@
mLog.mark("onCreate");
mDeps = getTetheringDependencies();
mContext = mDeps.getContext();
- mTethering = makeTethering(mDeps);
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mTethering = makeTethering(mDeps);
}
/**
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index 1fe162c..5d170d3 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -18,9 +18,17 @@
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP;
import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD;
@@ -110,6 +118,7 @@
mGestures.add(new Swipe(context, UP, DOWN, GESTURE_SWIPE_UP_AND_DOWN, this));
mGestures.add(new Swipe(context, UP, LEFT, GESTURE_SWIPE_UP_AND_LEFT, this));
mGestures.add(new Swipe(context, UP, RIGHT, GESTURE_SWIPE_UP_AND_RIGHT, this));
+ // Set up multi-finger gestures to be enabled later.
// Two-finger taps.
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP, this));
@@ -124,6 +133,24 @@
new MultiFingerMultiTap(mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP, this));
mMultiFingerGestures.add(
new MultiFingerMultiTap(mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP, this));
+ // Two-finger swipes.
+ mMultiFingerGestures.add(
+ new MultiFingerSwipe(context, 2, DOWN, GESTURE_2_FINGER_SWIPE_DOWN, this));
+ mMultiFingerGestures.add(
+ new MultiFingerSwipe(context, 2, LEFT, GESTURE_2_FINGER_SWIPE_LEFT, this));
+ mMultiFingerGestures.add(
+ new MultiFingerSwipe(context, 2, RIGHT, GESTURE_2_FINGER_SWIPE_RIGHT, this));
+ mMultiFingerGestures.add(
+ new MultiFingerSwipe(context, 2, UP, GESTURE_2_FINGER_SWIPE_UP, this));
+ // Three-finger swipes.
+ mMultiFingerGestures.add(
+ new MultiFingerSwipe(context, 3, DOWN, GESTURE_3_FINGER_SWIPE_DOWN, this));
+ mMultiFingerGestures.add(
+ new MultiFingerSwipe(context, 3, LEFT, GESTURE_3_FINGER_SWIPE_LEFT, this));
+ mMultiFingerGestures.add(
+ new MultiFingerSwipe(context, 3, RIGHT, GESTURE_3_FINGER_SWIPE_RIGHT, this));
+ mMultiFingerGestures.add(
+ new MultiFingerSwipe(context, 3, UP, GESTURE_3_FINGER_SWIPE_UP, this));
}
/**
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureUtils.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureUtils.java
index 0f5dd08..ac67480 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureUtils.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureUtils.java
@@ -8,6 +8,9 @@
*/
public final class GestureUtils {
+ public static int MM_PER_CM = 10;
+ public static float CM_PER_INCH = 2.54f;
+
private GestureUtils() {
/* cannot be instantiated */
}
@@ -85,4 +88,12 @@
return true;
}
+
+ /**
+ * Gets the index of the pointer that went up or down from a motion event.
+ */
+ public static int getActionIndex(MotionEvent event) {
+ return (event.getAction()
+ & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
new file mode 100644
index 0000000..8249239
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
@@ -0,0 +1,496 @@
+/*
+ * 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.accessibility.gestures;
+
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+
+import static com.android.server.accessibility.gestures.GestureUtils.MM_PER_CM;
+import static com.android.server.accessibility.gestures.GestureUtils.getActionIndex;
+import static com.android.server.accessibility.gestures.Swipe.CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS;
+import static com.android.server.accessibility.gestures.Swipe.CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS;
+import static com.android.server.accessibility.gestures.Swipe.GESTURE_CONFIRM_CM;
+import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.os.Handler;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * This class is responsible for matching one-finger swipe gestures. Each instance matches one swipe
+ * gesture. A swipe is specified as a series of one or more directions e.g. left, left and up, etc.
+ * At this time swipes with more than two directions are not supported.
+ */
+class MultiFingerSwipe extends GestureMatcher {
+
+ // Direction constants.
+ public static final int LEFT = 0;
+ public static final int RIGHT = 1;
+ public static final int UP = 2;
+ public static final int DOWN = 3;
+ // This is the calculated movement threshold used track if the user is still
+ // moving their finger.
+ private final float mGestureDetectionThresholdPixels;
+
+ // Buffer for storing points for gesture detection.
+ private final ArrayList<PointF>[] mStrokeBuffers;
+
+ // The swipe direction for this matcher.
+ private int mDirection;
+ private int[] mPointerIds;
+ // The starting point of each finger's path in the gesture.
+ private PointF[] mBase;
+ // The most recent entry in each finger's gesture path.
+ private PointF[] mPreviousGesturePoint;
+ private int mTargetFingerCount;
+ private int mCurrentFingerCount;
+ // Whether the appropriate number of fingers have gone down at some point. This is reset only on
+ // clear.
+ private boolean mTargetFingerCountReached = false;
+ // Constants for sampling motion event points.
+ // We sample based on a minimum distance between points, primarily to improve accuracy by
+ // reducing noisy minor changes in direction.
+ private static final float MIN_CM_BETWEEN_SAMPLES = 0.25f;
+ private final float mMinPixelsBetweenSamplesX;
+ private final float mMinPixelsBetweenSamplesY;
+ // The minmimum distance the finger must travel before we evaluate the initial direction of the
+ // swipe.
+ // Anything less is still considered a touch.
+ private int mTouchSlop;
+
+ MultiFingerSwipe(
+ Context context,
+ int fingerCount,
+ int direction,
+ int gesture,
+ GestureMatcher.StateChangeListener listener) {
+ super(gesture, new Handler(context.getMainLooper()), listener);
+ mTargetFingerCount = fingerCount;
+ mPointerIds = new int[mTargetFingerCount];
+ mBase = new PointF[mTargetFingerCount];
+ mPreviousGesturePoint = new PointF[mTargetFingerCount];
+ mStrokeBuffers = new ArrayList[mTargetFingerCount];
+ mDirection = direction;
+ DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+ mGestureDetectionThresholdPixels =
+ TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, MM_PER_CM, displayMetrics)
+ * GESTURE_CONFIRM_CM;
+ // Calculate minimum gesture velocity
+ final float pixelsPerCmX = displayMetrics.xdpi / GestureUtils.CM_PER_INCH;
+ final float pixelsPerCmY = displayMetrics.ydpi / GestureUtils.CM_PER_INCH;
+ mMinPixelsBetweenSamplesX = MIN_CM_BETWEEN_SAMPLES * pixelsPerCmX;
+ mMinPixelsBetweenSamplesY = MIN_CM_BETWEEN_SAMPLES * pixelsPerCmY;
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+ clear();
+ }
+
+ @Override
+ protected void clear() {
+ mTargetFingerCountReached = false;
+ mCurrentFingerCount = 0;
+ for (int i = 0; i < mTargetFingerCount; ++i) {
+ mPointerIds[i] = INVALID_POINTER_ID;
+ if (mBase[i] == null) {
+ mBase[i] = new PointF();
+ }
+ mBase[i].x = Float.NaN;
+ mBase[i].y = Float.NaN;
+ if (mPreviousGesturePoint[i] == null) {
+ mPreviousGesturePoint[i] = new PointF();
+ }
+ mPreviousGesturePoint[i].x = Float.NaN;
+ mPreviousGesturePoint[i].y = Float.NaN;
+ if (mStrokeBuffers[i] == null) {
+ mStrokeBuffers[i] = new ArrayList<>(100);
+ }
+ mStrokeBuffers[i].clear();
+ }
+ super.clear();
+ }
+
+ @Override
+ protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (mCurrentFingerCount > 0) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ mCurrentFingerCount = 1;
+ final int actionIndex = getActionIndex(rawEvent);
+ final int pointerId = rawEvent.getPointerId(actionIndex);
+ int pointerIndex = rawEvent.getPointerCount() - 1;
+ if (pointerId < 0 || pointerId > rawEvent.getPointerCount() - 1) {
+ // Nonsensical pointer id.
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ if (mPointerIds[pointerIndex] != INVALID_POINTER_ID) {
+ // Inconsistent event stream.
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ mPointerIds[pointerIndex] = pointerId;
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
+ if (Float.isNaN(mBase[pointerIndex].x) && Float.isNaN(mBase[pointerIndex].y)) {
+ final float x = rawEvent.getX(actionIndex);
+ final float y = rawEvent.getY(actionIndex);
+ if (x < 0f || y < 0f) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ mBase[pointerIndex].x = x;
+ mBase[pointerIndex].y = y;
+ mPreviousGesturePoint[pointerIndex].x = x;
+ mPreviousGesturePoint[pointerIndex].y = y;
+ } else {
+ // This event doesn't make sense in the middle of a gesture.
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ }
+
+ @Override
+ protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (event.getPointerCount() > mTargetFingerCount) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ mCurrentFingerCount += 1;
+ if (mCurrentFingerCount != rawEvent.getPointerCount()) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ if (mCurrentFingerCount == mTargetFingerCount) {
+ mTargetFingerCountReached = true;
+ }
+ final int actionIndex = getActionIndex(rawEvent);
+ final int pointerId = rawEvent.getPointerId(actionIndex);
+ if (pointerId < 0 || pointerId > rawEvent.getPointerCount() - 1) {
+ // Nonsensical pointer id.
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ int pointerIndex = mCurrentFingerCount - 1;
+ if (mPointerIds[pointerIndex] != INVALID_POINTER_ID) {
+ // Inconsistent event stream.
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ mPointerIds[pointerIndex] = pointerId;
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
+ if (Float.isNaN(mBase[pointerIndex].x) && Float.isNaN(mBase[pointerIndex].y)) {
+ final float x = rawEvent.getX(actionIndex);
+ final float y = rawEvent.getY(actionIndex);
+ if (x < 0f || y < 0f) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ mBase[pointerIndex].x = x;
+ mBase[pointerIndex].y = y;
+ mPreviousGesturePoint[pointerIndex].x = x;
+ mPreviousGesturePoint[pointerIndex].y = y;
+ } else {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ }
+
+ @Override
+ protected void onPointerUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (!mTargetFingerCountReached) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ mCurrentFingerCount -= 1;
+ final int actionIndex = getActionIndex(event);
+ final int pointerId = event.getPointerId(actionIndex);
+ if (pointerId < 0 || pointerId > rawEvent.getPointerCount() - 1) {
+ // Nonsensical pointer id.
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ final int pointerIndex = Arrays.binarySearch(mPointerIds, pointerId);
+ if (pointerIndex < 0) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ final float x = rawEvent.getX(actionIndex);
+ final float y = rawEvent.getY(actionIndex);
+ if (x < 0f || y < 0f) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ final float dX = Math.abs(x - mPreviousGesturePoint[pointerIndex].x);
+ final float dY = Math.abs(y - mPreviousGesturePoint[pointerIndex].y);
+ if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
+ mStrokeBuffers[pointerIndex].add(new PointF(x, y));
+ }
+ // We will evaluate all the paths on ACTION_UP.
+ }
+
+ @Override
+ protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ for (int pointerIndex = 0; pointerIndex < rawEvent.getPointerCount(); ++pointerIndex) {
+ if (DEBUG) {
+ Slog.d(getGestureName(), "Processing move on finger " + pointerIndex);
+ }
+ int index = rawEvent.findPointerIndex(mPointerIds[pointerIndex]);
+ final float x = rawEvent.getX(index);
+ final float y = rawEvent.getY(index);
+ if (x < 0f || y < 0f) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ final float dX = Math.abs(x - mPreviousGesturePoint[pointerIndex].x);
+ final float dY = Math.abs(y - mPreviousGesturePoint[pointerIndex].y);
+ final double moveDelta =
+ Math.hypot(
+ Math.abs(x - mBase[pointerIndex].x),
+ Math.abs(y - mBase[pointerIndex].y));
+ if (DEBUG) {
+ Slog.d(
+ getGestureName(),
+ "moveDelta:"
+ + Double.toString(moveDelta)
+ + " mGestureDetectionThreshold: "
+ + Float.toString(mGestureDetectionThresholdPixels));
+ }
+ if (getState() == STATE_CLEAR) {
+ if (moveDelta < mTouchSlop) {
+ // This still counts as a touch not a swipe.
+ continue;
+ } else if (mStrokeBuffers[pointerIndex].size() == 0) {
+ // First, make sure we have the right number of fingers down.
+ if (mCurrentFingerCount != mTargetFingerCount) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ // Then, make sure the pointer is going in the right direction.
+ int direction =
+ toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y);
+ if (direction != mDirection) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ } else {
+ // This is confirmed to be some kind of swipe so start tracking points.
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
+ for (int i = 0; i < mTargetFingerCount; ++i) {
+ mStrokeBuffers[i].add(new PointF(mBase[i]));
+ }
+ }
+ }
+ if (moveDelta > mGestureDetectionThresholdPixels) {
+ // Try to cancel if the finger starts to go the wrong way.
+ // Note that this only works because this matcher assumes one direction.
+ int direction =
+ toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y);
+ if (direction != mDirection) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ // If the pointer has moved more than the threshold,
+ // update the stored values.
+ mBase[pointerIndex].x = x;
+ mBase[pointerIndex].y = y;
+ mPreviousGesturePoint[pointerIndex].x = x;
+ mPreviousGesturePoint[pointerIndex].y = y;
+ if (getState() == STATE_CLEAR) {
+ startGesture(event, rawEvent, policyFlags);
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
+ }
+ }
+ }
+ if (getState() == STATE_GESTURE_STARTED) {
+ if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
+ // Sample every 2.5 MM in order to guard against minor variations in path.
+ mPreviousGesturePoint[pointerIndex].x = x;
+ mPreviousGesturePoint[pointerIndex].y = y;
+ mStrokeBuffers[pointerIndex].add(new PointF(x, y));
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ if (getState() != STATE_GESTURE_STARTED) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ mCurrentFingerCount = 0;
+ final int actionIndex = getActionIndex(event);
+ final int pointerId = event.getPointerId(actionIndex);
+ final int pointerIndex = Arrays.binarySearch(mPointerIds, pointerId);
+ if (pointerIndex < 0) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ final float x = rawEvent.getX(actionIndex);
+ final float y = rawEvent.getY(actionIndex);
+ if (x < 0f || y < 0f) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ final float dX = Math.abs(x - mPreviousGesturePoint[pointerIndex].x);
+ final float dY = Math.abs(y - mPreviousGesturePoint[pointerIndex].y);
+ if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
+ mStrokeBuffers[pointerIndex].add(new PointF(x, y));
+ }
+ recognizeGesture(event, rawEvent, policyFlags);
+ }
+
+ /**
+ * queues a transition to STATE_GESTURE_CANCEL based on the current state. If we have
+ * transitioned to STATE_GESTURE_STARTED the delay is longer.
+ */
+ private void cancelAfterPauseThreshold(
+ MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ cancelPendingTransitions();
+ switch (getState()) {
+ case STATE_CLEAR:
+ cancelAfter(CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS, event, rawEvent, policyFlags);
+ break;
+ case STATE_GESTURE_STARTED:
+ cancelAfter(CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS, event, rawEvent, policyFlags);
+ break;
+ default:
+ break;
+ }
+ }
+ /**
+ * Looks at the sequence of motions in mStrokeBuffer, classifies the gesture, then transitions
+ * to the complete or cancel state depending on the result.
+ */
+ private void recognizeGesture(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ // Check the path of each finger against the specified direction.
+ // Note that we sample every 2.5 MMm, and the direction matching is extremely tolerant (each
+ // direction has a 90-degree arch of tolerance) meaning that minor perpendicular movements
+ // should not create false negatives.
+ for (int i = 0; i < mTargetFingerCount; ++i) {
+ if (DEBUG) {
+ Slog.d(getGestureName(), "Recognizing finger: " + i);
+ }
+ if (mStrokeBuffers[i].size() < 2) {
+ Slog.d(getGestureName(), "Too few points.");
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ ArrayList<PointF> path = mStrokeBuffers[i];
+
+ if (DEBUG) {
+ Slog.d(getGestureName(), "path=" + path.toString());
+ }
+ // Classify line segments, and call Listener callbacks.
+ if (!recognizeGesturePath(event, rawEvent, policyFlags, path)) {
+ cancelGesture(event, rawEvent, policyFlags);
+ return;
+ }
+ }
+ // If we reach this point then all paths match.
+ completeGesture(event, rawEvent, policyFlags);
+ }
+
+ /**
+ * Tests the path of a given finger against the direction specified in this matcher.
+ *
+ * @return True if the path matches the specified direction for this matcher, otherwise false.
+ */
+ private boolean recognizeGesturePath(
+ MotionEvent event, MotionEvent rawEvent, int policyFlags, ArrayList<PointF> path) {
+
+ final int displayId = event.getDisplayId();
+ for (int i = 0; i < path.size() - 1; ++i) {
+ PointF start = path.get(i);
+ PointF end = path.get(i + 1);
+
+ float dX = end.x - start.x;
+ float dY = end.y - start.y;
+ int direction = toDirection(dX, dY);
+ if (direction != mDirection) {
+ if (DEBUG) {
+ Slog.d(
+ getGestureName(),
+ "Found direction "
+ + directionToString(direction)
+ + " when expecting "
+ + directionToString(mDirection));
+ }
+ return false;
+ }
+ }
+ if (DEBUG) {
+ Slog.d(getGestureName(), "Completed.");
+ }
+ return true;
+ }
+
+ private static int toDirection(float dX, float dY) {
+ if (Math.abs(dX) > Math.abs(dY)) {
+ // Horizontal
+ return (dX < 0) ? LEFT : RIGHT;
+ } else {
+ // Vertical
+ return (dY < 0) ? UP : DOWN;
+ }
+ }
+
+ public static String directionToString(int direction) {
+ switch (direction) {
+ case LEFT:
+ return "left";
+ case RIGHT:
+ return "right";
+ case UP:
+ return "up";
+ case DOWN:
+ return "down";
+ default:
+ return "Unknown Direction";
+ }
+ }
+
+ @Override
+ String getGestureName() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(mTargetFingerCount).append("-finger ");
+ builder.append("Swipe ").append(directionToString(mDirection));
+ return builder.toString();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder(super.toString());
+ if (getState() != STATE_GESTURE_CANCELED) {
+ builder.append(", mBase: ")
+ .append(mBase.toString())
+ .append(", mGestureDetectionThreshold:")
+ .append(mGestureDetectionThresholdPixels)
+ .append(", mMinPixelsBetweenSamplesX:")
+ .append(mMinPixelsBetweenSamplesX)
+ .append(", mMinPixelsBetweenSamplesY:")
+ .append(mMinPixelsBetweenSamplesY);
+ }
+ return builder.toString();
+ }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/SecondFingerMultiTap.java b/services/accessibility/java/com/android/server/accessibility/gestures/SecondFingerMultiTap.java
index 8a566fc..ada251f 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/SecondFingerMultiTap.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/SecondFingerMultiTap.java
@@ -18,6 +18,8 @@
import static android.view.MotionEvent.INVALID_POINTER_ID;
+import static com.android.server.accessibility.gestures.GestureUtils.getActionIndex;
+
import android.content.Context;
import android.os.Handler;
import android.view.MotionEvent;
@@ -155,11 +157,6 @@
return moveDelta <= slop;
}
- private int getActionIndex(MotionEvent event) {
- return event.getAction()
- & MotionEvent.ACTION_POINTER_INDEX_MASK << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
- }
-
@Override
public String toString() {
return super.toString()
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java
index a285c10..9108c69 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java
@@ -16,10 +16,10 @@
package com.android.server.accessibility.gestures;
+import static com.android.server.accessibility.gestures.GestureUtils.MM_PER_CM;
import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG;
import android.content.Context;
-import android.gesture.GesturePoint;
import android.graphics.PointF;
import android.os.Handler;
import android.util.DisplayMetrics;
@@ -44,10 +44,10 @@
public static final int DOWN = 3;
// This is the calculated movement threshold used track if the user is still
// moving their finger.
- private final float mGestureDetectionThreshold;
+ private final float mGestureDetectionThresholdPixels;
// Buffer for storing points for gesture detection.
- private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
+ private final ArrayList<PointF> mStrokeBuffer = new ArrayList<>(100);
// The minimal delta between moves to add a gesture point.
private static final int TOUCH_TOLERANCE_PIX = 3;
@@ -56,7 +56,7 @@
private static final float MIN_PREDICTION_SCORE = 2.0f;
// Distance a finger must travel before we decide if it is a gesture or not.
- private static final int GESTURE_CONFIRM_CM = 1;
+ public static final int GESTURE_CONFIRM_CM = 1;
// Time threshold used to determine if an interaction is a gesture or not.
// If the first movement of 1cm takes longer than this value, we assume it's
@@ -67,17 +67,16 @@
// all gestures started with the initial movement taking less than 100ms.
// When touch exploring, the first movement almost always takes longer than
// 200ms.
- private static final long CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS = 150;
+ public static final long CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS = 150;
// Time threshold used to determine if a gesture should be cancelled. If
// the finger takes more than this time to move 1cm, the ongoing gesture is
// cancelled.
- private static final long CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS = 300;
+ public static final long CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS = 300;
private int[] mDirections;
private float mBaseX;
private float mBaseY;
- private long mBaseTime;
private float mPreviousGestureX;
private float mPreviousGestureY;
// Constants for sampling motion event points.
@@ -119,8 +118,8 @@
super(gesture, new Handler(context.getMainLooper()), listener);
mDirections = directions;
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
- mGestureDetectionThreshold =
- TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 10, displayMetrics)
+ mGestureDetectionThresholdPixels =
+ TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, MM_PER_CM, displayMetrics)
* GESTURE_CONFIRM_CM;
// Calculate minimum gesture velocity
final float pixelsPerCmX = displayMetrics.xdpi / 2.54f;
@@ -135,18 +134,16 @@
protected void clear() {
mBaseX = Float.NaN;
mBaseY = Float.NaN;
- mBaseTime = 0;
mStrokeBuffer.clear();
super.clear();
}
@Override
protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
- cancelAfterDelay(event, rawEvent, policyFlags);
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
if (Float.isNaN(mBaseX) && Float.isNaN(mBaseY)) {
mBaseX = rawEvent.getX();
mBaseY = rawEvent.getY();
- mBaseTime = event.getEventTime();
mPreviousGestureX = mBaseX;
mPreviousGestureY = mBaseY;
}
@@ -157,10 +154,8 @@
protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
final float x = rawEvent.getX();
final float y = rawEvent.getY();
- final long time = event.getEventTime();
final float dX = Math.abs(x - mPreviousGestureX);
final float dY = Math.abs(y - mPreviousGestureY);
- final long timeDelta = time - mBaseTime;
final double moveDelta = Math.hypot(Math.abs(x - mBaseX), Math.abs(y - mBaseY));
if (DEBUG) {
Slog.d(
@@ -168,7 +163,7 @@
"moveDelta:"
+ Double.toString(moveDelta)
+ " mGestureDetectionThreshold: "
- + Float.toString(mGestureDetectionThreshold));
+ + Float.toString(mGestureDetectionThresholdPixels));
}
if (getState() == STATE_CLEAR) {
if (moveDelta < mTouchSlop) {
@@ -176,25 +171,24 @@
return;
} else if (mStrokeBuffer.size() == 0) {
// First, make sure the pointer is going in the right direction.
- cancelAfterDelay(event, rawEvent, policyFlags);
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
int direction = toDirection(x - mBaseX, y - mBaseY);
if (direction != mDirections[0]) {
cancelGesture(event, rawEvent, policyFlags);
return;
} else {
// This is confirmed to be some kind of swipe so start tracking points.
- mStrokeBuffer.add(new GesturePoint(mBaseX, mBaseY, mBaseTime));
+ mStrokeBuffer.add(new PointF(mBaseX, mBaseY));
}
}
- if (moveDelta > mGestureDetectionThreshold) {
+ if (moveDelta > mGestureDetectionThresholdPixels) {
// If the pointer has moved more than the threshold,
// update the stored values.
mBaseX = x;
mBaseY = y;
- mBaseTime = time;
if (getState() == STATE_CLEAR) {
startGesture(event, rawEvent, policyFlags);
- cancelAfterDelay(event, rawEvent, policyFlags);
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
}
}
}
@@ -202,8 +196,8 @@
if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
mPreviousGestureX = x;
mPreviousGestureY = y;
- mStrokeBuffer.add(new GesturePoint(x, y, time));
- cancelAfterDelay(event, rawEvent, policyFlags);
+ mStrokeBuffer.add(new PointF(x, y));
+ cancelAfterPauseThreshold(event, rawEvent, policyFlags);
}
}
}
@@ -217,11 +211,10 @@
final float x = rawEvent.getX();
final float y = rawEvent.getY();
- final long time = event.getEventTime();
final float dX = Math.abs(x - mPreviousGestureX);
final float dY = Math.abs(y - mPreviousGestureY);
if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
- mStrokeBuffer.add(new GesturePoint(x, y, time));
+ mStrokeBuffer.add(new PointF(x, y));
}
recognizeGesture(event, rawEvent, policyFlags);
}
@@ -240,7 +233,8 @@
* queues a transition to STATE_GESTURE_CANCEL based on the current state. If we have
* transitioned to STATE_GESTURE_STARTED the delay is longer.
*/
- private void cancelAfterDelay(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+ private void cancelAfterPauseThreshold(
+ MotionEvent event, MotionEvent rawEvent, int policyFlags) {
cancelPendingTransitions();
switch (getState()) {
case STATE_CLEAR:
@@ -275,7 +269,7 @@
// 90 degrees.
ArrayList<PointF> path = new ArrayList<>();
- PointF lastDelimiter = new PointF(mStrokeBuffer.get(0).x, mStrokeBuffer.get(0).y);
+ PointF lastDelimiter = mStrokeBuffer.get(0);
path.add(lastDelimiter);
float dX = 0; // Sum of unit vectors from last delimiter to each following point
@@ -283,9 +277,9 @@
int count = 0; // Number of points since last delimiter
float length = 0; // Vector length from delimiter to most recent point
- PointF next = new PointF();
+ PointF next = null;
for (int i = 1; i < mStrokeBuffer.size(); ++i) {
- next = new PointF(mStrokeBuffer.get(i).x, mStrokeBuffer.get(i).y);
+ next = mStrokeBuffer.get(i);
if (count > 0) {
// Average of unit vectors from delimiter to following points
float currentDX = dX / count;
@@ -428,7 +422,7 @@
.append(", mBaseY: ")
.append(mBaseY)
.append(", mGestureDetectionThreshold:")
- .append(mGestureDetectionThreshold)
+ .append(mGestureDetectionThresholdPixels)
.append(", mMinPixelsBetweenSamplesX:")
.append(mMinPixelsBetweenSamplesX)
.append(", mMinPixelsBetweenSamplesY:")
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 4f18f35..1a4fc32 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -154,6 +154,13 @@
@GuardedBy("mLock")
private FillEventHistory mEventHistory;
+ /**
+ * The last inline augmented autofill selection. Note that we don't log the selection from the
+ * dropdown UI since the service owns the UI in that case.
+ */
+ @GuardedBy("mLock")
+ private FillEventHistory mAugmentedAutofillEventHistory;
+
/** Shared instance, doesn't need to be logged */
private final AutofillCompatState mAutofillCompatState;
@@ -707,6 +714,13 @@
}
}
+ void setLastAugmentedAutofillResponse(int sessionId) {
+ synchronized (mLock) {
+ mAugmentedAutofillEventHistory = new FillEventHistory(sessionId, /* clientState= */
+ null);
+ }
+ }
+
/**
* Resets the last fill selection.
*/
@@ -716,6 +730,12 @@
}
}
+ void resetLastAugmentedAutofillResponse() {
+ synchronized (mLock) {
+ mAugmentedAutofillEventHistory = null;
+ }
+ }
+
@GuardedBy("mLock")
private boolean isValidEventLocked(String method, int sessionId) {
if (mEventHistory == null) {
@@ -798,6 +818,32 @@
}
}
+ void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId,
+ @Nullable Bundle clientState) {
+ synchronized (mLock) {
+ if (mAugmentedAutofillEventHistory == null
+ || mAugmentedAutofillEventHistory.getSessionId() != sessionId) {
+ return;
+ }
+ mAugmentedAutofillEventHistory.addEvent(
+ new Event(Event.TYPE_DATASET_SELECTED, suggestionId, clientState, null, null,
+ null, null, null, null, null, null));
+ }
+ }
+
+ void logAugmentedAutofillShown(int sessionId, @Nullable Bundle clientState) {
+ synchronized (mLock) {
+ if (mAugmentedAutofillEventHistory == null
+ || mAugmentedAutofillEventHistory.getSessionId() != sessionId) {
+ return;
+ }
+ mAugmentedAutofillEventHistory.addEvent(
+ new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
+ null, null, null, null, null));
+
+ }
+ }
+
/**
* Updates the last fill response when an autofill context is committed.
*/
@@ -881,8 +927,8 @@
* Gets the fill event history.
*
* @param callingUid The calling uid
- *
- * @return The history or {@code null} if there is none.
+ * @return The history for the autofill or the augmented autofill events depending on the {@code
+ * callingUid}, or {@code null} if there is none.
*/
FillEventHistory getFillEventHistory(int callingUid) {
synchronized (mLock) {
@@ -890,6 +936,10 @@
&& isCalledByServiceLocked("getFillEventHistory", callingUid)) {
return mEventHistory;
}
+ if (mAugmentedAutofillEventHistory != null && isCalledByAugmentedAutofillServiceLocked(
+ "getFillEventHistory", callingUid)) {
+ return mAugmentedAutofillEventHistory;
+ }
}
return null;
}
@@ -1163,8 +1213,32 @@
Slog.v(TAG, "getRemoteAugmentedAutofillServiceLocked(): " + componentName);
}
- mRemoteAugmentedAutofillService = new RemoteAugmentedAutofillService(getContext(),
- componentName, mUserId, new RemoteAugmentedAutofillServiceCallbacks() {
+ final RemoteAugmentedAutofillServiceCallbacks callbacks =
+ new RemoteAugmentedAutofillServiceCallbacks() {
+ @Override
+ public void resetLastResponse() {
+ AutofillManagerServiceImpl.this.resetLastAugmentedAutofillResponse();
+ }
+
+ @Override
+ public void setLastResponse(int sessionId) {
+ AutofillManagerServiceImpl.this.setLastAugmentedAutofillResponse(
+ sessionId);
+ }
+
+ @Override
+ public void logAugmentedAutofillShown(int sessionId, Bundle clientState) {
+ AutofillManagerServiceImpl.this.logAugmentedAutofillShown(sessionId,
+ clientState);
+ }
+
+ @Override
+ public void logAugmentedAutofillSelected(int sessionId, String suggestionId,
+ Bundle clientState) {
+ AutofillManagerServiceImpl.this.logAugmentedAutofillSelected(sessionId,
+ suggestionId, clientState);
+ }
+
@Override
public void onServiceDied(@NonNull RemoteAugmentedAutofillService service) {
Slog.w(TAG, "remote augmented autofill service died");
@@ -1175,8 +1249,10 @@
}
mRemoteAugmentedAutofillService = null;
}
- }, mMaster.isInstantServiceAllowed(), mMaster.verbose,
- mMaster.mAugmentedServiceIdleUnbindTimeoutMs,
+ };
+ mRemoteAugmentedAutofillService = new RemoteAugmentedAutofillService(getContext(),
+ componentName, mUserId, callbacks, mMaster.isInstantServiceAllowed(),
+ mMaster.verbose, mMaster.mAugmentedServiceIdleUnbindTimeoutMs,
mMaster.mAugmentedServiceRequestTimeoutMs);
}
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
index c7be80c..cb6c8f5 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
@@ -28,7 +28,6 @@
import android.view.SurfaceControl;
import android.view.View;
import android.view.autofill.AutofillId;
-import android.view.autofill.IAutoFillManagerClient;
import android.view.inputmethod.InlineSuggestion;
import android.view.inputmethod.InlineSuggestionInfo;
import android.view.inputmethod.InlineSuggestionsResponse;
@@ -49,15 +48,24 @@
private static final String TAG = "InlineSuggestionFactory";
/**
+ * Callback from the inline suggestion Ui.
+ */
+ public interface InlineSuggestionUiCallback {
+ /**
+ * Callback to autofill a dataset to the client app.
+ */
+ void autofill(@NonNull Dataset dataset);
+ }
+
+ /**
* Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by
* augmented autofill service.
*/
public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
- int sessionId,
@NonNull Dataset[] datasets,
@NonNull AutofillId autofillId,
@NonNull Context context,
- @NonNull IAutoFillManagerClient client) {
+ @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback) {
if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
@@ -74,8 +82,8 @@
Slog.w(TAG, "InlinePresentation not found in dataset");
return null;
}
- InlineSuggestion inlineSuggestion = createAugmentedInlineSuggestion(sessionId, dataset,
- inlinePresentation, inlineSuggestionUi, client);
+ InlineSuggestion inlineSuggestion = createAugmentedInlineSuggestion(dataset,
+ inlinePresentation, inlineSuggestionUi, inlineSuggestionUiCallback);
inlineSuggestions.add(inlineSuggestion);
}
return new InlineSuggestionsResponse(inlineSuggestions);
@@ -114,22 +122,17 @@
return new InlineSuggestionsResponse(inlineSuggestions);
}
- private static InlineSuggestion createAugmentedInlineSuggestion(int sessionId,
- @NonNull Dataset dataset,
+ private static InlineSuggestion createAugmentedInlineSuggestion(@NonNull Dataset dataset,
@NonNull InlinePresentation inlinePresentation,
@NonNull InlineSuggestionUi inlineSuggestionUi,
- @NonNull IAutoFillManagerClient client) {
+ @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback) {
// TODO(b/146453195): fill in the autofill hint properly.
final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
inlinePresentation.getInlinePresentationSpec(),
InlineSuggestionInfo.SOURCE_PLATFORM, new String[]{""},
InlineSuggestionInfo.TYPE_SUGGESTION);
final View.OnClickListener onClickListener = v -> {
- try {
- client.autofill(sessionId, dataset.getFieldIds(), dataset.getFieldValues());
- } catch (RemoteException e) {
- Slog.w(TAG, "Encounter exception autofilling the values");
- }
+ inlineSuggestionUiCallback.autofill(dataset);
};
final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 5fbdd25..5e6f6fea 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -69,6 +69,7 @@
private final int mIdleUnbindTimeoutMs;
private final int mRequestTimeoutMs;
private final ComponentName mComponentName;
+ private final RemoteAugmentedAutofillServiceCallbacks mCallbacks;
RemoteAugmentedAutofillService(Context context, ComponentName serviceName,
int userId, RemoteAugmentedAutofillServiceCallbacks callbacks,
@@ -81,6 +82,7 @@
mIdleUnbindTimeoutMs = idleUnbindTimeoutMs;
mRequestTimeoutMs = requestTimeoutMs;
mComponentName = serviceName;
+ mCallbacks = callbacks;
// Bind right away.
connect();
@@ -159,9 +161,12 @@
focusedId, focusedValue, requestTime, inlineSuggestionsRequest,
new IFillCallback.Stub() {
@Override
- public void onSuccess(@Nullable Dataset[] inlineSuggestionsData) {
- maybeHandleInlineSuggestions(sessionId, inlineSuggestionsData,
- focusedId, inlineSuggestionsCallback, client);
+ public void onSuccess(@Nullable Dataset[] inlineSuggestionsData,
+ @Nullable Bundle clientState) {
+ mCallbacks.resetLastResponse();
+ maybeRequestShowInlineSuggestions(sessionId,
+ inlineSuggestionsData, focusedId,
+ inlineSuggestionsCallback, client, clientState);
requestAutofill.complete(null);
}
@@ -223,20 +228,32 @@
});
}
- private void maybeHandleInlineSuggestions(int sessionId,
+ private void maybeRequestShowInlineSuggestions(int sessionId,
@Nullable Dataset[] inlineSuggestionsData, @NonNull AutofillId focusedId,
@Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
- @NonNull IAutoFillManagerClient client) {
+ @NonNull IAutoFillManagerClient client, @Nullable Bundle clientState) {
if (ArrayUtils.isEmpty(inlineSuggestionsData) || inlineSuggestionsCallback == null) {
return;
}
+ mCallbacks.setLastResponse(sessionId);
try {
inlineSuggestionsCallback.onInlineSuggestionsResponse(
- InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(sessionId,
- inlineSuggestionsData, focusedId, mContext, client));
+ InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
+ inlineSuggestionsData, focusedId, mContext,
+ dataset -> {
+ mCallbacks.logAugmentedAutofillSelected(sessionId,
+ dataset.getId(), clientState);
+ try {
+ client.autofill(sessionId, dataset.getFieldIds(),
+ dataset.getFieldValues());
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Encounter exception autofilling the values");
+ }
+ }));
} catch (RemoteException e) {
Slog.w(TAG, "Exception sending inline suggestions response back to IME.");
}
+ mCallbacks.logAugmentedAutofillShown(sessionId, clientState);
}
@Override
@@ -254,8 +271,13 @@
public interface RemoteAugmentedAutofillServiceCallbacks
extends AbstractRemoteService.VultureCallback<RemoteAugmentedAutofillService> {
- // NOTE: so far we don't need to notify the callback implementation (an inner class on
- // AutofillManagerServiceImpl) of the request results (success, timeouts, etc..), so this
- // callback interface is empty.
+ void resetLastResponse();
+
+ void setLastResponse(int sessionId);
+
+ void logAugmentedAutofillShown(int sessionId, @Nullable Bundle clientState);
+
+ void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId,
+ @Nullable Bundle clientState);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index ee37de5..415ecd8 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2674,7 +2674,9 @@
if (response.supportsInlineSuggestions()) {
if (requestShowInlineSuggestions(response)) {
- //TODO(b/137800469): Add logging instead of bypassing below logic.
+ //TODO(b/137800469): Fix it to log showed only when IME asks for inflation, rather
+ // than here where framework sends back the response.
+ mService.logDatasetShown(id, mClientState);
return;
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d933e9d..2982dc9 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -5537,20 +5537,6 @@
return nri.request.requestId == mDefaultRequest.requestId;
}
- // TODO : remove this method. It's a stopgap measure to help sheperding a number of dependent
- // changes that would conflict throughout the automerger graph. Having this method temporarily
- // helps with the process of going through with all these dependent changes across the entire
- // tree.
- /**
- * Register a new agent. {@see #registerNetworkAgent} below.
- */
- public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
- LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
- int currentScore, NetworkAgentConfig networkAgentConfig) {
- return registerNetworkAgent(messenger, networkInfo, linkProperties, networkCapabilities,
- currentScore, networkAgentConfig, NetworkProvider.ID_NONE);
- }
-
/**
* Register a new agent with ConnectivityService to handle a network.
*
@@ -5569,7 +5555,7 @@
*/
public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
- int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
+ NetworkScore currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
enforceNetworkFactoryPermission();
LinkProperties lp = new LinkProperties(linkProperties);
@@ -5577,12 +5563,10 @@
// TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
// satisfies mDefaultRequest.
final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
- final NetworkScore ns = new NetworkScore();
- ns.putIntExtension(NetworkScore.LEGACY_SCORE, currentScore);
final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
- ns, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig), this,
- mNetd, mDnsResolver, mNMS, providerId);
+ currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
+ this, mNetd, mDnsResolver, mNMS, providerId);
// Make sure the network capabilities reflect what the agent info says.
nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
final String extraInfo = networkInfo.getExtraInfo();
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index e342dd7..50843b0 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -2644,7 +2644,12 @@
if (noGPSLocation == null && lastNoGPSLocation != null) {
// New location has no no-GPS location: adopt last no-GPS location. This is set
// directly into location because we do not want to notify COARSE clients.
- location.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, lastNoGPSLocation);
+ Bundle extras = location.getExtras();
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ extras.putParcelable(Location.EXTRA_NO_GPS_LOCATION, lastNoGPSLocation);
+ location.setExtras(extras);
}
}
lastLocation.set(location);
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 9d4c783..1bb3c3a 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -86,12 +86,12 @@
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
-import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.HexDump;
import com.android.internal.util.Preconditions;
@@ -412,8 +412,8 @@
getBatteryStats().noteMobileRadioPowerState(powerState, tsNanos, uid);
} catch (RemoteException e) {
}
- StatsLog.write_non_chained(StatsLog.MOBILE_RADIO_POWER_STATE_CHANGED, uid, null,
- powerState);
+ FrameworkStatsLog.write_non_chained(
+ FrameworkStatsLog.MOBILE_RADIO_POWER_STATE_CHANGED, uid, null, powerState);
}
}
@@ -424,8 +424,8 @@
getBatteryStats().noteWifiRadioPowerState(powerState, tsNanos, uid);
} catch (RemoteException e) {
}
- StatsLog.write_non_chained(StatsLog.WIFI_RADIO_POWER_STATE_CHANGED, uid, null,
- powerState);
+ FrameworkStatsLog.write_non_chained(
+ FrameworkStatsLog.WIFI_RADIO_POWER_STATE_CHANGED, uid, null, powerState);
}
}
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index 14cd3a5..b43ae36 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -49,6 +49,8 @@
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.Preconditions;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Callable;
@@ -183,9 +185,7 @@
// write from handler thread only, read anywhere
private volatile ServiceInfo mServiceInfo;
-
- // read/write from handler thread only
- private IBinder mBinder;
+ private volatile IBinder mBinder;
public ServiceWatcher(Context context, Handler handler, String action,
@Nullable BinderRunner onBind, @Nullable Runnable onUnbind,
@@ -345,20 +345,25 @@
}
mBinder = binder;
+
+ // we always run the on bind callback even if we know that the binder is dead already so
+ // that there are always balance pairs of bind/unbind callbacks
if (mOnBind != null) {
- runOnBinder(mOnBind);
- }
- }
-
- @Override
- public void onBindingDied(ComponentName component) {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
- if (D) {
- Log.i(TAG, getLogPrefix() + " " + component.toShortString() + " died");
+ try {
+ mOnBind.run(binder);
+ } catch (RuntimeException | RemoteException e) {
+ // binders may propagate some specific non-RemoteExceptions from the other side
+ // through the binder as well - we cannot allow those to crash the system server
+ Log.e(TAG, getLogPrefix() + " exception running on " + mServiceInfo, e);
+ }
}
- onBestServiceChanged(true);
+ try {
+ // setting the binder to null lets us skip queued transactions
+ binder.linkToDeath(() -> mBinder = null, 0);
+ } catch (RemoteException e) {
+ mBinder = null;
+ }
}
@Override
@@ -375,6 +380,17 @@
}
}
+ @Override
+ public void onBindingDied(ComponentName component) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+ if (D) {
+ Log.i(TAG, getLogPrefix() + " " + component.toShortString() + " died");
+ }
+
+ onBestServiceChanged(true);
+ }
+
private void onUserSwitched(@UserIdInt int userId) {
mCurrentUserId = userId;
onBestServiceChanged(false);
@@ -398,7 +414,7 @@
* RemoteException thrown during execution.
*/
public final void runOnBinder(BinderRunner runner) {
- runOnHandler(() -> {
+ mHandler.post(() -> {
if (mBinder == null) {
return;
}
@@ -447,14 +463,6 @@
}
}
- private void runOnHandler(Runnable r) {
- if (Looper.myLooper() == mHandler.getLooper()) {
- r.run();
- } else {
- mHandler.post(r);
- }
- }
-
private <T> T runOnHandlerBlocking(Callable<T> c)
throws InterruptedException, TimeoutException {
if (Looper.myLooper() == mHandler.getLooper()) {
@@ -489,4 +497,12 @@
public String toString() {
return mServiceInfo.toString();
}
+
+ /**
+ * Dump for debugging.
+ */
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("service=" + mServiceInfo);
+ pw.println("connected=" + (mBinder != null));
+ }
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 36dd017..139a871 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -897,6 +897,13 @@
if ((events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
!= 0) {
updateReportSignalStrengthDecision(r.subId);
+ try {
+ if (mSignalStrength[phoneId] != null) {
+ r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
+ }
+ } catch (RemoteException ex) {
+ remove(r.binder);
+ }
}
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) {
try {
@@ -1326,9 +1333,10 @@
log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId
+ " phoneId=" + phoneId + " ss=" + signalStrength);
}
- if (r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) &&
- idMatch(r.subId, subId, phoneId)) {
+ if ((r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SIGNAL_STRENGTHS)
+ || r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH))
+ && idMatch(r.subId, subId, phoneId)) {
try {
if (DBG) {
log("notifySignalStrengthForPhoneId: callback.onSsS r=" + r
@@ -1341,7 +1349,7 @@
}
}
if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SIGNAL_STRENGTH) &&
- idMatch(r.subId, subId, phoneId)){
+ idMatch(r.subId, subId, phoneId)) {
try {
int gsmSignalStrength = signalStrength.getGsmSignalStrength();
int ss = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
@@ -2542,6 +2550,11 @@
android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
}
+ if ((events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH, null);
+ }
+
if ((events & READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK) != 0) {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
@@ -2657,7 +2670,8 @@
}
}
- if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
+ if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0
+ || (events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) {
try {
if (mSignalStrength[phoneId] != null) {
SignalStrength signalStrength = mSignalStrength[phoneId];
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8cf620d..6560777 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -355,7 +355,6 @@
import com.android.server.firewall.IntentFirewall;
import com.android.server.job.JobSchedulerInternal;
import com.android.server.pm.Installer;
-import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.uri.GrantUri;
import com.android.server.uri.UriGrantsManagerInternal;
@@ -5270,26 +5269,6 @@
mCallFinishBooting = false;
}
- ArraySet<String> completedIsas = new ArraySet<String>();
- for (String abi : Build.SUPPORTED_ABIS) {
- ZYGOTE_PROCESS.establishZygoteConnectionForAbi(abi);
- final String instructionSet = VMRuntime.getInstructionSet(abi);
- if (!completedIsas.contains(instructionSet)) {
- try {
- mInstaller.markBootComplete(VMRuntime.getInstructionSet(abi));
- } catch (InstallerException e) {
- if (!VMRuntime.didPruneDalvikCache()) {
- // This is technically not the right filter, as different zygotes may
- // have made different pruning decisions. But the log is best effort,
- // anyways.
- Slog.w(TAG, "Unable to mark boot complete for abi: " + abi + " (" +
- e.getMessage() +")");
- }
- }
- completedIsas.add(instructionSet);
- }
- }
-
// Let the ART runtime in zygote and system_server know that the boot completed.
ZYGOTE_PROCESS.bootCompleted();
VMRuntime.bootCompleted();
@@ -6876,6 +6855,7 @@
}
}
+ ProcessRecord dyingProc = null;
if (cpr != null && cpr.proc != null) {
providerRunning = !cpr.proc.killed;
@@ -6885,14 +6865,9 @@
// (See the commit message on I2c4ba1e87c2d47f2013befff10c49b3dc337a9a7 to see
// how to test this case.)
if (cpr.proc.killed && cpr.proc.killedByAm) {
- final long iden = Binder.clearCallingIdentity();
- try {
- mProcessList.killProcAndWaitIfNecessaryLocked(cpr.proc, false,
- cpr.uid == cpr.proc.uid || cpr.proc.isolated,
- "getContentProviderImpl: %s (killedByAm)", startTime);
- } finally {
- Binder.restoreCallingIdentity(iden);
- }
+ Slog.wtf(TAG, cpr.proc.toString() + " was killed by AM but isn't really dead");
+ // Now we are going to wait for the death before starting the new process.
+ dyingProc = cpr.proc;
}
}
@@ -6993,18 +6968,18 @@
// has been killed on us. We need to wait for a new
// process to be started, and make sure its death
// doesn't kill our process.
- Slog.i(TAG, "Existing provider " + cpr.name.flattenToShortString()
+ Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
+ " is crashing; detaching " + r);
boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
- mProcessList.killProcAndWaitIfNecessaryLocked(cpr.proc,
- false, true, "getContentProviderImpl: %s", startTime);
if (!lastRef) {
// This wasn't the last ref our process had on
- // the provider... we have now been killed, bail.
+ // the provider... we will be killed during cleaning up, bail.
return null;
}
+ // We'll just start a new process to host the content provider
providerRunning = false;
conn = null;
+ dyingProc = cpr.proc;
} else {
cpr.proc.verifiedAdj = cpr.proc.setAdj;
}
@@ -7080,7 +7055,7 @@
checkTime(startTime, "getContentProviderImpl: before getProviderByClass");
cpr = mProviderMap.getProviderByClass(comp, userId);
checkTime(startTime, "getContentProviderImpl: after getProviderByClass");
- final boolean firstClass = cpr == null;
+ boolean firstClass = cpr == null;
if (firstClass) {
final long ident = Binder.clearCallingIdentity();
@@ -7111,6 +7086,13 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
+ } else if (dyingProc == cpr.proc) {
+ // The old stable connection's client should be killed during proc cleaning up,
+ // so do not re-use the old ContentProviderRecord, otherwise the new clients
+ // could get killed unexpectedly.
+ cpr = new ContentProviderRecord(cpr);
+ // This is sort of "firstClass"
+ firstClass = true;
}
checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord");
@@ -14270,10 +14252,19 @@
cpr.launchingApp = null;
cpr.notifyAll();
}
- mProviderMap.removeProviderByClass(cpr.name, UserHandle.getUserId(cpr.uid));
+ final int userId = UserHandle.getUserId(cpr.uid);
+ // Don't remove from provider map if it doesn't match
+ // could be a new content provider is starting
+ if (mProviderMap.getProviderByClass(cpr.name, userId) == cpr) {
+ mProviderMap.removeProviderByClass(cpr.name, userId);
+ }
String names[] = cpr.info.authority.split(";");
for (int j = 0; j < names.length; j++) {
- mProviderMap.removeProviderByName(names[j], UserHandle.getUserId(cpr.uid));
+ // Don't remove from provider map if it doesn't match
+ // could be a new content provider is starting
+ if (mProviderMap.getProviderByName(names[j], userId) == cpr) {
+ mProviderMap.removeProviderByName(names[j], userId);
+ }
}
}
@@ -14368,6 +14359,10 @@
// Remove published content providers.
for (int i = app.pubProviders.size() - 1; i >= 0; i--) {
ContentProviderRecord cpr = app.pubProviders.valueAt(i);
+ if (cpr.proc != app) {
+ // If the hosting process record isn't really us, bail out
+ continue;
+ }
final boolean alwaysRemove = app.bad || !allowRestart;
final boolean inLaunching = removeDyingProviderLocked(app, cpr, alwaysRemove);
if (!alwaysRemove && inLaunching && cpr.hasConnectionOrHandle()) {
@@ -14453,6 +14448,27 @@
mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED_UI_MSG, app.pid, app.info.uid,
null).sendToTarget();
+ // If this is a precede instance of another process instance
+ allowRestart = true;
+ synchronized (app) {
+ if (app.mSuccessor != null) {
+ // We don't allow restart with this ProcessRecord now,
+ // because we have created a new one already.
+ allowRestart = false;
+ // If it's persistent, add the successor to mPersistentStartingProcesses
+ if (app.isPersistent() && !app.removed) {
+ if (mPersistentStartingProcesses.indexOf(app.mSuccessor) < 0) {
+ mPersistentStartingProcesses.add(app.mSuccessor);
+ }
+ }
+ // clean up the field so the successor's proc starter could proceed.
+ app.mSuccessor.mPrecedence = null;
+ app.mSuccessor = null;
+ // Notify if anyone is waiting for it.
+ app.notifyAll();
+ }
+ }
+
// If the caller is restarting this app, then leave it in its
// current lists and let the caller take care of it.
if (restarting) {
@@ -14482,7 +14498,7 @@
mAtmInternal.onCleanUpApplicationRecord(app.getWindowProcessController());
mProcessList.noteProcessDiedLocked(app);
- if (restart && !app.isolated) {
+ if (restart && allowRestart && !app.isolated) {
// We have components that still need to be running in the
// process, so re-launch it.
if (index < 0) {
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index bab133f..60aba27 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import static android.app.ActivityManager.RunningAppProcessInfo.procStateToImportance;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -52,6 +53,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ProcessMap;
import com.android.server.IoThread;
+import com.android.server.ServiceThread;
import com.android.server.SystemServiceManager;
import java.io.File;
@@ -179,9 +181,12 @@
mRawRecordsPool = new SynchronizedPool<ApplicationExitInfo>(APP_EXIT_RAW_INFO_POOL_SIZE);
}
- void init(ActivityManagerService service, Looper looper) {
+ void init(ActivityManagerService service) {
mService = service;
- mKillHandler = new KillHandler(looper);
+ ServiceThread thread = new ServiceThread(TAG + ":killHandler",
+ THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
+ thread.start();
+ mKillHandler = new KillHandler(thread.getLooper());
mProcExitInfoFile = new File(SystemServiceManager.ensureSystemDir(), APP_EXIT_INFO_FILE);
mAppExitInfoHistoryListSize = service.mContext.getResources().getInteger(
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index d5fc14b..39f79ca 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -550,7 +550,6 @@
return data;
}
}
- Slog.e(TAG, "no controller energy info supplied for " + receiver.getName());
} catch (TimeoutException e) {
Slog.w(TAG, "timeout reading " + receiver.getName() + " stats");
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 7c36a7e..3c7d6b8 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -739,7 +739,7 @@
mSystemServerSocketForZygote.getFileDescriptor(),
EVENT_INPUT, this::handleZygoteMessages);
}
- mAppExitInfoTracker.init(mService, sKillThread.getLooper());
+ mAppExitInfoTracker.init(mService);
mImperceptibleKillRunner = new ImperceptibleKillRunner(sKillThread.getLooper());
}
}
@@ -1842,26 +1842,9 @@
if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
if (DEBUG_PROCESSES) Slog.i(TAG_PROCESSES,
"Posting procStart msg for " + app.toShortString());
- mService.mProcStartHandler.post(() -> {
- try {
- final Process.ProcessStartResult startResult = startProcess(app.hostingRecord,
- entryPoint, app, app.startUid, gids, runtimeFlags, mountExternal,
- app.seInfo, requiredAbi, instructionSet, invokeWith, app.startTime);
- synchronized (mService) {
- handleProcessStartedLocked(app, startResult, startSeq);
- }
- } catch (RuntimeException e) {
- synchronized (mService) {
- Slog.e(ActivityManagerService.TAG, "Failure starting process "
- + app.processName, e);
- mPendingStarts.remove(startSeq);
- app.pendingStart = false;
- mService.forceStopPackageLocked(app.info.packageName,
- UserHandle.getAppId(app.uid),
- false, false, true, false, false, app.userId, "start failure");
- }
- }
- });
+ mService.mProcStartHandler.post(() -> handleProcessStart(
+ app, entryPoint, gids, runtimeFlags, mountExternal, requiredAbi,
+ instructionSet, invokeWith, startSeq));
return true;
} else {
try {
@@ -1882,6 +1865,66 @@
}
}
+ /**
+ * Main handler routine to start the given process from the ProcStartHandler.
+ *
+ * <p>Note: this function doesn't hold the global AM lock intentionally.</p>
+ */
+ private void handleProcessStart(final ProcessRecord app, final String entryPoint,
+ final int[] gids, final int runtimeFlags, final int mountExternal,
+ final String requiredAbi, final String instructionSet,
+ final String invokeWith, final long startSeq) {
+ // If there is a precede instance of the process, wait for its death with a timeout.
+ // Use local reference since we are not using locks here
+ final ProcessRecord precedence = app.mPrecedence;
+ if (precedence != null) {
+ final int pid = precedence.pid;
+ long now = System.currentTimeMillis();
+ final long end = now + PROC_KILL_TIMEOUT;
+ try {
+ Process.waitForProcessDeath(pid, PROC_KILL_TIMEOUT);
+ // It's killed successfully, but we'd make sure the cleanup work is done.
+ synchronized (precedence) {
+ if (app.mPrecedence != null) {
+ now = System.currentTimeMillis();
+ if (now < end) {
+ try {
+ precedence.wait(end - now);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ if (app.mPrecedence != null) {
+ // The cleanup work hasn't be done yet, let's log it and continue.
+ Slog.w(TAG, precedence + " has died, but its cleanup isn't done");
+ }
+ }
+ } catch (Exception e) {
+ // It's still alive...
+ Slog.wtf(TAG, precedence.toString() + " refused to die, but we need to launch "
+ + app);
+ }
+ }
+ try {
+ final Process.ProcessStartResult startResult = startProcess(app.hostingRecord,
+ entryPoint, app, app.startUid, gids, runtimeFlags, mountExternal,
+ app.seInfo, requiredAbi, instructionSet, invokeWith, app.startTime);
+ synchronized (mService) {
+ handleProcessStartedLocked(app, startResult, startSeq);
+ }
+ } catch (RuntimeException e) {
+ synchronized (mService) {
+ Slog.e(ActivityManagerService.TAG, "Failure starting process "
+ + app.processName, e);
+ mPendingStarts.remove(startSeq);
+ app.pendingStart = false;
+ mService.forceStopPackageLocked(app.info.packageName,
+ UserHandle.getAppId(app.uid),
+ false, false, true, false, false, app.userId, "start failure");
+ }
+ }
+ }
+
@GuardedBy("mService")
public void killAppZygoteIfNeededLocked(AppZygote appZygote, boolean force) {
final ApplicationInfo appInfo = appZygote.getAppInfo();
@@ -2136,6 +2179,7 @@
+ " app=" + app + " knownToBeDead=" + knownToBeDead
+ " thread=" + (app != null ? app.thread : null)
+ " pid=" + (app != null ? app.pid : -1));
+ ProcessRecord precedence = null;
if (app != null && app.pid > 0) {
if ((!knownToBeDead && !app.killed) || app.thread == null) {
// We already have the app running, or are waiting for it to
@@ -2150,9 +2194,15 @@
// An application record is attached to a previous process,
// clean it up now.
if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App died: " + app);
- // do the killing
- killProcAndWaitIfNecessaryLocked(app, true, app.uid == info.uid || app.isolated,
- "startProcess: bad proc running, killing: %s", startTime);
+ checkSlow(startTime, "startProcess: bad proc running, killing");
+ ProcessList.killProcessGroup(app.uid, app.pid);
+ checkSlow(startTime, "startProcess: done killing old proc");
+
+ Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+ // We are not going to re-use the ProcessRecord, as we haven't dealt with the cleanup
+ // routine of it yet, but we'd set it as the precedence of the new process.
+ precedence = app;
+ app = null;
}
if (app == null) {
@@ -2166,6 +2216,10 @@
app.crashHandler = crashHandler;
app.isolatedEntryPoint = entryPoint;
app.isolatedEntryPointArgs = entryPointArgs;
+ if (precedence != null) {
+ app.mPrecedence = precedence;
+ precedence.mSuccessor = app;
+ }
checkSlow(startTime, "startProcess: done creating new process record");
} else {
// If this is a new package in the process, add the package to the list
@@ -2193,44 +2247,6 @@
return success ? app : null;
}
- /**
- * Kill (if asked to) and wait for the given process died if necessary
- * @param app - The process record to kill
- * @param doKill - Kill the given process record
- * @param wait - Wait for the death of the given process
- * @param formatString - The log message for slow operation
- * @param startTime - The start timestamp of the operation
- */
- @GuardedBy("mService")
- void killProcAndWaitIfNecessaryLocked(final ProcessRecord app, final boolean doKill,
- final boolean wait, final String formatString, final long startTime) {
-
- checkSlow(startTime, String.format(formatString, "before appDied"));
-
- if (doKill) {
- // do the killing
- ProcessList.killProcessGroup(app.uid, app.pid);
- noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_UNKNOWN,
- String.format(formatString, ""));
- }
-
- // wait for the death
- if (wait) {
- try {
- Process.waitForProcessDeath(app.pid, PROC_KILL_TIMEOUT);
- } catch (Exception e) {
- // Maybe the process goes into zombie, use an expensive API to check again.
- if (mService.isProcessAliveLocked(app)) {
- Slog.w(TAG, String.format(formatString,
- "waiting for app killing timed out"));
- }
- }
- }
-
- checkSlow(startTime, String.format(formatString, "after appDied"));
- }
-
@GuardedBy("mService")
private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
StringBuilder sb = null;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b0c0aae..fc33c25 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -320,6 +320,14 @@
// set of disabled compat changes for the process (all others are enabled)
long[] mDisabledCompatChanges;
+ // The precede instance of the process, which would exist when the previous process is killed
+ // but not fully dead yet; in this case, the new instance of the process should be held until
+ // this precede instance is fully dead.
+ volatile ProcessRecord mPrecedence;
+ // The succeeding instance of the process, which is going to be started after this process
+ // is killed successfully.
+ volatile ProcessRecord mSuccessor;
+
// Cached task info for OomAdjuster
private static final int VALUE_INVALID = -1;
private static final int VALUE_FALSE = 0;
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 8d26176..7f7c9c4 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -35,6 +35,7 @@
import com.android.internal.compat.OverrideAllowedState;
import com.android.server.compat.config.Change;
import com.android.server.compat.config.XmlParser;
+import com.android.server.pm.ApexManager;
import org.xmlpull.v1.XmlPullParserException;
@@ -45,6 +46,7 @@
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import javax.xml.datatype.DatatypeConfigurationException;
@@ -369,12 +371,18 @@
Environment.getRootDirectory(), "etc", "compatconfig"));
config.initConfigFromLib(Environment.buildPath(
Environment.getRootDirectory(), "system_ext", "etc", "compatconfig"));
+
+ List<ApexManager.ActiveApexInfo> apexes = ApexManager.getInstance().getActiveApexInfos();
+ for (ApexManager.ActiveApexInfo apex : apexes) {
+ config.initConfigFromLib(Environment.buildPath(
+ apex.apexDirectory, "etc", "compatconfig"));
+ }
return config;
}
void initConfigFromLib(File libraryDir) {
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
- Slog.e(TAG, "No directory " + libraryDir + ", skipping");
+ Slog.d(TAG, "No directory " + libraryDir + ", skipping");
return;
}
for (File f : libraryDir.listFiles()) {
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 4d5af9a..bb8b12e 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -16,11 +16,6 @@
package com.android.server.compat;
-import static android.Manifest.permission.LOG_COMPAT_CHANGE;
-import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG;
-import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.content.Context;
@@ -73,14 +68,12 @@
@Override
public void reportChange(long changeId, ApplicationInfo appInfo) {
- checkCompatChangeLogPermission();
reportChange(changeId, appInfo.uid,
StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
}
@Override
public void reportChangeByPackageName(long changeId, String packageName, int userId) {
- checkCompatChangeLogPermission();
ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
if (appInfo == null) {
return;
@@ -90,13 +83,11 @@
@Override
public void reportChangeByUid(long changeId, int uid) {
- checkCompatChangeLogPermission();
reportChange(changeId, uid, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
}
@Override
public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
- checkCompatChangeReadPermission();
if (mCompatConfig.isChangeEnabled(changeId, appInfo)) {
reportChange(changeId, appInfo.uid,
StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
@@ -109,7 +100,6 @@
@Override
public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) {
- checkCompatChangeReadPermission();
ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
if (appInfo == null) {
return true;
@@ -119,7 +109,6 @@
@Override
public boolean isChangeEnabledByUid(long changeId, int uid) {
- checkCompatChangeReadPermission();
String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
if (packages == null || packages.length == 0) {
return true;
@@ -152,7 +141,6 @@
@Override
public void setOverrides(CompatibilityChangeConfig overrides, String packageName)
throws RemoteException, SecurityException {
- checkCompatChangeOverridePermission();
mCompatConfig.addOverrides(overrides, packageName);
killPackage(packageName);
}
@@ -160,13 +148,11 @@
@Override
public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName)
throws RemoteException, SecurityException {
- checkCompatChangeOverridePermission();
mCompatConfig.addOverrides(overrides, packageName);
}
@Override
public void clearOverrides(String packageName) throws RemoteException, SecurityException {
- checkCompatChangeOverridePermission();
mCompatConfig.removePackageOverrides(packageName);
killPackage(packageName);
}
@@ -174,14 +160,12 @@
@Override
public void clearOverridesForTest(String packageName)
throws RemoteException, SecurityException {
- checkCompatChangeOverridePermission();
mCompatConfig.removePackageOverrides(packageName);
}
@Override
public boolean clearOverride(long changeId, String packageName)
throws RemoteException, SecurityException {
- checkCompatChangeOverridePermission();
boolean existed = mCompatConfig.removeOverride(changeId, packageName);
killPackage(packageName);
return existed;
@@ -189,13 +173,11 @@
@Override
public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) {
- checkCompatChangeReadPermission();
return mCompatConfig.getAppConfig(appInfo);
}
@Override
public CompatibilityChangeInfo[] listAllChanges() {
- checkCompatChangeReadPermission();
return mCompatConfig.dumpChanges();
}
@@ -234,7 +216,6 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- checkCompatChangeReadPermission();
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
mCompatConfig.dumpConfig(pw);
}
@@ -292,25 +273,4 @@
Binder.restoreCallingIdentity(identity);
}
}
-
- private void checkCompatChangeLogPermission() throws SecurityException {
- if (mContext.checkCallingOrSelfPermission(LOG_COMPAT_CHANGE)
- != PERMISSION_GRANTED) {
- throw new SecurityException("Cannot log compat change usage");
- }
- }
-
- private void checkCompatChangeReadPermission() throws SecurityException {
- if (mContext.checkCallingOrSelfPermission(READ_COMPAT_CHANGE_CONFIG)
- != PERMISSION_GRANTED) {
- throw new SecurityException("Cannot read compat change");
- }
- }
-
- private void checkCompatChangeOverridePermission() throws SecurityException {
- if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG)
- != PERMISSION_GRANTED) {
- throw new SecurityException("Cannot override compat change");
- }
- }
}
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 17e2f69..f3d2012 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -35,11 +35,11 @@
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
-import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.BitUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.RingBuffer;
import com.android.internal.util.TokenBucket;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
@@ -278,7 +278,7 @@
addWakeupEvent(event);
String dstMac = event.dstHwAddr.toString();
- StatsLog.write(StatsLog.PACKET_WAKEUP_OCCURRED,
+ FrameworkStatsLog.write(FrameworkStatsLog.PACKET_WAKEUP_OCCURRED,
uid, iface, ethertype, dstMac, srcIp, dstIp, ipNextHeader, srcPort, dstPort);
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index d66aec5..3cfe916 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -483,7 +483,7 @@
return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
}
- int score = mNetworkScore.getIntExtension(NetworkScore.LEGACY_SCORE);
+ int score = mNetworkScore.getLegacyScore();
if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
}
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index decb1f9..96532f4 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -1050,6 +1050,7 @@
public void dumpLocked(PrintWriter pw) {
pw.println(" BrightnessObserver");
+ pw.println(" mAmbientLux: " + mAmbientLux);
pw.println(" mRefreshRateInZone: " + mRefreshRateInZone);
for (int d: mDisplayBrightnessThresholds) {
@@ -1059,6 +1060,8 @@
for (int d: mAmbientBrightnessThresholds) {
pw.println(" mAmbientBrightnessThreshold: " + d);
}
+
+ mLightSensorListener.dumpLocked(pw);
}
public void onDisplayChanged(int displayId) {
@@ -1222,6 +1225,10 @@
final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
private float mLastSensorData;
+ public void dumpLocked(PrintWriter pw) {
+ pw.println(" mLastSensorData: " + mLastSensorData);
+ }
+
@Override
public void onSensorChanged(SensorEvent event) {
mLastSensorData = event.values[0];
diff --git a/services/core/java/com/android/server/location/LocationFudger.java b/services/core/java/com/android/server/location/LocationFudger.java
index ae71fe3..04c7714 100644
--- a/services/core/java/com/android/server/location/LocationFudger.java
+++ b/services/core/java/com/android/server/location/LocationFudger.java
@@ -16,17 +16,19 @@
package com.android.server.location;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.security.SecureRandom;
import android.content.Context;
import android.database.ContentObserver;
import android.location.Location;
+import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.security.SecureRandom;
+
/**
* Contains the logic to obfuscate (fudge) locations for coarse applications.
@@ -177,7 +179,12 @@
private Location addCoarseLocationExtraLocked(Location location) {
Location coarse = createCoarseLocked(location);
- location.setExtraLocation(Location.EXTRA_COARSE_LOCATION, coarse);
+ Bundle extras = location.getExtras();
+ if (extras == null) {
+ extras = new Bundle();
+ }
+ extras.putParcelable(Location.EXTRA_COARSE_LOCATION, coarse);
+ location.setExtras(extras);
return coarse;
}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index 19fb669..96ffaa6 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -139,13 +139,13 @@
@GuardedBy("mLock")
private boolean mBound;
- @GuardedBy("mLock")
- private ProviderRequest mRequest;
+
+ private volatile ProviderRequest mRequest;
private LocationProviderProxy(Context context, String action, int enableOverlayResId,
int nonOverlayPackageResId) {
- // safe to use direct executor even though this class has internal locks - all of our
- // callbacks go to oneway binder transactions which cannot possibly be re-entrant
+ // safe to use direct executor since our locks are not acquired in a code path invoked by
+ // our owning provider
super(DIRECT_EXECUTOR, Collections.emptySet());
mContext = context;
@@ -167,8 +167,10 @@
mBound = true;
provider.setLocationProviderManager(mManager);
- if (!mRequest.equals(ProviderRequest.EMPTY_REQUEST)) {
- provider.setRequest(mRequest, mRequest.workSource);
+
+ ProviderRequest request = mRequest;
+ if (!request.equals(ProviderRequest.EMPTY_REQUEST)) {
+ provider.setRequest(request, request.workSource);
}
ComponentName service = mServiceWatcher.getBoundService().component;
@@ -187,10 +189,7 @@
@Override
public void onSetRequest(ProviderRequest request) {
- synchronized (mLock) {
- mRequest = request;
- }
-
+ mRequest = request;
mServiceWatcher.runOnBinder(binder -> {
ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
service.setRequest(request, request.workSource);
@@ -215,9 +214,6 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("service=" + mServiceWatcher);
- synchronized (mLock) {
- pw.println("bound=" + mBound);
- }
+ mServiceWatcher.dump(fd, pw, args);
}
}
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 1cd8aad..5123362 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -36,6 +36,7 @@
final Object mLock = new Object();
Callback mCallback;
+ boolean mIsSystemRouteProvider;
private volatile MediaRoute2ProviderInfo mProviderInfo;
@GuardedBy("mLock")
@@ -85,6 +86,7 @@
} else {
mProviderInfo = new MediaRoute2ProviderInfo.Builder(providerInfo)
.setUniqueId(mUniqueId)
+ .setSystemRouteProvider(mIsSystemRouteProvider)
.build();
}
}
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 558eb8d..924a9b7 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -83,6 +83,8 @@
super(sComponentName);
setCallback(callback);
+ mIsSystemRouteProvider = true;
+
mContext = context;
mHandler = new Handler(Looper.getMainLooper());
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7104790..68cc014 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2735,7 +2735,7 @@
// TODO(b/144152069): Remove informative toast
mUiHandler.post(() -> Toast.makeText(getContext(),
"Background custom toast blocked for package " + pkg + ".\n"
- + "See go/r-toast-block.",
+ + "See g.co/dev/toast.",
Toast.LENGTH_SHORT).show());
Slog.w(TAG, "Blocking custom toast from package " + pkg
+ " due to package not in the foreground");
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 90afeff..4ff37a2 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -95,6 +95,7 @@
* Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerFlattenedApex}
* depending on whether this device supports APEX, i.e. {@link ApexProperties#updatable()}
* evaluates to {@code true}.
+ * @hide
*/
public static ApexManager getInstance() {
return sApexManagerSingleton.get();
@@ -102,8 +103,9 @@
/**
* Minimal information about APEX mount points and the original APEX package they refer to.
+ * @hide
*/
- static class ActiveApexInfo {
+ public static class ActiveApexInfo {
@Nullable public final String apexModuleName;
public final File apexDirectory;
public final File preInstalledApexPath;
@@ -130,8 +132,10 @@
/**
* Returns {@link ActiveApexInfo} records relative to all active APEX packages.
+ *
+ * @hide
*/
- abstract List<ActiveApexInfo> getActiveApexInfos();
+ public abstract List<ActiveApexInfo> getActiveApexInfos();
abstract void systemReady(Context context);
@@ -362,7 +366,7 @@
}
@Override
- List<ActiveApexInfo> getActiveApexInfos() {
+ public List<ActiveApexInfo> getActiveApexInfos() {
synchronized (mLock) {
if (mActiveApexInfosCache == null) {
try {
@@ -798,7 +802,7 @@
*/
private static final class ApexManagerFlattenedApex extends ApexManager {
@Override
- List<ActiveApexInfo> getActiveApexInfos() {
+ public List<ActiveApexInfo> getActiveApexInfos() {
// There is no apexd running in case of flattened apex
// We look up the /apex directory and identify the active APEX modules from there.
// As "preinstalled" path, we just report /system since in the case of flattened APEX
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index c712431..ba7583f 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -36,9 +36,9 @@
import android.os.storage.StorageManager;
import android.util.ArraySet;
import android.util.Log;
-import android.util.StatsLog;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.PinnerService;
import com.android.server.pm.dex.DexManager;
@@ -444,7 +444,7 @@
}
if (dex_opt_performed) {
- StatsLog.write(StatsLog.APP_DOWNGRADED, pkg, package_size_before,
+ FrameworkStatsLog.write(FrameworkStatsLog.APP_DOWNGRADED, pkg, package_size_before,
getPackageSize(pm, pkg), /*aggressive=*/ false);
}
return dex_opt_performed;
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index f962eed..40ea6cf 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -450,16 +450,6 @@
}
}
- public void markBootComplete(String instructionSet) throws InstallerException {
- assertValidInstructionSet(instructionSet);
- if (!checkBeforeRemote()) return;
- try {
- mInstalld.markBootComplete(instructionSet);
- } catch (Exception e) {
- throw InstallerException.from(e);
- }
- }
-
public void freeCache(String uuid, long targetFreeBytes, long cacheReservedBytes, int flags)
throws InstallerException {
if (!checkBeforeRemote()) return;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 02ec472..148c5ad 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -288,7 +288,6 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
-import android.util.StatsLog;
import android.util.TimingsTraceLog;
import android.util.Xml;
import android.util.apk.ApkSignatureVerifier;
@@ -310,6 +309,7 @@
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.server.AttributeCache;
@@ -2163,7 +2163,8 @@
getPackageExternalStorageType(volume, isExternal(res.pkg));
// If the package was installed externally, log it.
if (packageExternalStorageType != StorageEnums.UNKNOWN) {
- StatsLog.write(StatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
packageExternalStorageType, res.pkg.getPackageName());
}
}
@@ -2727,7 +2728,7 @@
t.traceBegin("get system config");
SystemConfig systemConfig = SystemConfig.getInstance();
mAvailableFeatures = systemConfig.getAvailableFeatures();
- ApplicationPackageManager.invalidateSysFeatureCache();
+ ApplicationPackageManager.invalidateHasSystemFeatureCache();
t.traceEnd();
mProtectedPackages = new ProtectedPackages(mContext);
@@ -19706,9 +19707,7 @@
if (packageName == null) {
return null;
}
- if (getPackageInfo(packageName, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE
- | MATCH_DIRECT_BOOT_UNAWARE | MATCH_DISABLED_COMPONENTS,
- UserHandle.getCallingUserId()) == null) {
+ if (getPackageInfo(packageName, MATCH_FACTORY_ONLY, UserHandle.USER_SYSTEM) == null) {
return null;
}
return packageName;
@@ -22179,13 +22178,15 @@
if (!isPreviousLocationExternal && isExternal(pkg)) {
// Move from internal to external storage.
- StatsLog.write(StatsLog.APP_MOVED_STORAGE_REPORTED, packageExternalStorageType,
- StatsLog.APP_MOVED_STORAGE_REPORTED__MOVE_TYPE__TO_EXTERNAL,
+ FrameworkStatsLog.write(FrameworkStatsLog.APP_MOVED_STORAGE_REPORTED,
+ packageExternalStorageType,
+ FrameworkStatsLog.APP_MOVED_STORAGE_REPORTED__MOVE_TYPE__TO_EXTERNAL,
packageName);
} else if (isPreviousLocationExternal && !isExternal(pkg)) {
// Move from external to internal storage.
- StatsLog.write(StatsLog.APP_MOVED_STORAGE_REPORTED, packageExternalStorageType,
- StatsLog.APP_MOVED_STORAGE_REPORTED__MOVE_TYPE__TO_INTERNAL,
+ FrameworkStatsLog.write(FrameworkStatsLog.APP_MOVED_STORAGE_REPORTED,
+ packageExternalStorageType,
+ FrameworkStatsLog.APP_MOVED_STORAGE_REPORTED__MOVE_TYPE__TO_INTERNAL,
packageName);
}
}
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 4f8a86d..96f1219 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -256,6 +256,8 @@
mContext = context;
}
+ private native void nativeInit();
+
/**
* Use of this StatsPullAtomCallbackImpl means we avoid one class per tagId, which we would
* get if we used lambdas.
@@ -399,6 +401,7 @@
super.onBootPhase(phase);
if (phase == PHASE_SYSTEM_SERVICES_READY) {
BackgroundThread.getHandler().post(() -> {
+ nativeInit();
initializePullersState();
registerAllPullers();
registerEventListeners();
@@ -896,7 +899,6 @@
return data;
}
}
- Slog.e(TAG, "no controller energy info supplied for " + receiver.getName());
} catch (TimeoutException e) {
Slog.w(TAG, "timeout reading " + receiver.getName() + " stats");
}
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index cdf8ea3..bbd1ae6 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -198,7 +198,7 @@
Slog.w(TAG, "Failed to read staged distro.", e);
}
}
- return new RulesState(baseVersion.rulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED,
+ return new RulesState(baseVersion.getRulesVersion(), DISTRO_FORMAT_VERSION_SUPPORTED,
operationInProgress, stagedOperationStatus, stagedDistroRulesVersion,
distroStatus, installedDistroRulesVersion);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index dda11f9..b05c250 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3074,7 +3074,11 @@
mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
waitingToShow = false;
- boolean delayed = isAnimating(TRANSITION | CHILDREN);
+ // Defer removal of this activity when either a child is animating, or app transition is on
+ // going. App transition animation might be applied on the parent stack not on the activity,
+ // but the actual frame buffer is associated with the activity, so we have to keep the
+ // activity while a parent is animating.
+ boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN);
if (getDisplayContent().mClosingApps.contains(this)) {
delayed = true;
} else if (getDisplayContent().mAppTransition.isTransitionSet()) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index ddf0117..0d72d84 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -341,6 +341,9 @@
private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
+ // TODO(task-hierarchy): remove when tiles can be actual parents
+ TaskTile mTile = null;
+
private final Handler mHandler;
private class ActivityStackHandler extends Handler {
@@ -638,11 +641,20 @@
}
@Override
+ public void resolveOverrideConfiguration(Configuration newParentConfig) {
+ super.resolveOverrideConfiguration(newParentConfig);
+ if (mTile != null) {
+ // If this is a virtual child of a tile, simulate the parent-child relationship
+ mTile.updateResolvedConfig(getResolvedOverrideConfiguration());
+ }
+ }
+
+ @Override
public void onConfigurationChanged(Configuration newParentConfig) {
// Calling Task#onConfigurationChanged() for leaf task since the ops in this method are
// particularly for ActivityStack, like preventing bounds changes when inheriting certain
// windowing mode.
- if (!isRootTask()) {
+ if (!isRootTask() || this instanceof TaskTile) {
super.onConfigurationChanged(newParentConfig);
return;
}
@@ -3944,7 +3956,6 @@
? ((WindowContainer) newParent).getDisplayContent() : null;
final DisplayContent oldDisplay = oldParent != null
? ((WindowContainer) oldParent).getDisplayContent() : null;
-
super.onParentChanged(newParent, oldParent);
if (display != null && inSplitScreenPrimaryWindowingMode()
@@ -3963,6 +3974,11 @@
if (oldDisplay != null && oldDisplay.isRemoving()) {
postReparent();
}
+ if (mTile != null && getSurfaceControl() != null) {
+ // by now, the TaskStack should already have been reparented, so we can reparent its
+ // surface here
+ reparentSurfaceControl(getPendingTransaction(), mTile.getSurfaceControl());
+ }
}
void reparent(DisplayContent newParent, boolean onTop) {
@@ -4000,7 +4016,16 @@
@Override
void getRelativeDisplayedPosition(Point outPos) {
- super.getRelativeDisplayedPosition(outPos);
+ // check for tile which is "virtually" a parent.
+ if (mTile != null) {
+ final Rect dispBounds = getDisplayedBounds();
+ outPos.set(dispBounds.left, dispBounds.top);
+ final Rect parentBounds = mTile.getBounds();
+ outPos.offset(-parentBounds.left, -parentBounds.top);
+ } else {
+ super.getRelativeDisplayedPosition(outPos);
+ }
+
final int outset = getStackOutset();
outPos.x -= outset;
outPos.y -= outset;
@@ -4010,6 +4035,16 @@
if (mSurfaceControl == null) {
return;
}
+ if (mTile != null) {
+ // Tile controls crop, so the app needs to be able to draw its background outside of
+ // the stack bounds for when the tile crop gets bigger than the stack.
+ if (mLastSurfaceSize.equals(0, 0)) {
+ return;
+ }
+ transaction.setWindowCrop(mSurfaceControl, null);
+ mLastSurfaceSize.set(0, 0);
+ return;
+ }
final Rect stackBounds = getDisplayedBounds();
int width = stackBounds.width();
@@ -4033,6 +4068,9 @@
@Override
void onDisplayChanged(DisplayContent dc) {
+ if (mTile != null && dc != mTile.getDisplay()) {
+ mTile.removeChild(this);
+ }
super.onDisplayChanged(dc);
if (isRootTask()) {
updateSurfaceBounds();
@@ -4848,6 +4886,42 @@
return shouldSleepActivities() || mAtmService.mShuttingDown;
}
+ TaskTile getTile() {
+ return mTile;
+ }
+
+ /**
+ * Don't call this directly. instead use {@link TaskTile#addChild} or
+ * {@link TaskTile#removeChild}.
+ */
+ void setTile(TaskTile tile) {
+ TaskTile origTile = mTile;
+ mTile = tile;
+ final ConfigurationContainer parent = getParent();
+ if (parent != null) {
+ onConfigurationChanged(parent.getConfiguration());
+ }
+
+ // Reparent to tile surface or back to original parent
+ if (getSurfaceControl() == null) {
+ return;
+ }
+ if (mTile != null) {
+ reparentSurfaceControl(getPendingTransaction(), mTile.getSurfaceControl());
+ } else if (mTile == null && origTile != null) {
+ reparentSurfaceControl(getPendingTransaction(), getParentSurfaceControl());
+ }
+ }
+
+ @Override
+ void removeImmediately() {
+ // TODO(task-hierarchy): remove this override when tiles are in hierarchy
+ if (mTile != null) {
+ mTile.removeChild(this);
+ }
+ super.removeImmediately();
+ }
+
@Override
public void dumpDebug(ProtoOutputStream proto, long fieldId,
@WindowTraceLogLevel int logLevel) {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 362e781..a513ef8 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1711,6 +1711,7 @@
* invisible as well and added to the stopping list. After which we process the
* stopping list by handling the idle.
*/
+ stack.cancelAnimation();
stack.mForceHidden = true;
stack.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
stack.mForceHidden = false;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f019013..d6e7077 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -144,6 +144,7 @@
import android.app.IAssistDataReceiver;
import android.app.INotificationManager;
import android.app.IRequestFinishCallback;
+import android.app.ITaskOrganizerController;
import android.app.ITaskStackListener;
import android.app.Notification;
import android.app.NotificationManager;
@@ -225,10 +226,8 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.view.IRecentsAnimationRunner;
-import android.view.ITaskOrganizer;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
-import android.view.WindowContainerTransaction;
import android.view.WindowManager;
import com.android.internal.R;
@@ -292,7 +291,6 @@
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -344,10 +342,6 @@
/** This activity is being relaunched due to a free-resize operation. */
public static final int RELAUNCH_REASON_FREE_RESIZE = 2;
- /** Flag indicating that an applied transaction may have effected lifecycle */
- private static final int TRANSACT_EFFECTS_CLIENT_CONFIG = 1;
- private static final int TRANSACT_EFFECTS_LIFECYCLE = 1 << 1;
-
Context mContext;
/**
@@ -669,8 +663,7 @@
/**
* Stores the registration and state of TaskOrganizers in use.
*/
- TaskOrganizerController mTaskOrganizerController =
- new TaskOrganizerController(this, mGlobalLock);
+ TaskOrganizerController mTaskOrganizerController = new TaskOrganizerController(this);
private int mDeviceOwnerUid = Process.INVALID_UID;
@@ -1286,15 +1279,6 @@
}
@Override
- public final void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) {
- enforceCallerIsRecentsOrHasPermission(
- MANAGE_ACTIVITY_STACKS, "registerTaskOrganizer()");
- synchronized (mGlobalLock) {
- mTaskOrganizerController.registerTaskOrganizer(organizer, windowingMode);
- }
- }
-
- @Override
public IBinder requestStartActivityPermissionToken(IBinder delegatorToken) {
int callingUid = Binder.getCallingUid();
if (UserHandle.getAppId(callingUid) != SYSTEM_UID) {
@@ -3304,116 +3288,6 @@
}
}
- private int sanitizeAndApplyChange(WindowContainer container,
- WindowContainerTransaction.Change change) {
- if (!(container instanceof Task || container instanceof ActivityStack)) {
- throw new RuntimeException("Invalid token in task transaction");
- }
- // The "client"-facing API should prevent bad changes; however, just in case, sanitize
- // masks here.
- int configMask = change.getConfigSetMask();
- int windowMask = change.getWindowSetMask();
- configMask &= ActivityInfo.CONFIG_WINDOW_CONFIGURATION
- | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
- windowMask &= WindowConfiguration.WINDOW_CONFIG_BOUNDS;
- int effects = 0;
- if (configMask != 0) {
- Configuration c = new Configuration(container.getRequestedOverrideConfiguration());
- c.setTo(change.getConfiguration(), configMask, windowMask);
- container.onRequestedOverrideConfigurationChanged(c);
- // TODO(b/145675353): remove the following once we could apply new bounds to the
- // pinned stack together with its children.
- resizePinnedStackIfNeeded(container, configMask, windowMask, c);
- effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
- }
- if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {
- if (container.setFocusable(change.getFocusable())) {
- effects |= TRANSACT_EFFECTS_LIFECYCLE;
- }
- }
- return effects;
- }
-
- private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask,
- int windowMask, Configuration config) {
- if ((container instanceof ActivityStack)
- && ((configMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0)
- && ((windowMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)) {
- final ActivityStack stack = (ActivityStack) container;
- if (stack.inPinnedWindowingMode()) {
- stack.resize(config.windowConfiguration.getBounds(),
- null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
- PRESERVE_WINDOWS, true /* deferResume */);
- }
- }
- }
-
- private int applyWindowContainerChange(WindowContainer wc,
- WindowContainerTransaction.Change c) {
- int effects = sanitizeAndApplyChange(wc, c);
-
- Rect enterPipBounds = c.getEnterPipBounds();
- if (enterPipBounds != null) {
- Task tr = (Task) wc;
- mStackSupervisor.updatePictureInPictureMode(tr,
- enterPipBounds, true);
- }
- return effects;
- }
-
- @Override
- public void applyContainerTransaction(WindowContainerTransaction t) {
- mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "applyContainerTransaction()");
- if (t == null) {
- return;
- }
- long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- int effects = 0;
- deferWindowLayout();
- try {
- ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
- Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
- t.getChanges().entrySet().iterator();
- while (entries.hasNext()) {
- final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
- entries.next();
- final WindowContainer wc = WindowContainer.RemoteToken.fromBinder(
- entry.getKey()).getContainer();
- int containerEffect = applyWindowContainerChange(wc, entry.getValue());
- effects |= containerEffect;
- // Lifecycle changes will trigger ensureConfig for everything.
- if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0
- && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
- haveConfigChanges.add(wc);
- }
- }
- if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
- // Already calls ensureActivityConfig
- mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
- } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
- final PooledConsumer f = PooledLambda.obtainConsumer(
- ActivityRecord::ensureActivityConfiguration,
- PooledLambda.__(ActivityRecord.class), 0,
- false /* preserveWindow */);
- try {
- for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
- haveConfigChanges.valueAt(i).forAllActivities(f);
- }
- } finally {
- f.recycle();
- }
- }
- } finally {
- continueWindowLayout();
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
@Override
public boolean releaseActivityInstance(IBinder token) {
synchronized (mGlobalLock) {
@@ -4442,6 +4316,13 @@
}
}
+ @Override
+ public ITaskOrganizerController getTaskOrganizerController() {
+ mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS,
+ "getTaskOrganizerController()");
+ return mTaskOrganizerController;
+ }
+
/**
* Check that we have the features required for VR-related API calls, and throw an exception if
* not.
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 3927d5f..0798a91 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -431,6 +431,8 @@
while (!candidates.isEmpty()) {
final WindowContainer current = candidates.removeFirst();
final WindowContainer parent = current.getParent();
+ siblings.clear();
+ siblings.add(current);
boolean canPromote = true;
if (parent == null) {
@@ -468,12 +470,11 @@
//
// [Task] +- [ActivityRecord1] (visible, in opening apps)
// +- [ActivityRecord2] (visible, not in opening apps)
- siblings.clear();
for (int j = 0; j < parent.getChildCount(); ++j) {
final WindowContainer sibling = parent.getChildAt(j);
- if (sibling == current || candidates.remove(sibling)) {
+ if (candidates.remove(sibling)) {
siblings.add(sibling);
- } else if (sibling.isVisible()) {
+ } else if (sibling != current && sibling.isVisible()) {
canPromote = false;
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
new file mode 100644
index 0000000..b3edc91
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -0,0 +1,265 @@
+/*
+ * 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.wm;
+
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
+
+import static com.android.internal.util.Preconditions.checkState;
+import static com.android.server.wm.DisplayAreaChildProto.DISPLAY_AREA;
+import static com.android.server.wm.DisplayAreaChildProto.UNKNOWN;
+import static com.android.server.wm.DisplayAreaChildProto.WINDOW;
+import static com.android.server.wm.DisplayAreaProto.CHILDREN;
+import static com.android.server.wm.DisplayAreaProto.NAME;
+import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+
+import android.graphics.Rect;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.protolog.common.ProtoLog;
+
+import java.util.Comparator;
+import java.util.function.Predicate;
+
+/**
+ * Container for grouping WindowContainer below DisplayContent.
+ *
+ * DisplayAreas are managed by a {@link DisplayAreaPolicy}, and can override configurations and
+ * can be leashed.
+ *
+ * DisplayAreas can contain nested DisplayAreas.
+ *
+ * DisplayAreas come in three flavors, to ensure that windows have the right Z-Order:
+ * - BELOW_TASKS: Can only contain BELOW_TASK DisplayAreas and WindowTokens that go below tasks.
+ * - ABOVE_TASKS: Can only contain ABOVE_TASK DisplayAreas and WindowTokens that go above tasks.
+ * - ANY: Can contain any kind of DisplayArea, and any kind of WindowToken or the Task container.
+ * Cannot have a sibling that is of type ANY.
+ *
+ * @param <T> type of the children of the DisplayArea.
+ */
+public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
+
+ protected final Type mType;
+ private final String mName;
+
+ DisplayArea(WindowManagerService wms, Type type, String name) {
+ super(wms);
+ // TODO(display-area): move this up to ConfigurationContainer
+ mOrientation = SCREEN_ORIENTATION_UNSET;
+ mType = type;
+ mName = name;
+ }
+
+ @Override
+ void onChildPositionChanged(WindowContainer child) {
+ super.onChildPositionChanged(child);
+
+ // Verify that we have proper ordering
+ Type.checkChild(mType, Type.typeOf(child));
+
+ if (child instanceof ActivityStack) {
+ // TODO(display-area): ActivityStacks are type ANY, but are allowed to have siblings.
+ // They might need a separate type.
+ return;
+ }
+
+ for (int i = 1; i < getChildCount(); i++) {
+ final WindowContainer top = getChildAt(i - 1);
+ final WindowContainer bottom = getChildAt(i);
+ if (child == top || child == bottom) {
+ Type.checkSiblings(Type.typeOf(top), Type.typeOf(bottom));
+ }
+ }
+ }
+
+ @Override
+ boolean fillsParent() {
+ return true;
+ }
+
+ @Override
+ String getName() {
+ return mName;
+ }
+
+ @Override
+ public final void dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel) {
+ final long token = proto.start(fieldId);
+ super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
+ proto.write(NAME, mName);
+ for (int i = 0; i < getChildCount(); i++) {
+ final long childToken = proto.start(CHILDREN);
+ final T child = getChildAt(i);
+ if (child instanceof ActivityStack) {
+ // TODO(display-area): Dump stacks & tasks here, instead of in DisplayContent's
+ // dumpDebug. For now, skip them here to avoid dumping them as UNKNOWN.
+ } else if (child instanceof WindowToken) {
+ ((WindowToken) child).dumpDebug(proto, WINDOW, logLevel);
+ } else if (child instanceof DisplayArea) {
+ child.dumpDebug(proto, DISPLAY_AREA, logLevel);
+ } else {
+ proto.write(UNKNOWN, child.getClass().getSimpleName());
+ }
+ proto.end(childToken);
+ }
+ proto.end(token);
+ }
+
+ /**
+ * DisplayArea that contains WindowTokens, and orders them according to their type.
+ */
+ public static class Tokens extends DisplayArea<WindowToken> {
+ int mLastKeyguardForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+
+ private final Comparator<WindowToken> mWindowComparator =
+ Comparator.comparingInt(WindowToken::getWindowLayerFromType);
+
+ private final Predicate<WindowState> mGetOrientingWindow = w -> {
+ final WindowManagerPolicy policy = mWmService.mPolicy;
+ if (policy.isKeyguardHostWindow(w.mAttrs)) {
+ if (mWmService.mKeyguardGoingAway) {
+ return false;
+ }
+ // Consider unoccluding only when all unknown visibilities have been
+ // resolved, as otherwise we just may be starting another occluding activity.
+ final boolean isUnoccluding =
+ mDisplayContent.mAppTransition.getAppTransition()
+ == TRANSIT_KEYGUARD_UNOCCLUDE
+ && mDisplayContent.mUnknownAppVisibilityController.allResolved();
+ // If keyguard is showing, or we're unoccluding, force the keyguard's orientation,
+ // even if SystemUI hasn't updated the attrs yet.
+ if (policy.isKeyguardShowingAndNotOccluded() || isUnoccluding) {
+ return true;
+ }
+ }
+ final int req = w.mAttrs.screenOrientation;
+ if (req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND
+ || req == SCREEN_ORIENTATION_UNSET) {
+ return false;
+ }
+ return true;
+ };
+
+ Tokens(WindowManagerService wms, Type type, String name) {
+ super(wms, type, name);
+ }
+
+ void addChild(WindowToken token) {
+ addChild(token, mWindowComparator);
+ }
+
+ @Override
+ int getOrientation(int candidate) {
+ // Find a window requesting orientation.
+ final WindowState win = getWindow(mGetOrientingWindow);
+
+ if (win == null) {
+ return candidate;
+ }
+ int req = win.mAttrs.screenOrientation;
+ ProtoLog.v(WM_DEBUG_ORIENTATION, "%s forcing orientation to %d for display id=%d",
+ win, req, mDisplayContent.getDisplayId());
+ if (mWmService.mPolicy.isKeyguardHostWindow(win.mAttrs)) {
+ // SystemUI controls the Keyguard orientation asynchronously, and mAttrs may be
+ // stale. We record / use the last known override.
+ if (req != SCREEN_ORIENTATION_UNSET && req != SCREEN_ORIENTATION_UNSPECIFIED) {
+ mLastKeyguardForcedOrientation = req;
+ } else {
+ req = mLastKeyguardForcedOrientation;
+ }
+ }
+ return req;
+ }
+ }
+
+ /**
+ * Top-most DisplayArea under DisplayContent.
+ */
+ public static class Root extends DisplayArea<DisplayArea> {
+ private final Dimmer mDimmer = new Dimmer(this);
+ private final Rect mTmpDimBoundsRect = new Rect();
+
+ Root(WindowManagerService wms) {
+ super(wms, Type.ANY, "DisplayArea.Root");
+ }
+
+ @Override
+ Dimmer getDimmer() {
+ return mDimmer;
+ }
+
+ @Override
+ void prepareSurfaces() {
+ mDimmer.resetDimStates();
+ super.prepareSurfaces();
+ getBounds(mTmpDimBoundsRect);
+
+ if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+ scheduleAnimation();
+ }
+ }
+ }
+
+ enum Type {
+ /** Can only contain WindowTokens above the APPLICATION_LAYER. */
+ ABOVE_TASKS,
+ /** Can only contain WindowTokens below the APPLICATION_LAYER. */
+ BELOW_TASKS,
+ /** Can contain anything. */
+ ANY;
+
+ static void checkSiblings(Type bottom, Type top) {
+ checkState(!(bottom == ANY && top == ANY), "ANY cannot be a sibling of ANY");
+ checkState(!(bottom != BELOW_TASKS && top == BELOW_TASKS),
+ bottom + " must be above BELOW_TASKS");
+ checkState(!(bottom == ABOVE_TASKS && top != ABOVE_TASKS),
+ top + " must be below ABOVE_TASKS");
+ }
+
+ static void checkChild(Type parent, Type child) {
+ switch (parent) {
+ case ABOVE_TASKS:
+ checkState(child == ABOVE_TASKS, "ABOVE_TASKS can only contain ABOVE_TASKS");
+ break;
+ case BELOW_TASKS:
+ checkState(child == BELOW_TASKS, "BELOW_TASKS can only contain BELOW_TASKS");
+ break;
+ }
+ }
+
+ static Type typeOf(WindowContainer c) {
+ if (c instanceof DisplayArea) {
+ return ((DisplayArea) c).mType;
+ } else if (c instanceof WindowToken && !(c instanceof ActivityRecord)) {
+ return typeOf((WindowToken) c);
+ } else if (c instanceof ActivityStack) {
+ return ANY;
+ } else {
+ throw new IllegalArgumentException("Unknown container: " + c);
+ }
+ }
+
+ private static Type typeOf(WindowToken c) {
+ return c.getWindowLayerFromType() < APPLICATION_LAYER ? BELOW_TASKS : ABOVE_TASKS;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
new file mode 100644
index 0000000..06e7b48
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -0,0 +1,123 @@
+/*
+ * 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.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+
+import com.android.server.wm.DisplayContent.TaskContainers;
+
+/**
+ * Policy that manages DisplayAreas.
+ */
+public abstract class DisplayAreaPolicy {
+ protected final WindowManagerService mWmService;
+ protected final DisplayContent mContent;
+
+ /**
+ * The root DisplayArea. Attach all DisplayAreas to this area (directly or indirectly).
+ */
+ protected final DisplayArea.Root mRoot;
+
+ /**
+ * The IME container. The IME's windows are automatically added to this container.
+ */
+ protected final DisplayArea<? extends WindowContainer> mImeContainer;
+
+ /**
+ * The Tasks container. Tasks etc. are automatically added to this container.
+ */
+ protected final TaskContainers mTaskContainers;
+
+ DisplayAreaPolicy(WindowManagerService wmService,
+ DisplayContent content, DisplayArea.Root root,
+ DisplayArea<? extends WindowContainer> imeContainer, TaskContainers taskContainers) {
+ mWmService = wmService;
+ mContent = content;
+ mRoot = root;
+ mImeContainer = imeContainer;
+ mTaskContainers = taskContainers;
+ }
+
+ /**
+ * Called to ask the policy to set up the DisplayArea hierarchy. At a minimum this must:
+ *
+ * - attach mImeContainer to mRoot (or one of its descendants)
+ * - attach mTaskStacks to mRoot (or one of its descendants)
+ *
+ * Additionally, this is the right place to set up any other DisplayAreas as desired.
+ */
+ public abstract void attachDisplayAreas();
+
+ /**
+ * Called to ask the policy to attach the given WindowToken to the DisplayArea hierarchy.
+ *
+ * This must attach the token to mRoot (or one of its descendants).
+ */
+ public abstract void addWindow(WindowToken token);
+
+ /**
+ * Default policy that has no special features.
+ */
+ public static class Default extends DisplayAreaPolicy {
+
+ public Default(WindowManagerService wmService, DisplayContent content,
+ DisplayArea.Root root,
+ DisplayArea<? extends WindowContainer> imeContainer,
+ TaskContainers taskContainers) {
+ super(wmService, content, root, imeContainer, taskContainers);
+ }
+
+ private final DisplayArea.Tokens mBelow = new DisplayArea.Tokens(mWmService,
+ DisplayArea.Type.BELOW_TASKS, "BelowTasks");
+ private final DisplayArea<DisplayArea> mAbove = new DisplayArea<>(mWmService,
+ DisplayArea.Type.ABOVE_TASKS, "AboveTasks");
+ private final DisplayArea.Tokens mAboveBelowIme = new DisplayArea.Tokens(mWmService,
+ DisplayArea.Type.ABOVE_TASKS, "AboveTasksBelowIme");
+ private final DisplayArea.Tokens mAboveAboveIme = new DisplayArea.Tokens(mWmService,
+ DisplayArea.Type.ABOVE_TASKS, "AboveTasksAboveIme");
+
+ @Override
+ public void attachDisplayAreas() {
+ mRoot.addChild(mBelow, 0);
+ mRoot.addChild(mTaskContainers, 1);
+ mRoot.addChild(mAbove, 2);
+
+ mAbove.addChild(mAboveBelowIme, 0);
+ mAbove.addChild(mImeContainer, 1);
+ mAbove.addChild(mAboveAboveIme, 2);
+ }
+
+ @Override
+ public void addWindow(WindowToken token) {
+ switch (DisplayArea.Type.typeOf(token)) {
+ case ABOVE_TASKS:
+ if (token.getWindowLayerFromType()
+ < mWmService.mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)) {
+ mAboveBelowIme.addChild(token);
+ } else {
+ mAboveAboveIme.addChild(token);
+ }
+ break;
+ case BELOW_TASKS:
+ mBelow.addChild(token);
+ break;
+ default:
+ throw new IllegalArgumentException("don't know how to sort " + token);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6e479b2..81af0fe 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -80,7 +80,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
@@ -98,9 +97,7 @@
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.wm.DisplayContentProto.ABOVE_APP_WINDOWS;
import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
-import static com.android.server.wm.DisplayContentProto.BELOW_APP_WINDOWS;
import static com.android.server.wm.DisplayContentProto.CHANGING_APPS;
import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES;
@@ -109,9 +106,9 @@
import static com.android.server.wm.DisplayContentProto.DPI;
import static com.android.server.wm.DisplayContentProto.FOCUSED_APP;
import static com.android.server.wm.DisplayContentProto.ID;
-import static com.android.server.wm.DisplayContentProto.IME_WINDOWS;
import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
import static com.android.server.wm.DisplayContentProto.OVERLAY_WINDOWS;
+import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
import static com.android.server.wm.DisplayContentProto.ROTATION;
import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
import static com.android.server.wm.DisplayContentProto.STACKS;
@@ -207,6 +204,7 @@
import android.view.Gravity;
import android.view.IDisplayWindowInsetsController;
import android.view.ISystemGestureExclusionListener;
+import android.view.ITaskOrganizer;
import android.view.IWindow;
import android.view.InputChannel;
import android.view.InputDevice;
@@ -295,14 +293,7 @@
/** The containers below are the only child containers {@link #mWindowContainers} can have. */
// Contains all window containers that are related to apps (Activities)
private final TaskContainers mTaskContainers = new TaskContainers(mWmService);
- // Contains all non-app window containers that should be displayed above the app containers
- // (e.g. Status bar)
- private final AboveAppWindowContainers mAboveAppWindowsContainers =
- new AboveAppWindowContainers("mAboveAppWindowsContainers", mWmService);
- // Contains all non-app window containers that should be displayed below the app containers
- // (e.g. Wallpaper).
- private final NonAppWindowContainers mBelowAppWindowsContainers =
- new NonAppWindowContainers("mBelowAppWindowsContainers", mWmService);
+
// Contains all IME window containers. Note that the z-ordering of the IME windows will depend
// on the IME target. We mainly have this container grouping so we can keep track of all the IME
// window containers together and move them in-sync if/when needed. We use a subclass of
@@ -310,6 +301,11 @@
// TODO(display-area): is "no magnification" in the comment still true?
private final ImeContainer mImeWindowsContainers = new ImeContainer(mWmService);
+ private final DisplayArea.Root mRootDisplayArea = new DisplayArea.Root(mWmService);
+
+ private final DisplayAreaPolicy mDisplayAreaPolicy = new DisplayAreaPolicy.Default(
+ mWmService, this, mRootDisplayArea, mImeWindowsContainers, mTaskContainers);
+
private WindowState mTmpWindow;
private WindowState mTmpWindow2;
private boolean mUpdateImeTarget;
@@ -401,14 +397,6 @@
private int mCurrentOverrideConfigurationChanges;
/**
- * Last orientation forced by the keyguard. It is applied when keyguard is shown and is not
- * occluded.
- *
- * @see NonAppWindowContainers#getOrientation()
- */
- private int mLastKeyguardForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
-
- /**
* The maximum aspect ratio (longerSide/shorterSide) that is treated as close-to-square. The
* orientation requests from apps would be ignored if the display is close-to-square.
*/
@@ -664,6 +652,9 @@
private final RootWindowContainer.FindTaskResult
mTmpFindTaskResult = new RootWindowContainer.FindTaskResult();
+ // When non-null, new stacks get put into this tile.
+ TaskTile mLaunchTile = null;
+
private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
final ActivityRecord activity = w.mActivityRecord;
@@ -1104,18 +1095,16 @@
// Add non-app token to container hierarchy on the display. App tokens are added through
// the parent container managing them (e.g. Tasks).
switch (token.windowType) {
- case TYPE_WALLPAPER:
- mBelowAppWindowsContainers.addChild(token);
- break;
case TYPE_INPUT_METHOD:
case TYPE_INPUT_METHOD_DIALOG:
mImeWindowsContainers.addChild(token);
break;
case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
+ // TODO(display-area): Migrate to DisplayArea
mOverlayContainers.addChild(token);
break;
default:
- mAboveAppWindowsContainers.addChild(token);
+ mDisplayAreaPolicy.addWindow(token);
break;
}
}
@@ -2129,13 +2118,7 @@
return getLastOrientation();
}
}
- final int orientation = mAboveAppWindowsContainers.getOrientation();
- if (orientation != SCREEN_ORIENTATION_UNSET) {
- return orientation;
- }
-
- // Top system windows are not requesting an orientation. Start searching from apps.
- return mTaskContainers.getOrientation();
+ return mRootDisplayArea.getOrientation();
}
void updateDisplayInfo() {
@@ -2782,23 +2765,12 @@
final long token = proto.start(fieldId);
super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
proto.write(ID, mDisplayId);
+ mRootDisplayArea.dumpDebug(proto, ROOT_DISPLAY_AREA, logLevel);
for (int stackNdx = mTaskContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = mTaskContainers.getChildAt(stackNdx);
stack.dumpDebugInnerStackOnly(proto, STACKS, logLevel);
}
mDividerControllerLocked.dumpDebug(proto, DOCKED_STACK_DIVIDER_CONTROLLER);
- for (int i = mAboveAppWindowsContainers.getChildCount() - 1; i >= 0; --i) {
- final WindowToken windowToken = mAboveAppWindowsContainers.getChildAt(i);
- windowToken.dumpDebug(proto, ABOVE_APP_WINDOWS, logLevel);
- }
- for (int i = mBelowAppWindowsContainers.getChildCount() - 1; i >= 0; --i) {
- final WindowToken windowToken = mBelowAppWindowsContainers.getChildAt(i);
- windowToken.dumpDebug(proto, BELOW_APP_WINDOWS, logLevel);
- }
- for (int i = mImeWindowsContainers.getChildCount() - 1; i >= 0; --i) {
- final WindowToken windowToken = mImeWindowsContainers.getChildAt(i);
- windowToken.dumpDebug(proto, IME_WINDOWS, logLevel);
- }
for (int i = mOverlayContainers.getChildCount() - 1; i >= 0; --i) {
final WindowToken windowToken = mOverlayContainers.getChildAt(i);
windowToken.dumpDebug(proto, OVERLAY_WINDOWS, logLevel);
@@ -4212,7 +4184,7 @@
* Window container class that contains all containers on this display relating to Apps.
* I.e Activities.
*/
- private final class TaskContainers extends DisplayChildWindowContainer<ActivityStack> {
+ final class TaskContainers extends DisplayArea<ActivityStack> {
/**
* A control placed at the appropriate level for transitions to occur.
*/
@@ -4222,16 +4194,15 @@
/**
* Given that the split-screen divider does not have an AppWindowToken, it
- * will have to live inside of a "NonAppWindowContainer", in particular
- * {@link DisplayContent#mAboveAppWindowsContainers}. However, in visual Z order
+ * will have to live inside of a "NonAppWindowContainer". However, in visual Z order
* it will need to be interleaved with some of our children, appearing on top of
* both docked stacks but underneath any assistant stacks.
*
* To solve this problem we have this anchor control, which will always exist so
* we can always assign it the correct value in our {@link #assignChildLayers}.
- * Likewise since it always exists, {@link AboveAppWindowContainers} can always
+ * Likewise since it always exists, we can always
* assign the divider a layer relative to it. This way we prevent linking lifecycle
- * events between the two containers.
+ * events between tasks and the divider window.
*/
SurfaceControl mSplitScreenDividerAnchor = null;
@@ -4242,7 +4213,7 @@
private ActivityStack mRootSplitScreenPrimaryTask = null;
TaskContainers(WindowManagerService service) {
- super(service);
+ super(service, Type.ANY, "TaskContainers");
}
/**
@@ -4275,8 +4246,15 @@
@VisibleForTesting
ActivityStack getTopStack() {
- return mTaskContainers.getChildCount() > 0
- ? mTaskContainers.getChildAt(mTaskContainers.getChildCount() - 1) : null;
+ // TODO(task-hierarchy): Just grab index -1 once tiles are in hierarchy.
+ for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
+ final ActivityStack child = mTaskContainers.getChildAt(i);
+ if (child instanceof TaskTile) {
+ continue;
+ }
+ return child;
+ }
+ return null;
}
int getIndexOf(ActivityStack stack) {
@@ -4318,6 +4296,10 @@
}
private void addStackReferenceIfNeeded(ActivityStack stack) {
+ // TODO(task-hierarchy): Remove when tiles are in hierarchy.
+ if (stack instanceof TaskTile) {
+ return;
+ }
if (stack.isActivityTypeHome()) {
if (mRootHomeTask != null) {
if (!stack.isDescendantOf(mRootHomeTask)) {
@@ -4735,31 +4717,16 @@
mSplitScreenDividerAnchor = null;
}
}
- }
-
- private final class AboveAppWindowContainers extends NonAppWindowContainers {
- AboveAppWindowContainers(String name, WindowManagerService service) {
- super(name, service);
- }
@Override
- void assignChildLayers(SurfaceControl.Transaction t) {
- boolean needAssignIme = mImeWindowsContainers.getSurfaceControl() != null;
- for (int j = 0; j < mChildren.size(); ++j) {
- final WindowToken wt = mChildren.get(j);
-
- wt.assignLayer(t, j);
- wt.assignChildLayers(t);
-
- int layer = mWmService.mPolicy.getWindowLayerFromTypeLw(
- wt.windowType, wt.mOwnerCanManageAppTokens);
-
- if (needAssignIme && layer >= mWmService.mPolicy.getWindowLayerFromTypeLw(
- TYPE_INPUT_METHOD_DIALOG, true)) {
- mImeWindowsContainers.assignRelativeLayer(t, wt.getSurfaceControl(), -1);
- needAssignIme = false;
- }
+ void onChildPositionChanged(WindowContainer child) {
+ // TODO(task-hierarchy): Move functionality to TaskTile when it's a proper parent.
+ TaskTile tile = ((ActivityStack) child).getTile();
+ if (tile == null) {
+ return;
}
+ mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
+ tile, false /* force */);
}
}
@@ -4774,13 +4741,10 @@
@Override
void assignChildLayers(SurfaceControl.Transaction t) {
mImeWindowsContainers.setNeedsLayer();
- mBelowAppWindowsContainers.assignLayer(t, 0);
- mTaskContainers.assignLayer(t, 1);
- mAboveAppWindowsContainers.assignLayer(t, 2);
+
+ mRootDisplayArea.assignLayer(t, 0);
final WindowState imeTarget = mInputMethodTarget;
- boolean needAssignIme = true;
-
// In the case where we have an IME target that is not in split-screen mode IME
// assignment is easy. We just need the IME to go directly above the target. This way
// children of the target will naturally go above the IME and everyone is happy.
@@ -4813,11 +4777,7 @@
// Above we have assigned layers to our children, now we ask them to assign
// layers to their children.
- mBelowAppWindowsContainers.assignChildLayers(t);
- mTaskContainers.assignChildLayers(t);
- mAboveAppWindowsContainers.assignChildLayers(t);
- mImeWindowsContainers.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE);
- mImeWindowsContainers.assignChildLayers(t);
+ mRootDisplayArea.assignChildLayers(t);
}
@Override
@@ -4826,10 +4786,8 @@
}
void addChildren() {
- addChild(mBelowAppWindowsContainers, null);
- addChild(mTaskContainers, null);
- addChild(mAboveAppWindowsContainers, null);
- addChild(mImeWindowsContainers, null);
+ addChild(mRootDisplayArea, 0);
+ mDisplayAreaPolicy.attachDisplayAreas();
}
@Override
@@ -4856,32 +4814,6 @@
< mWmService.mPolicy.getWindowLayerFromTypeLw(token2.windowType,
token2.mOwnerCanManageAppTokens) ? -1 : 1;
- private final Predicate<WindowState> mGetOrientingWindow = w -> {
- final WindowManagerPolicy policy = mWmService.mPolicy;
- if (policy.isKeyguardHostWindow(w.mAttrs)) {
- if (mWmService.mKeyguardGoingAway) {
- return false;
- }
- // Consider unoccluding only when all unknown visibilities have been
- // resolved, as otherwise we just may be starting another occluding activity.
- final boolean isUnoccluding =
- mDisplayContent.mAppTransition.getAppTransition()
- == TRANSIT_KEYGUARD_UNOCCLUDE
- && mDisplayContent.mUnknownAppVisibilityController.allResolved();
- // If keyguard is showing, or we're unoccluding, force the keyguard's orientation,
- // even if SystemUI hasn't updated the attrs yet.
- if (policy.isKeyguardShowingAndNotOccluded() || isUnoccluding) {
- return true;
- }
- }
- final int req = w.mAttrs.screenOrientation;
- if (req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND
- || req == SCREEN_ORIENTATION_UNSET) {
- return false;
- }
- return true;
- };
-
private final String mName;
private final Dimmer mDimmer = new Dimmer(this);
private final Rect mTmpDimBoundsRect = new Rect();
@@ -4903,26 +4835,9 @@
@Override
int getOrientation(int candidate) {
- // Find a window requesting orientation.
- final WindowState win = getWindow(mGetOrientingWindow);
-
- if (win != null) {
- int req = win.mAttrs.screenOrientation;
- ProtoLog.v(WM_DEBUG_ORIENTATION,
- "%s forcing orientation to %d for display id=%d", win, req,
- mDisplayId);
- if (mWmService.mPolicy.isKeyguardHostWindow(win.mAttrs)) {
- // SystemUI controls the Keyguard orientation asynchronously, and mAttrs may be
- // stale. We record / use the last known override.
- if (req != SCREEN_ORIENTATION_UNSET && req != SCREEN_ORIENTATION_UNSPECIFIED) {
- mDisplayContent.mLastKeyguardForcedOrientation = req;
- } else {
- req = mDisplayContent.mLastKeyguardForcedOrientation;
- }
- }
- return req;
- }
- return candidate;
+ ProtoLog.w(WM_DEBUG_ORIENTATION, "NonAppWindowContainer cannot set orientation: %s",
+ this);
+ return SCREEN_ORIENTATION_UNSET;
}
@Override
@@ -4957,11 +4872,11 @@
* - the container doesn't always participate in window traversal, according to
* {@link #skipImeWindowsDuringTraversal()}
*/
- private class ImeContainer extends NonAppWindowContainers {
+ private static class ImeContainer extends DisplayArea.Tokens {
boolean mNeedsLayer = false;
ImeContainer(WindowManagerService wms) {
- super("ImeContainer", wms);
+ super(wms, Type.ABOVE_TASKS, "ImeContainer");
}
public void setNeedsLayer() {
@@ -6271,6 +6186,10 @@
}
boolean isTopNotPinnedStack(ActivityStack stack) {
+ // TODO(task-hierarchy): Remove when tiles are in hierarchy.
+ if (stack instanceof TaskTile) {
+ return false;
+ }
for (int i = getStackCount() - 1; i >= 0; --i) {
final ActivityStack current = getStackAt(i);
if (!current.inPinnedWindowingMode()) {
@@ -6685,6 +6604,19 @@
return getHomeActivityForUser(mRootWindowContainer.mCurrentUser);
}
+ // TODO(task-hierarchy): Remove when tiles are in hierarchy.
+ void addTile(TaskTile tile) {
+ mTaskContainers.addChild(tile, POSITION_BOTTOM);
+ ITaskOrganizer organizer = mAtmService.mTaskOrganizerController.getTaskOrganizer(
+ tile.getWindowingMode());
+ tile.setTaskOrganizer(organizer);
+ }
+
+ // TODO(task-hierarchy): Remove when tiles are in hierarchy.
+ void removeTile(TaskTile tile) {
+ mTaskContainers.removeChild(tile);
+ }
+
@Nullable
ActivityRecord getHomeActivityForUser(int userId) {
final ActivityStack homeStack = getRootHomeTask();
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index da77314..efe79b3 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -526,7 +526,7 @@
mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout);
mIsWaitingForRemoteRotation = false;
mDisplayContent.sendNewConfiguration();
- mService.mAtmService.applyContainerTransaction(t);
+ mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t);
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 2d4211a..d0179ad 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -20,17 +20,31 @@
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
+import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
import android.annotation.Nullable;
import android.app.StatusBarManager;
import android.util.IntArray;
+import android.util.SparseArray;
+import android.view.InsetsAnimationControlCallbacks;
+import android.view.InsetsAnimationControlImpl;
+import android.view.InsetsController;
+import android.view.InsetsSourceControl;
import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
+import android.view.SurfaceControl;
+import android.view.SyncRtSurfaceTransactionApplier;
import android.view.ViewRootImpl;
+import android.view.WindowInsetsAnimationCallback;
+import android.view.WindowInsetsAnimationControlListener;
+
+import com.android.server.DisplayThread;
/**
* Policy that implements who gets control over the windows generating insets.
@@ -46,6 +60,8 @@
private WindowState mFocusedWin;
private BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
+ private boolean mAnimatingShown;
+ private final float[] mTmpFloat9 = new float[9];
InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
mStateController = stateController;
@@ -91,11 +107,14 @@
changed = true;
}
if (changed) {
- updateBarControlTarget(mFocusedWin);
- mPolicy.getStatusBarManagerInternal().showTransient(mDisplayContent.getDisplayId(),
- mShowingTransientTypes.toArray());
- mStateController.notifyInsetsChanged();
- // TODO(b/118118435): Animation
+ startAnimation(mShowingTransientTypes, true, () -> {
+ synchronized (mDisplayContent.mWmService.mGlobalLock) {
+ mPolicy.getStatusBarManagerInternal().showTransient(
+ mDisplayContent.getDisplayId(),
+ mShowingTransientTypes.toArray());
+ mStateController.notifyInsetsChanged();
+ }
+ });
}
}
@@ -103,11 +122,13 @@
if (mShowingTransientTypes.size() == 0) {
return;
}
-
- // TODO(b/118118435): Animation
- mShowingTransientTypes.clear();
- updateBarControlTarget(mFocusedWin);
- mStateController.notifyInsetsChanged();
+ startAnimation(mShowingTransientTypes, false, () -> {
+ synchronized (mDisplayContent.mWmService.mGlobalLock) {
+ mShowingTransientTypes.clear();
+ mStateController.notifyInsetsChanged();
+ updateBarControlTarget(mFocusedWin);
+ }
+ });
}
boolean isTransient(@InternalInsetsType int type) {
@@ -247,6 +268,29 @@
return isDockedStackVisible || isFreeformStackVisible || isResizing;
}
+ private void startAnimation(IntArray internalTypes, boolean show, Runnable callback) {
+ int typesReady = 0;
+ final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
+ updateBarControlTarget(mFocusedWin);
+ for (int i = internalTypes.size() - 1; i >= 0; i--) {
+ InsetsSourceProvider provider =
+ mStateController.getSourceProvider(internalTypes.get(i));
+ if (provider == null) continue;
+ InsetsSourceControl control = provider.getControl(provider.getControlTarget());
+ if (control == null || control.getLeash() == null) continue;
+ typesReady |= InsetsState.toPublicType(internalTypes.get(i));
+ controls.put(control.getType(), control);
+ }
+ controlAnimationUnchecked(typesReady, controls, show, callback);
+ }
+
+ private void controlAnimationUnchecked(int typesReady,
+ SparseArray<InsetsSourceControl> controls, boolean show, Runnable callback) {
+ InsetsPolicyAnimationControlListener listener =
+ new InsetsPolicyAnimationControlListener(show, callback);
+ listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show);
+ }
+
private class BarWindow {
private final int mId;
@@ -267,7 +311,103 @@
}
}
- // TODO(b/118118435): Implement animations for it (with SurfaceAnimator)
+ private class InsetsPolicyAnimationControlListener extends
+ InsetsController.InternalAnimationControlListener {
+ Runnable mFinishCallback;
+ InsetsPolicyAnimationControlCallbacks mControlCallbacks;
+
+ InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback) {
+ super(show);
+ mFinishCallback = finishCallback;
+ mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this);
+ }
+
+ @Override
+ protected void onAnimationFinish() {
+ super.onAnimationFinish();
+ mControlCallbacks.mAnimationControl.finish(mAnimatingShown);
+ DisplayThread.getHandler().post(mFinishCallback);
+ }
+
+ private class InsetsPolicyAnimationControlCallbacks implements
+ InsetsAnimationControlCallbacks {
+ private InsetsAnimationControlImpl mAnimationControl = null;
+ private InsetsPolicyAnimationControlListener mListener;
+
+ InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener) {
+ super();
+ mListener = listener;
+ }
+
+ private void controlAnimationUnchecked(int typesReady,
+ SparseArray<InsetsSourceControl> controls, boolean show) {
+ if (typesReady == 0) {
+ // nothing to animate.
+ return;
+ }
+ mAnimatingShown = show;
+
+ mAnimationControl = new InsetsAnimationControlImpl(controls,
+ mFocusedWin.getDisplayContent().getBounds(), getState(),
+ mListener, typesReady, this, mListener.getDurationMs(),
+ InsetsController.INTERPOLATOR, true,
+ show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
+ : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN);
+ }
+
+ /** Called on SurfaceAnimationThread lock without global WM lock held. */
+ @Override
+ public void scheduleApplyChangeInsets() {
+ InsetsState state = getState();
+ if (mAnimationControl.applyChangeInsets(state)) {
+ mAnimationControl.finish(mAnimatingShown);
+ }
+ }
+
+ @Override
+ public void notifyFinished(InsetsAnimationControlImpl controller, boolean shown) {
+ // Nothing's needed here. Finish steps is handled in the listener
+ // onAnimationFinished callback.
+ }
+
+ /**
+ * This method will return a state with fullscreen frame override. No need to make copy
+ * after getting state from this method.
+ * @return The client insets state with full display frame override.
+ */
+ private InsetsState getState() {
+ // To animate the transient animation correctly, we need to let the state hold
+ // the full display frame.
+ InsetsState overrideState = new InsetsState(mFocusedWin.getRequestedInsetsState(),
+ true);
+ overrideState.setDisplayFrame(mFocusedWin.getDisplayContent().getBounds());
+ return overrideState;
+ }
+
+ /** Called on SurfaceAnimationThread lock without global WM lock held. */
+ @Override
+ public void applySurfaceParams(
+ final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ for (int i = params.length - 1; i >= 0; i--) {
+ SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i];
+ applyParams(t, surfaceParams, mTmpFloat9);
+ }
+ t.apply();
+ }
+
+ /** Called on SurfaceAnimationThread lock without global WM lock held. */
+ @Override
+ public void startAnimation(InsetsAnimationControlImpl controller,
+ WindowInsetsAnimationControlListener listener, int types,
+ WindowInsetsAnimationCallback.InsetsAnimation animation,
+ WindowInsetsAnimationCallback.AnimationBounds bounds,
+ int layoutDuringAnimation) {
+ SurfaceAnimationThread.getHandler().post(() -> listener.onReady(controller, types));
+ }
+ }
+ }
+
private class TransientControlTarget implements InsetsControlTarget {
@Override
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 568966a..289ac4c 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -60,6 +60,8 @@
w.notifyInsetsChanged();
}
};
+ private final InsetsControlTarget mEmptyImeControlTarget = () -> {
+ };
InsetsStateController(DisplayContent displayContent) {
mDisplayContent = displayContent;
@@ -182,7 +184,10 @@
}
void onImeControlTargetChanged(@Nullable InsetsControlTarget imeTarget) {
- onControlChanged(ITYPE_IME, imeTarget);
+
+ // Make sure that we always have a control target for the IME, even if the IME target is
+ // null. Otherwise there is no leash that will hide it and IME becomes "randomly" visible.
+ onControlChanged(ITYPE_IME, imeTarget != null ? imeTarget : mEmptyImeControlTarget);
notifyPendingInsetsControlChanged();
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 647be0f..9770947 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -388,11 +388,12 @@
// surfaces needs to be done immediately.
mWindowManager.executeAppTransition();
- // After reordering the stacks, reset the minimized state. At this point, either
- // the target activity is now top-most and we will stay minimized (if in
- // split-screen), or we will have returned to the app, and the minimized state
- // should be reset
- mWindowManager.checkSplitScreenMinimizedChanged(true /* animate */);
+ if (targetStack.getTile() != null) {
+ // Client state may have changed during the recents animation, so force
+ // send task info so the client can synchronize its state.
+ mService.mTaskOrganizerController.dispatchTaskInfoChanged(
+ targetStack.mTile, true /* force */);
+ }
} catch (Exception e) {
Slog.e(TAG, "Failed to clean up recents activity", e);
throw e;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index e6fd512..a13399b 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1012,6 +1012,9 @@
mWmService.scheduleAnimationLocked();
+ // Send any pending task-info changes that were queued-up during a layout deferment
+ mWmService.mAtmService.mTaskOrganizerController.dispatchPendingTaskInfoChanges();
+
if (DEBUG_WINDOW_TRACE) Slog.e(TAG,
"performSurfacePlacementInner exit: animating="
+ mWmService.mAnimator.isAnimating());
@@ -2959,6 +2962,12 @@
case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
}
+ // TODO(task-hierarchy): Find another way to differentiate tile from normal stack once it is
+ // part of the hierarchy
+ if (stack instanceof TaskTile) {
+ // Don't launch directly into tiles.
+ return false;
+ }
// There is a 1-to-1 relationship between stack and task when not in
// primary split-windowing mode.
if (stack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 36cae1f..28dc2a4 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1257,7 +1257,7 @@
if (affinityIntent != null) return affinityIntent;
// Probably a task that contains other tasks, so return the intent for the top task?
final Task topTask = getTopMostTask();
- return topTask != null ? topTask.getBaseIntent() : null;
+ return (topTask != this && topTask != null) ? topTask.getBaseIntent() : null;
}
/** Returns the first non-finishing activity from the bottom. */
@@ -3214,7 +3214,10 @@
info.taskId = mTaskId;
info.displayId = getDisplayId();
info.isRunning = getTopNonFinishingActivity() != null;
- info.baseIntent = new Intent(getBaseIntent());
+ final Intent baseIntent = getBaseIntent();
+ // Make a copy of base intent because this is like a snapshot info.
+ // Besides, {@link RecentTasks#getRecentTasksImpl} may modify it.
+ info.baseIntent = baseIntent == null ? new Intent() : new Intent(baseIntent);
info.baseActivity = mReuseActivitiesReport.base != null
? mReuseActivitiesReport.base.intent.getComponent()
: null;
@@ -3229,6 +3232,10 @@
info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode();
info.resizeMode = mResizeMode;
info.configuration.setTo(getConfiguration());
+ info.token = mRemoteToken;
+ // Get's the first non-undefined activity type among this and children. Can't use
+ // configuration.windowConfiguration because that would only be this level.
+ info.topActivityType = getActivityType();
}
/**
@@ -3375,7 +3382,7 @@
if (affinity != null) {
sb.append(" A=");
sb.append(affinity);
- } else if (intent != null) {
+ } else if (intent != null && intent.getComponent() != null) {
sb.append(" I=");
sb.append(intent.getComponent().flattenToShortString());
} else if (affinityIntent != null && affinityIntent.getComponent() != null) {
@@ -3865,7 +3872,12 @@
boolean isControlledByTaskOrganizer() {
final Task rootTask = getRootTask();
- return rootTask == this && rootTask.mTaskOrganizer != null;
+ return rootTask == this && rootTask.mTaskOrganizer != null
+ // TODO(task-hierarchy): Figure out how to control nested tasks.
+ // For now, if this is in a tile let WM drive.
+ && !(rootTask instanceof TaskTile)
+ && !(rootTask instanceof ActivityStack
+ && ((ActivityStack) rootTask).getTile() != null);
}
@Override
@@ -3893,6 +3905,9 @@
}
void setTaskOrganizer(ITaskOrganizer organizer) {
+ if (mTaskOrganizer == organizer) {
+ return;
+ }
// Let the old organizer know it has lost control.
if (mTaskOrganizer != null) {
sendTaskVanished();
@@ -3918,8 +3933,6 @@
public void updateSurfacePosition() {
// Avoid fighting with the TaskOrganizer over Surface position.
if (isControlledByTaskOrganizer()) {
- getPendingTransaction().setPosition(mSurfaceControl, 0, 0);
- scheduleAnimation();
return;
} else {
super.updateSurfacePosition();
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 66c65e2..44a6fc9 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -16,26 +16,51 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ITaskOrganizerController;
+import android.app.WindowConfiguration;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.ArraySet;
import android.util.Slog;
import android.view.ITaskOrganizer;
-import android.view.SurfaceControl;
+import android.view.IWindowContainer;
+import android.view.WindowContainerTransaction;
+
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledLambda;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.WeakHashMap;
/**
* Stores the TaskOrganizers associated with a given windowing mode and
* their associated state.
*/
-class TaskOrganizerController {
+class TaskOrganizerController extends ITaskOrganizerController.Stub {
private static final String TAG = "TaskOrganizerController";
- private WindowManagerGlobalLock mGlobalLock;
+ /** Flag indicating that an applied transaction may have effected lifecycle */
+ private static final int TRANSACT_EFFECTS_CLIENT_CONFIG = 1;
+ private static final int TRANSACT_EFFECTS_LIFECYCLE = 1 << 1;
+
+ private final WindowManagerGlobalLock mGlobalLock;
private class DeathRecipient implements IBinder.DeathRecipient {
int mWindowingMode;
@@ -87,11 +112,20 @@
final HashMap<Integer, ITaskOrganizer> mTaskOrganizersByPendingSyncId = new HashMap();
+ private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>();
+ private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>();
+
final ActivityTaskManagerService mService;
- TaskOrganizerController(ActivityTaskManagerService atm, WindowManagerGlobalLock lock) {
+ RunningTaskInfo mTmpTaskInfo;
+
+ TaskOrganizerController(ActivityTaskManagerService atm) {
mService = atm;
- mGlobalLock = lock;
+ mGlobalLock = atm.mGlobalLock;
+ }
+
+ private void enforceStackPermission(String func) {
+ mService.mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, func);
}
private void clearIfNeeded(int windowingMode) {
@@ -106,26 +140,35 @@
* If there was already a TaskOrganizer for this windowing mode it will be evicted
* and receive taskVanished callbacks in the process.
*/
- void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) {
- if (windowingMode != WINDOWING_MODE_PINNED &&
- windowingMode != WINDOWING_MODE_MULTI_WINDOW) {
- throw new UnsupportedOperationException(
- "As of now only Pinned and Multiwindow windowing modes are"
- + " supported for registerTaskOrganizer");
-
+ @Override
+ public void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) {
+ if (windowingMode != WINDOWING_MODE_PINNED
+ && windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ && windowingMode != WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ && windowingMode != WINDOWING_MODE_MULTI_WINDOW) {
+ throw new UnsupportedOperationException("As of now only Pinned/Split/Multiwindow"
+ + " windowing modes are supported for registerTaskOrganizer");
}
- clearIfNeeded(windowingMode);
- DeathRecipient dr = new DeathRecipient(organizer, windowingMode);
+ enforceStackPermission("registerTaskOrganizer()");
+ final long origId = Binder.clearCallingIdentity();
try {
- organizer.asBinder().linkToDeath(dr, 0);
- } catch (RemoteException e) {
- Slog.e(TAG, "TaskOrganizer failed to register death recipient");
+ synchronized (mGlobalLock) {
+ clearIfNeeded(windowingMode);
+ DeathRecipient dr = new DeathRecipient(organizer, windowingMode);
+ try {
+ organizer.asBinder().linkToDeath(dr, 0);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "TaskOrganizer failed to register death recipient");
+ }
+
+ final TaskOrganizerState state = new TaskOrganizerState(organizer, dr);
+ mTaskOrganizersForWindowingMode.put(windowingMode, state);
+
+ mTaskOrganizerStates.put(organizer, state);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
}
-
- final TaskOrganizerState state = new TaskOrganizerState(organizer, dr);
- mTaskOrganizersForWindowingMode.put(windowingMode, state);
-
- mTaskOrganizerStates.put(organizer, state);
}
ITaskOrganizer getTaskOrganizer(int windowingMode) {
@@ -138,7 +181,7 @@
private void sendTaskAppeared(ITaskOrganizer organizer, Task task) {
try {
- organizer.taskAppeared(task.getRemoteToken(), task.getTaskInfo());
+ organizer.taskAppeared(task.getTaskInfo());
} catch (Exception e) {
Slog.e(TAG, "Exception sending taskAppeared callback" + e);
}
@@ -167,4 +210,254 @@
// we do this AFTER sending taskVanished.
state.removeTask(task);
}
+
+ @Override
+ public RunningTaskInfo createRootTask(int displayId, int windowingMode) {
+ enforceStackPermission("createRootTask()");
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ DisplayContent display = mService.mRootWindowContainer.getDisplayContent(displayId);
+ if (display == null) {
+ return null;
+ }
+ final int nextId = display.getNextStackId();
+ TaskTile tile = new TaskTile(mService, nextId, windowingMode);
+ display.addTile(tile);
+ RunningTaskInfo out = new RunningTaskInfo();
+ tile.fillTaskInfo(out);
+ mLastSentTaskInfos.put(tile, out);
+ return out;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public boolean deleteRootTask(IWindowContainer token) {
+ enforceStackPermission("deleteRootTask()");
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ TaskTile tile = TaskTile.forToken(token.asBinder());
+ if (tile == null) {
+ return false;
+ }
+ tile.removeImmediately();
+ return true;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ void dispatchPendingTaskInfoChanges() {
+ if (mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
+ return;
+ }
+ for (int i = 0, n = mPendingTaskInfoChanges.size(); i < n; ++i) {
+ dispatchTaskInfoChanged(mPendingTaskInfoChanges.get(i), false /* force */);
+ }
+ mPendingTaskInfoChanges.clear();
+ }
+
+ void dispatchTaskInfoChanged(Task task, boolean force) {
+ if (!force && mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
+ // Defer task info reporting while layout is deferred. This is because layout defer
+ // blocks tend to do lots of re-ordering which can mess up animations in receivers.
+ mPendingTaskInfoChanges.remove(task);
+ mPendingTaskInfoChanges.add(task);
+ return;
+ }
+ RunningTaskInfo lastInfo = mLastSentTaskInfos.get(task);
+ if (mTmpTaskInfo == null) {
+ mTmpTaskInfo = new RunningTaskInfo();
+ }
+ task.fillTaskInfo(mTmpTaskInfo);
+ boolean changed = lastInfo == null
+ || mTmpTaskInfo.topActivityType != lastInfo.topActivityType
+ || mTmpTaskInfo.isResizable() != lastInfo.isResizable();
+ if (!(changed || force)) {
+ return;
+ }
+ final RunningTaskInfo newInfo = mTmpTaskInfo;
+ mLastSentTaskInfos.put(task, mTmpTaskInfo);
+ // Since we've stored this, clean up the reference so a new one will be created next time.
+ // Transferring it this way means we only have to construct new RunningTaskInfos when they
+ // change.
+ mTmpTaskInfo = null;
+
+ if (task.mTaskOrganizer != null) {
+ try {
+ task.mTaskOrganizer.onTaskInfoChanged(newInfo);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ @Override
+ public IWindowContainer getImeTarget(int displayId) {
+ enforceStackPermission("getImeTarget()");
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ DisplayContent dc = mService.mWindowManager.mRoot
+ .getDisplayContent(displayId);
+ if (dc == null || dc.mInputMethodTarget == null) {
+ return null;
+ }
+ // Avoid WindowState#getRootTask() so we don't attribute system windows to a task.
+ final Task task = dc.mInputMethodTarget.getTask();
+ if (task == null) {
+ return null;
+ }
+ ActivityStack rootTask = (ActivityStack) task.getRootTask();
+ final TaskTile tile = rootTask.getTile();
+ if (tile != null) {
+ rootTask = tile;
+ }
+ return rootTask.mRemoteToken;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
+ public void setLaunchRoot(int displayId, @Nullable IWindowContainer tile) {
+ enforceStackPermission("setLaunchRoot()");
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ DisplayContent display = mService.mRootWindowContainer.getDisplayContent(displayId);
+ if (display == null) {
+ return;
+ }
+ TaskTile taskTile = tile == null ? null : TaskTile.forToken(tile.asBinder());
+ if (taskTile == null) {
+ display.mLaunchTile = null;
+ return;
+ }
+ if (taskTile.getDisplay() != display) {
+ throw new RuntimeException("Can't set launch root for display " + displayId
+ + " to task on display " + taskTile.getDisplay().getDisplayId());
+ }
+ display.mLaunchTile = taskTile;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ private int sanitizeAndApplyChange(WindowContainer container,
+ WindowContainerTransaction.Change change) {
+ if (!(container instanceof Task)) {
+ throw new RuntimeException("Invalid token in task transaction");
+ }
+ // The "client"-facing API should prevent bad changes; however, just in case, sanitize
+ // masks here.
+ int configMask = change.getConfigSetMask();
+ int windowMask = change.getWindowSetMask();
+ configMask &= ActivityInfo.CONFIG_WINDOW_CONFIGURATION
+ | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+ windowMask &= WindowConfiguration.WINDOW_CONFIG_BOUNDS;
+ int effects = 0;
+ if (configMask != 0) {
+ Configuration c = new Configuration(container.getRequestedOverrideConfiguration());
+ c.setTo(change.getConfiguration(), configMask, windowMask);
+ container.onRequestedOverrideConfigurationChanged(c);
+ // TODO(b/145675353): remove the following once we could apply new bounds to the
+ // pinned stack together with its children.
+ resizePinnedStackIfNeeded(container, configMask, windowMask, c);
+ effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
+ }
+ if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {
+ if (container.setFocusable(change.getFocusable())) {
+ effects |= TRANSACT_EFFECTS_LIFECYCLE;
+ }
+ }
+ return effects;
+ }
+
+ private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask,
+ int windowMask, Configuration config) {
+ if ((container instanceof ActivityStack)
+ && ((configMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0)
+ && ((windowMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)) {
+ final ActivityStack stack = (ActivityStack) container;
+ if (stack.inPinnedWindowingMode()) {
+ stack.resize(config.windowConfiguration.getBounds(),
+ null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
+ PRESERVE_WINDOWS, true /* deferResume */);
+ }
+ }
+ }
+
+ private int applyWindowContainerChange(WindowContainer wc,
+ WindowContainerTransaction.Change c) {
+ int effects = sanitizeAndApplyChange(wc, c);
+
+ Rect enterPipBounds = c.getEnterPipBounds();
+ if (enterPipBounds != null) {
+ Task tr = (Task) wc;
+ mService.mStackSupervisor.updatePictureInPictureMode(tr,
+ enterPipBounds, true);
+ }
+ return effects;
+ }
+
+ @Override
+ public void applyContainerTransaction(WindowContainerTransaction t) {
+ enforceStackPermission("applyContainerTransaction()");
+ if (t == null) {
+ return;
+ }
+ long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ int effects = 0;
+ mService.deferWindowLayout();
+ try {
+ ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
+ Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
+ t.getChanges().entrySet().iterator();
+ while (entries.hasNext()) {
+ final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
+ entries.next();
+ final WindowContainer wc = WindowContainer.RemoteToken.fromBinder(
+ entry.getKey()).getContainer();
+ int containerEffect = applyWindowContainerChange(wc, entry.getValue());
+ effects |= containerEffect;
+ // Lifecycle changes will trigger ensureConfig for everything.
+ if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0
+ && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
+ haveConfigChanges.add(wc);
+ }
+ }
+ if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
+ // Already calls ensureActivityConfig
+ mService.mRootWindowContainer.ensureActivitiesVisible(
+ null, 0, PRESERVE_WINDOWS);
+ } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
+ final PooledConsumer f = PooledLambda.obtainConsumer(
+ ActivityRecord::ensureActivityConfiguration,
+ PooledLambda.__(ActivityRecord.class), 0,
+ false /* preserveWindow */);
+ try {
+ for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
+ haveConfigChanges.valueAt(i).forAllActivities(f);
+ }
+ } finally {
+ f.recycle();
+ }
+ }
+ } finally {
+ mService.continueWindowLayout();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index e47eaee..f4e4245 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -36,7 +36,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-
import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
import static com.android.internal.policy.DecorView.getColorViewLeftInset;
@@ -188,8 +187,9 @@
return null;
}
sysUiVis = topFullscreenOpaqueWindow.getSystemUiVisibility();
- windowFlags = topFullscreenOpaqueWindow.getAttrs().flags;
- windowPrivateFlags = topFullscreenOpaqueWindow.getAttrs().privateFlags;
+ WindowManager.LayoutParams attrs = topFullscreenOpaqueWindow.mAttrs;
+ windowFlags = attrs.flags;
+ windowPrivateFlags = attrs.privateFlags;
layoutParams.packageName = mainWindow.getAttrs().packageName;
layoutParams.windowAnimations = mainWindow.getAttrs().windowAnimations;
@@ -204,6 +204,14 @@
layoutParams.width = LayoutParams.MATCH_PARENT;
layoutParams.height = LayoutParams.MATCH_PARENT;
layoutParams.systemUiVisibility = sysUiVis;
+ layoutParams.insetsFlags.behavior
+ = topFullscreenOpaqueWindow.mAttrs.insetsFlags.behavior;
+ layoutParams.insetsFlags.appearance
+ = topFullscreenOpaqueWindow.mAttrs.insetsFlags.appearance;
+ layoutParams.setFitInsetsTypes(attrs.getFitInsetsTypes());
+ layoutParams.setFitInsetsSides(attrs.getFitInsetsSides());
+ layoutParams.setFitInsetsIgnoringVisibility(attrs.isFitInsetsIgnoringVisibility());
+
layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId));
final TaskDescription td = task.getTaskDescription();
diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java
new file mode 100644
index 0000000..add11d6
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskTile.java
@@ -0,0 +1,225 @@
+/*
+ * 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.wm;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+
+import android.app.ActivityManager;
+import android.app.TaskInfo;
+import android.app.WindowConfiguration;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.Slog;
+import android.view.SurfaceControl;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+
+/**
+ * A Tile. Right now this acts as a proxy for manipulating non-child stacks. Eventually, this
+ * can become an actual parent.
+ */
+// TODO(task-hierarchy): Remove when tasks can nest >2 or when single tasks can handle their
+// own lifecycles.
+public class TaskTile extends ActivityStack {
+ private static final String TAG = "TaskTile";
+ final ArrayList<WindowContainer> mChildren = new ArrayList<>();
+
+ private static ActivityInfo createEmptyActivityInfo() {
+ ActivityInfo info = new ActivityInfo();
+ info.applicationInfo = new ApplicationInfo();
+ return info;
+ }
+
+ TaskTile(ActivityTaskManagerService atmService, int id, int windowingMode) {
+ super(atmService, id, new Intent() /*intent*/, null /*affinityIntent*/, null /*affinity*/,
+ null /*rootAffinity*/, null /*realActivity*/, null /*origActivity*/,
+ false /*rootWasReset*/, false /*autoRemoveRecents*/, false /*askedCompatMode*/,
+ 0 /*userId*/, 0 /*effectiveUid*/, null /*lastDescription*/,
+ System.currentTimeMillis(), true /*neverRelinquishIdentity*/,
+ new ActivityManager.TaskDescription(), id, INVALID_TASK_ID, INVALID_TASK_ID,
+ 0 /*taskAffiliationColor*/, 0 /*callingUid*/, "" /*callingPackage*/,
+ RESIZE_MODE_RESIZEABLE, false /*supportsPictureInPicture*/,
+ false /*_realActivitySuspended*/, false /*userSetupComplete*/, INVALID_MIN_SIZE,
+ INVALID_MIN_SIZE, createEmptyActivityInfo(), null /*voiceSession*/,
+ null /*voiceInteractor*/, null /*stack*/);
+ getRequestedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
+ }
+
+ @Override
+ void onDisplayChanged(DisplayContent dc) {
+ mDisplayContent = null;
+ if (dc != null) {
+ dc.getPendingTransaction().merge(getPendingTransaction());
+ }
+ mDisplayContent = dc;
+ // Virtual parent, so don't notify children.
+ }
+
+ /**
+ * If there is a disconnection, this will clean up any vestigial surfaces left on the tile
+ * leash by moving known children to a new surfacecontrol and then removing the old one.
+ */
+ void cleanupSurfaces() {
+ if (mSurfaceControl == null) {
+ return;
+ }
+ SurfaceControl oldSurface = mSurfaceControl;
+ WindowContainer parentWin = getParent();
+ if (parentWin == null) {
+ return;
+ }
+ mSurfaceControl = parentWin.makeChildSurface(null).setName("TaskTile " + mTaskId + " - "
+ + getRequestedOverrideWindowingMode()).setContainerLayer().build();
+ SurfaceControl.Transaction t = parentWin.getPendingTransaction();
+ t.show(mSurfaceControl);
+ for (int i = 0; i < mChildren.size(); ++i) {
+ if (mChildren.get(i).getSurfaceControl() == null) {
+ continue;
+ }
+ mChildren.get(i).reparentSurfaceControl(t, mSurfaceControl);
+ }
+ t.remove(oldSurface);
+ }
+
+ @Override
+ protected void addChild(WindowContainer child, Comparator<WindowContainer> comparator) {
+ throw new RuntimeException("Improper use of addChild() on Tile");
+ }
+
+ @Override
+ void addChild(WindowContainer child, int index) {
+ mChildren.add(child);
+ if (child instanceof ActivityStack) {
+ ((ActivityStack) child).setTile(this);
+ }
+ mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
+ this, false /* force */);
+ }
+
+ @Override
+ void removeChild(WindowContainer child) {
+ if (child instanceof ActivityStack) {
+ ((ActivityStack) child).setTile(null);
+ }
+ mChildren.remove(child);
+ mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
+ this, false /* force */);
+ }
+
+ void removeAllChildren() {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final WindowContainer child = mChildren.get(i);
+ if (child instanceof ActivityStack) {
+ ((ActivityStack) child).setTile(null);
+ }
+ }
+ mChildren.clear();
+ mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
+ this, false /* force */);
+ }
+
+ @Override
+ protected int getChildCount() {
+ // Currently 0 as this isn't a proper hierarchy member yet.
+ return 0;
+ }
+
+ @Override
+ public void setWindowingMode(/*@WindowConfiguration.WindowingMode*/ int windowingMode) {
+ Configuration c = new Configuration(getRequestedOverrideConfiguration());
+ c.windowConfiguration.setWindowingMode(windowingMode);
+ onRequestedOverrideConfigurationChanged(c);
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newParentConfig) {
+ super.onConfigurationChanged(newParentConfig);
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final WindowContainer child = mChildren.get(i);
+ child.onConfigurationChanged(child.getParent().getConfiguration());
+ }
+ }
+
+ /**
+ * Until this can be part of the hierarchy, the Stack level can use this utility during
+ * resolveOverrideConfig to simulate inheritance.
+ */
+ void updateResolvedConfig(Configuration inOutResolvedConfig) {
+ Rect resolveBounds = inOutResolvedConfig.windowConfiguration.getBounds();
+ if (resolveBounds == null || resolveBounds.isEmpty()) {
+ resolveBounds.set(getRequestedOverrideBounds());
+ }
+ int stackMode = inOutResolvedConfig.windowConfiguration.getWindowingMode();
+ if (stackMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED
+ || stackMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN) {
+ // Also replace FULLSCREEN because we interpret FULLSCREEN as "fill parent"
+ inOutResolvedConfig.windowConfiguration.setWindowingMode(
+ getRequestedOverrideWindowingMode());
+ }
+ if (inOutResolvedConfig.smallestScreenWidthDp
+ == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
+ inOutResolvedConfig.smallestScreenWidthDp =
+ getRequestedOverrideConfiguration().smallestScreenWidthDp;
+ }
+ }
+
+ @Override
+ void fillTaskInfo(TaskInfo info) {
+ super.fillTaskInfo(info);
+ WindowContainer top = null;
+ // Check mChildren.isEmpty directly because hasChild() -> getChildCount() always returns 0
+ if (!mChildren.isEmpty()) {
+ // Find the top-most root task which is a virtual child of this Tile. Because this is a
+ // virtual parent, the mChildren order here isn't changed during hierarchy operations.
+ WindowContainer parent = mChildren.get(0).getParent();
+ for (int i = parent.getChildCount() - 1; i >= 0; --i) {
+ if (mChildren.contains(parent.getChildAt(i))) {
+ top = parent.getChildAt(i);
+ break;
+ }
+ }
+ }
+ final Task topTask = top == null ? null : top.getTopMostTask();
+ boolean isResizable = topTask == null || topTask.isResizeable();
+ info.resizeMode = isResizable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE;
+ info.topActivityType = top == null ? ACTIVITY_TYPE_UNDEFINED : top.getActivityType();
+ info.configuration.setTo(getRequestedOverrideConfiguration());
+ }
+
+ @Override
+ void removeImmediately() {
+ removeAllChildren();
+ super.removeImmediately();
+ }
+
+ static TaskTile forToken(IBinder token) {
+ try {
+ return (TaskTile) ((TaskToken) token).getContainer();
+ } catch (ClassCastException e) {
+ Slog.w(TAG, "Bad tile token: " + token, e);
+ return null;
+ }
+ }
+}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 4a2636e..390068e 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -22,6 +22,8 @@
"BroadcastRadio/TunerCallback.cpp",
"BroadcastRadio/convert.cpp",
"BroadcastRadio/regions.cpp",
+ "stats/PowerStatsPuller.cpp",
+ "stats/SubsystemSleepStatePuller.cpp",
"com_android_server_am_BatteryStatsService.cpp",
"com_android_server_connectivity_Vpn.cpp",
"com_android_server_ConsumerIrService.cpp",
@@ -37,6 +39,7 @@
"com_android_server_security_VerityUtils.cpp",
"com_android_server_SerialService.cpp",
"com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp",
+ "com_android_server_stats_pull_StatsPullAtomService.cpp",
"com_android_server_storage_AppFuseBridge.cpp",
"com_android_server_SystemServer.cpp",
"com_android_server_TestNetworkService.cpp",
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 78b64ca..67254b8 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -14,6 +14,12 @@
* limitations under the License.
*/
+#include <dlfcn.h>
+#include <pthread.h>
+
+#include <chrono>
+#include <thread>
+
#include <jni.h>
#include <nativehelper/JNIHelp.h>
@@ -25,12 +31,17 @@
#include <sensorservicehidl/SensorManager.h>
#include <bionic/malloc.h>
+#include <bionic/reserved_signals.h>
+#include <android-base/properties.h>
#include <cutils/properties.h>
#include <utils/Log.h>
#include <utils/misc.h>
#include <utils/AndroidThreads.h>
+using android::base::GetIntProperty;
+using namespace std::chrono_literals;
+
namespace android {
static void android_server_SystemServer_startSensorService(JNIEnv* /* env */, jobject /* clazz */) {
@@ -68,7 +79,50 @@
static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /* env */,
jobject /* clazz */) {
- android_mallopt(M_INIT_ZYGOTE_CHILD_PROFILING, nullptr, 0);
+ android_mallopt(M_INIT_ZYGOTE_CHILD_PROFILING, nullptr, 0);
+}
+
+static int get_current_max_fd() {
+ // Not actually guaranteed to be the max, but close enough for our purposes.
+ int fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
+ LOG_ALWAYS_FATAL_IF(fd == -1, "failed to open /dev/null: %s", strerror(errno));
+ close(fd);
+ return fd;
+}
+
+static const char kFdLeakEnableThresholdProperty[] = "persist.sys.debug.fdtrack_enable_threshold";
+static const char kFdLeakAbortThresholdProperty[] = "persist.sys.debug.fdtrack_abort_threshold";
+static const char kFdLeakCheckIntervalProperty[] = "persist.sys.debug.fdtrack_interval";
+
+static void android_server_SystemServer_spawnFdLeakCheckThread(JNIEnv*, jobject) {
+ std::thread([]() {
+ pthread_setname_np(pthread_self(), "FdLeakCheckThread");
+ bool loaded = false;
+ while (true) {
+ const int enable_threshold = GetIntProperty(kFdLeakEnableThresholdProperty, 1024);
+ const int abort_threshold = GetIntProperty(kFdLeakAbortThresholdProperty, 2048);
+ const int check_interval = GetIntProperty(kFdLeakCheckIntervalProperty, 120);
+ int max_fd = get_current_max_fd();
+ if (max_fd > enable_threshold && !loaded) {
+ loaded = true;
+ ALOGE("fd count above threshold of %d, starting fd backtraces", enable_threshold);
+ if (dlopen("libfdtrack.so", RTLD_GLOBAL) == nullptr) {
+ ALOGE("failed to load libfdtrack.so: %s", dlerror());
+ }
+ } else if (max_fd > abort_threshold) {
+ raise(BIONIC_SIGNAL_FDTRACK);
+
+ // Wait for a bit to allow fdtrack to dump backtraces to logcat.
+ std::this_thread::sleep_for(5s);
+
+ LOG_ALWAYS_FATAL(
+ "b/140703823: aborting due to fd leak: check logs for fd "
+ "backtraces");
+ }
+
+ std::this_thread::sleep_for(std::chrono::seconds(check_interval));
+ }
+ }).detach();
}
/*
@@ -80,6 +134,9 @@
{ "startHidlServices", "()V", (void*) android_server_SystemServer_startHidlServices },
{ "initZygoteChildHeapProfiling", "()V",
(void*) android_server_SystemServer_initZygoteChildHeapProfiling },
+ { "spawnFdLeakCheckThread", "()V",
+ (void*) android_server_SystemServer_spawnFdLeakCheckThread },
+
};
int register_android_server_SystemServer(JNIEnv* env)
diff --git a/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp b/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp
new file mode 100644
index 0000000..f5b778e
--- /dev/null
+++ b/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "StatsPullAtomService"
+
+#include <jni.h>
+#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
+#include <statslog.h>
+
+#include "stats/PowerStatsPuller.h"
+#include "stats/SubsystemSleepStatePuller.h"
+
+namespace android {
+
+static server::stats::PowerStatsPuller gPowerStatsPuller;
+static server::stats::SubsystemSleepStatePuller gSubsystemSleepStatePuller;
+
+static status_pull_atom_return_t onDevicePowerMeasurementCallback(int32_t atom_tag,
+ pulled_stats_event_list* data,
+ void* cookie) {
+ return gPowerStatsPuller.Pull(atom_tag, data);
+}
+
+static status_pull_atom_return_t subsystemSleepStateCallback(int32_t atom_tag,
+ pulled_stats_event_list* data,
+ void* cookie) {
+ return gSubsystemSleepStatePuller.Pull(atom_tag, data);
+}
+
+static void nativeInit(JNIEnv* env, jobject javaObject) {
+ // on device power measurement
+ gPowerStatsPuller = server::stats::PowerStatsPuller();
+ register_stats_pull_atom_callback(android::util::ON_DEVICE_POWER_MEASUREMENT,
+ onDevicePowerMeasurementCallback,
+ /* metadata= */ nullptr,
+ /* cookie= */ nullptr);
+
+ // subsystem sleep state
+ gSubsystemSleepStatePuller = server::stats::SubsystemSleepStatePuller();
+ register_stats_pull_atom_callback(android::util::SUBSYSTEM_SLEEP_STATE,
+ subsystemSleepStateCallback,
+ /* metadata= */ nullptr,
+ /* cookie= */ nullptr);
+}
+
+static const JNINativeMethod sMethods[] = {{"nativeInit", "()V", (void*)nativeInit}};
+
+int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "com/android/server/stats/pull/StatsPullAtomService",
+ sMethods, NELEM(sMethods));
+ if (res < 0) {
+ ALOGE("failed to register native methods");
+ }
+ return res;
+}
+
+} // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 19fa062..1202ad3 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -59,6 +59,7 @@
int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
JNIEnv* env);
int register_android_server_incremental_IncrementalManagerService(JNIEnv* env);
+int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
};
using namespace android;
@@ -111,5 +112,6 @@
register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
env);
register_android_server_incremental_IncrementalManagerService(env);
+ register_android_server_stats_pull_StatsPullAtomService(env);
return JNI_VERSION_1_4;
}
diff --git a/services/core/jni/stats/OWNERS b/services/core/jni/stats/OWNERS
new file mode 100644
index 0000000..a61babf
--- /dev/null
+++ b/services/core/jni/stats/OWNERS
@@ -0,0 +1,9 @@
+jeffreyhuang@google.com
+joeo@google.com
+jtnguyen@google.com
+muhammadq@google.com
+ruchirr@google.com
+singhtejinder@google.com
+tsaichristine@google.com
+yaochen@google.com
+yro@google.com
diff --git a/services/core/jni/stats/PowerStatsPuller.cpp b/services/core/jni/stats/PowerStatsPuller.cpp
new file mode 100644
index 0000000..e80b5cf
--- /dev/null
+++ b/services/core/jni/stats/PowerStatsPuller.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG false // STOPSHIP if true
+#define LOG_TAG "PowerStatsPuller"
+
+#include <android/hardware/power/stats/1.0/IPowerStats.h>
+#include <log/log.h>
+#include <statslog.h>
+
+#include <vector>
+
+#include "PowerStatsPuller.h"
+
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::power::stats::V1_0::EnergyData;
+using android::hardware::power::stats::V1_0::IPowerStats;
+using android::hardware::power::stats::V1_0::RailInfo;
+using android::hardware::power::stats::V1_0::Status;
+
+namespace android {
+namespace server {
+namespace stats {
+
+static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHal = nullptr;
+static std::mutex gPowerStatsHalMutex;
+static bool gPowerStatsExist = true; // Initialized to ensure making a first attempt.
+static std::vector<RailInfo> gRailInfo;
+
+struct PowerStatsPullerDeathRecipient : virtual public hardware::hidl_death_recipient {
+ virtual void serviceDied(uint64_t cookie,
+ const wp<android::hidl::base::V1_0::IBase>& who) override {
+ // The HAL just died. Reset all handles to HAL services.
+ std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
+ gPowerStatsHal = nullptr;
+ }
+};
+
+static sp<PowerStatsPullerDeathRecipient> gDeathRecipient = new PowerStatsPullerDeathRecipient();
+
+static bool getPowerStatsHalLocked() {
+ if (gPowerStatsHal == nullptr && gPowerStatsExist) {
+ gPowerStatsHal = android::hardware::power::stats::V1_0::IPowerStats::getService();
+ if (gPowerStatsHal == nullptr) {
+ ALOGW("Couldn't load power.stats HAL service");
+ gPowerStatsExist = false;
+ } else {
+ // Link death recipient to power.stats service handle
+ hardware::Return<bool> linked = gPowerStatsHal->linkToDeath(gDeathRecipient, 0);
+ if (!linked.isOk()) {
+ ALOGE("Transaction error in linking to power.stats HAL death: %s",
+ linked.description().c_str());
+ gPowerStatsHal = nullptr;
+ return false;
+ } else if (!linked) {
+ ALOGW("Unable to link to power.stats HAL death notifications");
+ // We should still continue even though linking failed
+ }
+ }
+ }
+ return gPowerStatsHal != nullptr;
+}
+
+PowerStatsPuller::PowerStatsPuller() {}
+
+status_pull_atom_return_t PowerStatsPuller::Pull(int32_t atomTag, pulled_stats_event_list* data) {
+ std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
+
+ if (!getPowerStatsHalLocked()) {
+ return STATS_PULL_SKIP;
+ }
+
+ // Pull getRailInfo if necessary
+ if (gRailInfo.empty()) {
+ bool resultSuccess = true;
+ Return<void> ret = gPowerStatsHal->getRailInfo(
+ [&resultSuccess](const hidl_vec<RailInfo>& list, Status status) {
+ resultSuccess = (status == Status::SUCCESS || status == Status::NOT_SUPPORTED);
+ if (status != Status::SUCCESS) return;
+ gRailInfo.reserve(list.size());
+ for (size_t i = 0; i < list.size(); ++i) {
+ gRailInfo.push_back(list[i]);
+ }
+ });
+ if (!resultSuccess || !ret.isOk()) {
+ ALOGE("power.stats getRailInfo() failed. Description: %s", ret.description().c_str());
+ gPowerStatsHal = nullptr;
+ return STATS_PULL_SKIP;
+ }
+ // If SUCCESS but empty, or if NOT_SUPPORTED, then never try again.
+ if (gRailInfo.empty()) {
+ ALOGE("power.stats has no rail information");
+ gPowerStatsExist = false; // No rail info, so never try again.
+ gPowerStatsHal = nullptr;
+ return STATS_PULL_SKIP;
+ }
+ }
+
+ // Pull getEnergyData and write the data out
+ const hidl_vec<uint32_t> desiredRailIndices; // Empty vector indicates we want all.
+ bool resultSuccess = true;
+ Return<void> ret =
+ gPowerStatsHal
+ ->getEnergyData(desiredRailIndices,
+ [&data, &resultSuccess](hidl_vec<EnergyData> energyDataList,
+ Status status) {
+ resultSuccess = (status == Status::SUCCESS);
+ if (!resultSuccess) return;
+
+ for (size_t i = 0; i < energyDataList.size(); i++) {
+ const EnergyData& energyData = energyDataList[i];
+
+ if (energyData.index >= gRailInfo.size()) {
+ ALOGE("power.stats getEnergyData() returned an "
+ "invalid rail index %u.",
+ energyData.index);
+ resultSuccess = false;
+ return;
+ }
+ const RailInfo& rail = gRailInfo[energyData.index];
+
+ stats_event* event = add_stats_event_to_pull_data(data);
+ stats_event_set_atom_id(event,
+ android::util::ON_DEVICE_POWER_MEASUREMENT);
+ stats_event_write_string8(event,
+ rail.subsysName.c_str());
+ stats_event_write_string8(event, rail.railName.c_str());
+ stats_event_write_int64(event, energyData.timestamp);
+ stats_event_write_int64(event, energyData.energy);
+ stats_event_build(event);
+
+ ALOGV("power.stat: %s.%s: %llu, %llu",
+ rail.subsysName.c_str(), rail.railName.c_str(),
+ (unsigned long long)energyData.timestamp,
+ (unsigned long long)energyData.energy);
+ }
+ });
+ if (!resultSuccess || !ret.isOk()) {
+ ALOGE("power.stats getEnergyData() failed. Description: %s", ret.description().c_str());
+ gPowerStatsHal = nullptr;
+ return STATS_PULL_SKIP;
+ }
+ return STATS_PULL_SUCCESS;
+}
+
+} // namespace stats
+} // namespace server
+} // namespace android
diff --git a/cmds/statsd/src/external/PowerStatsPuller.h b/services/core/jni/stats/PowerStatsPuller.h
similarity index 72%
rename from cmds/statsd/src/external/PowerStatsPuller.h
rename to services/core/jni/stats/PowerStatsPuller.h
index 6f15bd6..048dbb9 100644
--- a/cmds/statsd/src/external/PowerStatsPuller.h
+++ b/services/core/jni/stats/PowerStatsPuller.h
@@ -16,23 +16,22 @@
#pragma once
-#include "StatsPuller.h"
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
namespace android {
-namespace os {
-namespace statsd {
+namespace server {
+namespace stats {
/**
* Reads hal for power.stats
*/
-class PowerStatsPuller : public StatsPuller {
+class PowerStatsPuller {
public:
PowerStatsPuller();
-
-private:
- bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+ status_pull_atom_return_t Pull(int32_t atomTag, pulled_stats_event_list* data);
};
-} // namespace statsd
-} // namespace os
-} // namespace android
+} // namespace stats
+} // namespace server
+} // namespace android
diff --git a/services/core/jni/stats/SubsystemSleepStatePuller.cpp b/services/core/jni/stats/SubsystemSleepStatePuller.cpp
new file mode 100644
index 0000000..c6a836c
--- /dev/null
+++ b/services/core/jni/stats/SubsystemSleepStatePuller.cpp
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG false // STOPSHIP if true
+#define LOG_TAG "SubsystemSleepStatePuller"
+
+#include <log/log.h>
+#include <statslog.h>
+
+#include <android/hardware/power/1.0/IPower.h>
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/stats/1.0/IPowerStats.h>
+
+#include <fcntl.h>
+#include <hardware/power.h>
+#include <hardware_legacy/power.h>
+#include <inttypes.h>
+#include <semaphore.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <unordered_map>
+#include "SubsystemSleepStatePuller.h"
+
+using android::hardware::hidl_vec;
+using android::hardware::power::V1_0::IPower;
+using android::hardware::power::V1_0::PowerStatePlatformSleepState;
+using android::hardware::power::V1_0::PowerStateVoter;
+using android::hardware::power::V1_1::PowerStateSubsystem;
+using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
+using android::hardware::power::stats::V1_0::PowerEntityInfo;
+using android::hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
+using android::hardware::power::stats::V1_0::PowerEntityStateSpace;
+
+using android::hardware::Return;
+using android::hardware::Void;
+
+namespace android {
+namespace server {
+namespace stats {
+
+static std::function<status_pull_atom_return_t(int32_t atomTag, pulled_stats_event_list* data)>
+ gPuller = {};
+
+static sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
+static sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr;
+static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr;
+
+static std::unordered_map<uint32_t, std::string> gEntityNames = {};
+static std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> gStateNames = {};
+
+static std::mutex gPowerHalMutex;
+
+// The caller must be holding gPowerHalMutex.
+static void deinitPowerStatsLocked() {
+ gPowerHalV1_0 = nullptr;
+ gPowerHalV1_1 = nullptr;
+ gPowerStatsHalV1_0 = nullptr;
+}
+
+struct SubsystemSleepStatePullerDeathRecipient : virtual public hardware::hidl_death_recipient {
+ virtual void serviceDied(uint64_t cookie,
+ const wp<android::hidl::base::V1_0::IBase>& who) override {
+
+ // The HAL just died. Reset all handles to HAL services.
+ std::lock_guard<std::mutex> lock(gPowerHalMutex);
+ deinitPowerStatsLocked();
+ }
+};
+
+static sp<SubsystemSleepStatePullerDeathRecipient> gDeathRecipient =
+ new SubsystemSleepStatePullerDeathRecipient();
+
+SubsystemSleepStatePuller::SubsystemSleepStatePuller() {}
+
+// The caller must be holding gPowerHalMutex.
+static bool checkResultLocked(const Return<void> &ret, const char* function) {
+ if (!ret.isOk()) {
+ ALOGE("%s failed: requested HAL service not available. Description: %s",
+ function, ret.description().c_str());
+ if (ret.isDeadObject()) {
+ deinitPowerStatsLocked();
+ }
+ return false;
+ }
+ return true;
+}
+
+// The caller must be holding gPowerHalMutex.
+// gPowerStatsHalV1_0 must not be null
+static bool initializePowerStats() {
+ using android::hardware::power::stats::V1_0::Status;
+
+ // Clear out previous content if we are re-initializing
+ gEntityNames.clear();
+ gStateNames.clear();
+
+ Return<void> ret;
+ ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) {
+ if (status != Status::SUCCESS) {
+ ALOGE("Error getting power entity info");
+ return;
+ }
+
+ // construct lookup table of powerEntityId to power entity name
+ for (auto info : infos) {
+ gEntityNames.emplace(info.powerEntityId, info.powerEntityName);
+ }
+ });
+ if (!checkResultLocked(ret, __func__)) {
+ return false;
+ }
+
+ ret = gPowerStatsHalV1_0->getPowerEntityStateInfo({}, [](auto stateSpaces, auto status) {
+ if (status != Status::SUCCESS) {
+ ALOGE("Error getting state info");
+ return;
+ }
+
+ // construct lookup table of powerEntityId, powerEntityStateId to power entity state name
+ for (auto stateSpace : stateSpaces) {
+ std::unordered_map<uint32_t, std::string> stateNames = {};
+ for (auto state : stateSpace.states) {
+ stateNames.emplace(state.powerEntityStateId,
+ state.powerEntityStateName);
+ }
+ gStateNames.emplace(stateSpace.powerEntityId, stateNames);
+ }
+ });
+ if (!checkResultLocked(ret, __func__)) {
+ return false;
+ }
+
+ return (!gEntityNames.empty()) && (!gStateNames.empty());
+}
+
+// The caller must be holding gPowerHalMutex.
+static bool getPowerStatsHalLocked() {
+ if(gPowerStatsHalV1_0 == nullptr) {
+ gPowerStatsHalV1_0 = android::hardware::power::stats::V1_0::IPowerStats::getService();
+ if (gPowerStatsHalV1_0 == nullptr) {
+ ALOGE("Unable to get power.stats HAL service.");
+ return false;
+ }
+
+ // Link death recipient to power.stats service handle
+ hardware::Return<bool> linked = gPowerStatsHalV1_0->linkToDeath(gDeathRecipient, 0);
+ if (!linked.isOk()) {
+ ALOGE("Transaction error in linking to power.stats HAL death: %s",
+ linked.description().c_str());
+ deinitPowerStatsLocked();
+ return false;
+ } else if (!linked) {
+ ALOGW("Unable to link to power.stats HAL death notifications");
+ // We should still continue even though linking failed
+ }
+ return initializePowerStats();
+ }
+ return true;
+}
+
+// The caller must be holding gPowerHalMutex.
+static status_pull_atom_return_t getIPowerStatsDataLocked(int32_t atomTag,
+ pulled_stats_event_list* data) {
+ using android::hardware::power::stats::V1_0::Status;
+
+ if(!getPowerStatsHalLocked()) {
+ return STATS_PULL_SKIP;
+ }
+ // Get power entity state residency data
+ bool success = false;
+ Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData(
+ {}, [&data, &success](auto results, auto status) {
+ if (status == Status::NOT_SUPPORTED) {
+ ALOGW("getPowerEntityStateResidencyData is not supported");
+ success = false;
+ return;
+ }
+ for (auto result : results) {
+ for (auto stateResidency : result.stateResidencyData) {
+ stats_event* event = add_stats_event_to_pull_data(data);
+ stats_event_set_atom_id(event, android::util::SUBSYSTEM_SLEEP_STATE);
+ stats_event_write_string8(event,
+ gEntityNames.at(result.powerEntityId).c_str());
+ stats_event_write_string8(event,
+ gStateNames.at(result.powerEntityId)
+ .at(stateResidency.powerEntityStateId)
+ .c_str());
+ stats_event_write_int64(event, stateResidency.totalStateEntryCount);
+ stats_event_write_int64(event, stateResidency.totalTimeInStateMs);
+ stats_event_build(event);
+ }
+ }
+ success = true;
+ });
+ // Intentionally not returning early here.
+ // bool success determines if this succeeded or not.
+ checkResultLocked(ret, __func__);
+ if (!success) {
+ return STATS_PULL_SKIP;
+ }
+ return STATS_PULL_SUCCESS;
+}
+
+// The caller must be holding gPowerHalMutex.
+static bool getPowerHalLocked() {
+ if(gPowerHalV1_0 == nullptr) {
+ gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
+ if(gPowerHalV1_0 == nullptr) {
+ ALOGE("Unable to get power HAL service.");
+ return false;
+ }
+ gPowerHalV1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
+
+ // Link death recipient to power service handle
+ hardware::Return<bool> linked = gPowerHalV1_0->linkToDeath(gDeathRecipient, 0);
+ if (!linked.isOk()) {
+ ALOGE("Transaction error in linking to power HAL death: %s",
+ linked.description().c_str());
+ gPowerHalV1_0 = nullptr;
+ return false;
+ } else if (!linked) {
+ ALOGW("Unable to link to power. death notifications");
+ // We should still continue even though linking failed
+ }
+ }
+ return true;
+}
+
+// The caller must be holding gPowerHalMutex.
+static status_pull_atom_return_t getIPowerDataLocked(int32_t atomTag,
+ pulled_stats_event_list* data) {
+ using android::hardware::power::V1_0::Status;
+
+ if(!getPowerHalLocked()) {
+ return STATS_PULL_SKIP;
+ }
+
+ Return<void> ret;
+ ret = gPowerHalV1_0->getPlatformLowPowerStats(
+ [&data](hidl_vec<PowerStatePlatformSleepState> states, Status status) {
+ if (status != Status::SUCCESS) return;
+
+ for (size_t i = 0; i < states.size(); i++) {
+ const PowerStatePlatformSleepState& state = states[i];
+ stats_event* event = add_stats_event_to_pull_data(data);
+ stats_event_set_atom_id(event, android::util::SUBSYSTEM_SLEEP_STATE);
+ stats_event_write_string8(event, state.name.c_str());
+ stats_event_write_string8(event, "");
+ stats_event_write_int64(event, state.totalTransitions);
+ stats_event_write_int64(event, state.residencyInMsecSinceBoot);
+ stats_event_build(event);
+
+ ALOGV("powerstate: %s, %lld, %lld, %d", state.name.c_str(),
+ (long long)state.residencyInMsecSinceBoot,
+ (long long)state.totalTransitions,
+ state.supportedOnlyInSuspend ? 1 : 0);
+ for (const auto& voter : state.voters) {
+ stats_event* event = add_stats_event_to_pull_data(data);
+ stats_event_set_atom_id(event, android::util::SUBSYSTEM_SLEEP_STATE);
+ stats_event_write_string8(event, state.name.c_str());
+ stats_event_write_string8(event, voter.name.c_str());
+ stats_event_write_int64(event, voter.totalNumberOfTimesVotedSinceBoot);
+ stats_event_write_int64(event, voter.totalTimeInMsecVotedForSinceBoot);
+ stats_event_build(event);
+
+ ALOGV("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(),
+ voter.name.c_str(),
+ (long long)voter.totalTimeInMsecVotedForSinceBoot,
+ (long long)voter.totalNumberOfTimesVotedSinceBoot);
+ }
+ }
+ });
+ if (!checkResultLocked(ret, __func__)) {
+ return STATS_PULL_SKIP;
+ }
+
+ // Trying to cast to IPower 1.1, this will succeed only for devices supporting 1.1
+ sp<android::hardware::power::V1_1::IPower> gPowerHal_1_1 =
+ android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
+ if (gPowerHal_1_1 != nullptr) {
+ ret = gPowerHal_1_1->getSubsystemLowPowerStats(
+ [&data](hidl_vec<PowerStateSubsystem> subsystems, Status status) {
+ if (status != Status::SUCCESS) return;
+
+ if (subsystems.size() > 0) {
+ for (size_t i = 0; i < subsystems.size(); i++) {
+ const PowerStateSubsystem& subsystem = subsystems[i];
+ for (size_t j = 0; j < subsystem.states.size(); j++) {
+ const PowerStateSubsystemSleepState& state =
+ subsystem.states[j];
+ stats_event* event = add_stats_event_to_pull_data(data);
+ stats_event_set_atom_id(event,
+ android::util::SUBSYSTEM_SLEEP_STATE);
+ stats_event_write_string8(event, subsystem.name.c_str());
+ stats_event_write_string8(event, state.name.c_str());
+ stats_event_write_int64(event, state.totalTransitions);
+ stats_event_write_int64(event, state.residencyInMsecSinceBoot);
+ stats_event_build(event);
+
+ ALOGV("subsystemstate: %s, %s, %lld, %lld, %lld",
+ subsystem.name.c_str(), state.name.c_str(),
+ (long long)state.residencyInMsecSinceBoot,
+ (long long)state.totalTransitions,
+ (long long)state.lastEntryTimestampMs);
+ }
+ }
+ }
+ });
+ }
+ return STATS_PULL_SUCCESS;
+}
+
+// The caller must be holding gPowerHalMutex.
+std::function<status_pull_atom_return_t(int32_t atomTag, pulled_stats_event_list* data)>
+getPullerLocked() {
+ std::function<status_pull_atom_return_t(int32_t atomTag, pulled_stats_event_list * data)> ret =
+ {};
+
+ // First see if power.stats HAL is available. Fall back to power HAL if
+ // power.stats HAL is unavailable.
+ if(android::hardware::power::stats::V1_0::IPowerStats::getService() != nullptr) {
+ ALOGI("Using power.stats HAL");
+ ret = getIPowerStatsDataLocked;
+ } else if(android::hardware::power::V1_0::IPower::getService() != nullptr) {
+ ALOGI("Using power HAL");
+ ret = getIPowerDataLocked;
+ }
+
+ return ret;
+}
+
+status_pull_atom_return_t SubsystemSleepStatePuller::Pull(int32_t atomTag,
+ pulled_stats_event_list* data) {
+ std::lock_guard<std::mutex> lock(gPowerHalMutex);
+
+ if(!gPuller) {
+ gPuller = getPullerLocked();
+ }
+
+ if(gPuller) {
+ return gPuller(atomTag, data);
+ }
+
+ ALOGE("Unable to load Power Hal or power.stats HAL");
+ return STATS_PULL_SKIP;
+}
+
+} // namespace stats
+} // namespace server
+} // namespace android
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.h b/services/core/jni/stats/SubsystemSleepStatePuller.h
similarity index 71%
rename from cmds/statsd/src/external/SubsystemSleepStatePuller.h
rename to services/core/jni/stats/SubsystemSleepStatePuller.h
index 87f5f02..59dbbd2 100644
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.h
+++ b/services/core/jni/stats/SubsystemSleepStatePuller.h
@@ -16,24 +16,22 @@
#pragma once
-#include <utils/String16.h>
-#include "StatsPuller.h"
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
namespace android {
-namespace os {
-namespace statsd {
+namespace server {
+namespace stats {
/**
* Reads hal for sleep states
*/
-class SubsystemSleepStatePuller : public StatsPuller {
+class SubsystemSleepStatePuller {
public:
SubsystemSleepStatePuller();
-
-private:
- bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+ status_pull_atom_return_t Pull(int32_t atomTag, pulled_stats_event_list* data);
};
-} // namespace statsd
-} // namespace os
-} // namespace android
+} // namespace stats
+} // namespace server
+} // namespace android
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b7162f9..6ba4330 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -342,6 +342,7 @@
private static final String START_SENSOR_SERVICE = "StartSensorService";
private static final String START_HIDL_SERVICES = "StartHidlServices";
+ private static final String START_BLOB_STORE_SERVICE = "startBlobStoreManagerService";
private static final String SYSPROP_START_COUNT = "sys.system_server.start_count";
private static final String SYSPROP_START_ELAPSED = "sys.system_server.start_elapsed";
@@ -349,6 +350,7 @@
private Future<?> mSensorServiceStart;
private Future<?> mZygotePreload;
+ private Future<?> mBlobStoreServiceStart;
/**
* Start the sensor service. This is a blocking call and can take time.
@@ -365,6 +367,12 @@
*/
private static native void initZygoteChildHeapProfiling();
+
+ /**
+ * Spawn a thread that monitors for fd leaks.
+ */
+ private static native void spawnFdLeakCheckThread();
+
/**
* The main entry point from zygote.
*/
@@ -499,6 +507,11 @@
initZygoteChildHeapProfiling();
}
+ // Debug builds - spawn a thread to monitor for fd leaks.
+ if (Build.IS_DEBUGGABLE) {
+ spawnFdLeakCheckThread();
+ }
+
// Check whether we failed to shut down last time we tried.
// This call may not return.
performPendingShutdown();
@@ -1784,6 +1797,13 @@
t.traceEnd();
}
+ mBlobStoreServiceStart = SystemServerInitThreadPool.submit(() -> {
+ final TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
+ traceLog.traceBegin(START_BLOB_STORE_SERVICE);
+ mSystemServiceManager.startService(BLOB_STORE_MANAGER_SERVICE_CLASS);
+ traceLog.traceEnd();
+ }, START_BLOB_STORE_SERVICE);
+
// Dreams (interactive idle-time views, a/k/a screen savers, and doze mode)
t.traceBegin("StartDreamManager");
mSystemServiceManager.startService(DreamManagerService.class);
@@ -2028,10 +2048,6 @@
mSystemServiceManager.startService(ClipboardService.class);
t.traceEnd();
- t.traceBegin("StartBlobStoreManagerService");
- mSystemServiceManager.startService(BLOB_STORE_MANAGER_SERVICE_CLASS);
- t.traceEnd();
-
t.traceBegin("AppServiceManager");
mSystemServiceManager.startService(AppBindingService.Lifecycle.class);
t.traceEnd();
@@ -2149,6 +2165,9 @@
mSystemServiceManager.startService(APP_SEARCH_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ ConcurrentUtils.waitForFutureNoInterrupt(mBlobStoreServiceStart,
+ START_BLOB_STORE_SERVICE);
+
// These are needed to propagate to the runnable below.
final NetworkManagementService networkManagementF = networkManagement;
final NetworkStatsService networkStatsF = networkStats;
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index f990810..8381205 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -46,6 +46,7 @@
"service-appsearch",
"service-jobscheduler",
"service-permission",
+ "service-blobstore",
// TODO: remove once Android migrates to JUnit 4.12,
// which provides assertThrows
"testng",
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index b7c9001..d2ddff3 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -66,8 +66,6 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.SUSPEND_APPS"/>
<uses-permission android:name="android.permission.CONTROL_KEYGUARD"/>
- <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
- <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
<uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/>
<uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
diff --git a/services/tests/servicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
new file mode 100644
index 0000000..ff728e7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 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.blob;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.blob.BlobHandle;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
+import android.util.LongSparseArray;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.blob.BlobStoreManagerService.Injector;
+import com.android.server.blob.BlobStoreManagerService.SessionStateChangeListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class BlobStoreManagerServiceTest {
+ private Context mContext;
+ private Handler mHandler;
+ private BlobStoreManagerService mService;
+
+ private LongSparseArray<BlobStoreSession> mUserSessions;
+ private ArrayMap<BlobHandle, BlobMetadata> mUserBlobs;
+
+ private SessionStateChangeListener mStateChangeListener;
+
+ private static final String TEST_PKG1 = "com.example1";
+ private static final String TEST_PKG2 = "com.example2";
+ private static final String TEST_PKG3 = "com.example3";
+
+ private static final int TEST_UID1 = 10001;
+ private static final int TEST_UID2 = 10002;
+ private static final int TEST_UID3 = 10003;
+
+ @Before
+ public void setUp() {
+ // Share classloader to allow package private access.
+ System.setProperty("dexmaker.share_classloader", "true");
+
+ mContext = InstrumentationRegistry.getTargetContext();
+ mHandler = new TestHandler(Looper.getMainLooper());
+ mService = new BlobStoreManagerService(mContext, new TestInjector());
+ mUserSessions = new LongSparseArray<>();
+ mUserBlobs = new ArrayMap<>();
+
+ mService.addUserSessionsForTest(mUserSessions, UserHandle.myUserId());
+ mService.addUserBlobsForTest(mUserBlobs, UserHandle.myUserId());
+
+ mStateChangeListener = mService.new SessionStateChangeListener();
+ }
+
+ @Test
+ public void testHandlePackageRemoved() throws Exception {
+ // Setup sessions
+ final File sessionFile1 = mock(File.class);
+ final long sessionId1 = 11;
+ final BlobStoreSession session1 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1,
+ sessionId1, sessionFile1);
+ mUserSessions.append(sessionId1, session1);
+
+ final File sessionFile2 = mock(File.class);
+ final long sessionId2 = 25;
+ final BlobStoreSession session2 = createBlobStoreSessionMock(TEST_PKG2, TEST_UID2,
+ sessionId2, sessionFile2);
+ mUserSessions.append(sessionId2, session2);
+
+ final File sessionFile3 = mock(File.class);
+ final long sessionId3 = 37;
+ final BlobStoreSession session3 = createBlobStoreSessionMock(TEST_PKG3, TEST_UID3,
+ sessionId3, sessionFile3);
+ mUserSessions.append(sessionId3, session3);
+
+ final File sessionFile4 = mock(File.class);
+ final long sessionId4 = 48;
+ final BlobStoreSession session4 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1,
+ sessionId4, sessionFile4);
+ mUserSessions.append(sessionId4, session4);
+
+ // Setup blobs
+ final File blobFile1 = mock(File.class);
+ final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(),
+ "label1", System.currentTimeMillis(), "tag1");
+ final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobFile1, true);
+ mUserBlobs.put(blobHandle1, blobMetadata1);
+
+ final File blobFile2 = mock(File.class);
+ final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest2".getBytes(),
+ "label2", System.currentTimeMillis(), "tag2");
+ final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobFile2, false);
+ mUserBlobs.put(blobHandle2, blobMetadata2);
+
+ // Invoke test method
+ mService.handlePackageRemoved(TEST_PKG1, TEST_UID1);
+
+ // Verify sessions are removed
+ verify(sessionFile1).delete();
+ verify(sessionFile2, never()).delete();
+ verify(sessionFile3, never()).delete();
+ verify(sessionFile4).delete();
+
+ assertThat(mUserSessions.size()).isEqualTo(2);
+ assertThat(mUserSessions.get(sessionId1)).isNull();
+ assertThat(mUserSessions.get(sessionId2)).isNotNull();
+ assertThat(mUserSessions.get(sessionId3)).isNotNull();
+ assertThat(mUserSessions.get(sessionId4)).isNull();
+
+ // Verify blobs are removed
+ verify(blobMetadata1).removeCommitter(TEST_PKG1, TEST_UID1);
+ verify(blobMetadata1).removeLeasee(TEST_PKG1, TEST_UID1);
+ verify(blobMetadata2).removeCommitter(TEST_PKG1, TEST_UID1);
+ verify(blobMetadata2).removeLeasee(TEST_PKG1, TEST_UID1);
+
+ verify(blobFile1, never()).delete();
+ verify(blobFile2).delete();
+
+ assertThat(mUserBlobs.size()).isEqualTo(1);
+ assertThat(mUserBlobs.get(blobHandle1)).isNotNull();
+ assertThat(mUserBlobs.get(blobHandle2)).isNull();
+ }
+
+ private BlobStoreSession createBlobStoreSessionMock(String ownerPackageName, int ownerUid,
+ long sessionId, File sessionFile) {
+ final BlobStoreSession session = mock(BlobStoreSession.class);
+ when(session.getOwnerPackageName()).thenReturn(ownerPackageName);
+ when(session.getOwnerUid()).thenReturn(ownerUid);
+ when(session.getSessionId()).thenReturn(sessionId);
+ when(session.getSessionFile()).thenReturn(sessionFile);
+ return session;
+ }
+
+ private BlobMetadata createBlobMetadataMock(File blobFile, boolean hasLeases) {
+ final BlobMetadata blobMetadata = mock(BlobMetadata.class);
+ when(blobMetadata.getBlobFile()).thenReturn(blobFile);
+ when(blobMetadata.hasLeases()).thenReturn(hasLeases);
+ return blobMetadata;
+ }
+
+ private class TestHandler extends Handler {
+ TestHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void dispatchMessage(Message msg) {
+ // Ignore all messages
+ }
+ }
+
+ private class TestInjector extends Injector {
+ @Override
+ public Handler initializeMessageHandler() {
+ return mHandler;
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index d22502d..4532400 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -16,9 +16,6 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -27,17 +24,14 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
import android.app.Activity;
-import android.app.ActivityManager;
import android.app.PictureInPictureParams;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.EnterPipRequestedItem;
@@ -46,7 +40,6 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.view.IDisplayWindowListener;
-import android.view.WindowContainerTransaction;
import androidx.test.filters.MediumTest;
@@ -126,47 +119,6 @@
}
@Test
- public void testTaskTransaction() {
- removeGlobalMinSizeRestriction();
- final ActivityStack stack = new StackBuilder(mRootWindowContainer)
- .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
- final Task task = stack.getTopMostTask();
- WindowContainerTransaction t = new WindowContainerTransaction();
- Rect newBounds = new Rect(10, 10, 100, 100);
- t.setBounds(task.mRemoteToken, new Rect(10, 10, 100, 100));
- mService.applyContainerTransaction(t);
- assertEquals(newBounds, task.getBounds());
- }
-
- @Test
- public void testStackTransaction() {
- removeGlobalMinSizeRestriction();
- final ActivityStack stack = new StackBuilder(mRootWindowContainer)
- .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
- ActivityManager.StackInfo info =
- mService.getStackInfo(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
- WindowContainerTransaction t = new WindowContainerTransaction();
- assertEquals(stack.mRemoteToken, info.stackToken);
- Rect newBounds = new Rect(10, 10, 100, 100);
- t.setBounds(info.stackToken, new Rect(10, 10, 100, 100));
- mService.applyContainerTransaction(t);
- assertEquals(newBounds, stack.getBounds());
- }
-
- @Test
- public void testContainerChanges() {
- removeGlobalMinSizeRestriction();
- final ActivityStack stack = new StackBuilder(mRootWindowContainer)
- .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
- final Task task = stack.getTopMostTask();
- WindowContainerTransaction t = new WindowContainerTransaction();
- assertTrue(task.isFocusable());
- t.setFocusable(stack.mRemoteToken, false);
- mService.applyContainerTransaction(t);
- assertFalse(task.isFocusable());
- }
-
- @Test
public void testDisplayWindowListener() {
final ArrayList<Integer> added = new ArrayList<>();
final ArrayList<Integer> changed = new ArrayList<>();
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
new file mode 100644
index 0000000..618e608
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.wm;
+
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+
+import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
+import static com.android.server.wm.DisplayArea.Type.ANY;
+import static com.android.server.wm.DisplayArea.Type.BELOW_TASKS;
+import static com.android.server.wm.DisplayArea.Type.checkChild;
+import static com.android.server.wm.DisplayArea.Type.checkSiblings;
+import static com.android.server.wm.DisplayArea.Type.typeOf;
+import static com.android.server.wm.testing.Assert.assertThrows;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+@Presubmit
+public class DisplayAreaTest {
+
+ @Rule
+ public SystemServicesTestRule mWmsRule = new SystemServicesTestRule();
+
+ @Test
+ public void testDisplayArea_positionChanged_throwsIfIncompatibleChild() {
+ WindowManagerService wms = mWmsRule.getWindowManagerService();
+ DisplayArea<WindowContainer> parent = new DisplayArea<>(wms, BELOW_TASKS, "Parent");
+ DisplayArea<WindowContainer> child = new DisplayArea<>(wms, ANY, "Child");
+
+ assertThrows(IllegalStateException.class, () -> parent.addChild(child, 0));
+ }
+
+ @Test
+ public void testDisplayArea_positionChanged_throwsIfIncompatibleSibling() {
+ WindowManagerService wms = mWmsRule.getWindowManagerService();
+ DisplayArea<WindowContainer> parent = new SurfacelessDisplayArea<>(wms, ANY, "Parent");
+ DisplayArea<WindowContainer> child1 = new DisplayArea<>(wms, ANY, "Child1");
+ DisplayArea<WindowContainer> child2 = new DisplayArea<>(wms, ANY, "Child2");
+
+ parent.addChild(child1, 0);
+ assertThrows(IllegalStateException.class, () -> parent.addChild(child2, 0));
+ }
+
+ @Test
+ public void testType_typeOf() {
+ WindowManagerService wms = mWmsRule.getWindowManagerService();
+
+ assertEquals(ABOVE_TASKS, typeOf(new DisplayArea<>(wms, ABOVE_TASKS, "test")));
+ assertEquals(ANY, typeOf(new DisplayArea<>(wms, ANY, "test")));
+ assertEquals(BELOW_TASKS, typeOf(new DisplayArea<>(wms, BELOW_TASKS, "test")));
+
+ assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_APPLICATION_OVERLAY)));
+ assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_PRESENTATION)));
+ assertEquals(BELOW_TASKS, typeOf(createWindowToken(TYPE_WALLPAPER)));
+
+ assertThrows(IllegalArgumentException.class, () -> typeOf(mock(ActivityRecord.class)));
+ assertThrows(IllegalArgumentException.class, () -> typeOf(mock(WindowContainer.class)));
+ }
+
+ @Test
+ public void testType_checkSiblings() {
+ checkSiblings(BELOW_TASKS, BELOW_TASKS);
+ checkSiblings(BELOW_TASKS, ANY);
+ checkSiblings(BELOW_TASKS, ABOVE_TASKS);
+ checkSiblings(ANY, ABOVE_TASKS);
+ checkSiblings(ABOVE_TASKS, ABOVE_TASKS);
+
+ assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, BELOW_TASKS));
+ assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, ANY));
+ assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, ANY));
+ assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, BELOW_TASKS));
+ }
+
+ @Test
+ public void testType_checkChild() {
+ checkChild(ANY, ANY);
+ checkChild(ANY, ABOVE_TASKS);
+ checkChild(ANY, BELOW_TASKS);
+ checkChild(ABOVE_TASKS, ABOVE_TASKS);
+ checkChild(BELOW_TASKS, BELOW_TASKS);
+
+ assertThrows(IllegalStateException.class, () -> checkChild(ABOVE_TASKS, BELOW_TASKS));
+ assertThrows(IllegalStateException.class, () -> checkChild(ABOVE_TASKS, ANY));
+ assertThrows(IllegalStateException.class, () -> checkChild(BELOW_TASKS, ABOVE_TASKS));
+ assertThrows(IllegalStateException.class, () -> checkChild(BELOW_TASKS, ANY));
+ }
+
+ private WindowToken createWindowToken(int type) {
+ return new WindowToken(mWmsRule.getWindowManagerService(), new Binder(),
+ type, false /* persist */, null /* displayContent */,
+ false /* canManageTokens */);
+ }
+
+ private static class SurfacelessDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
+
+ SurfacelessDisplayArea(WindowManagerService wms, Type type, String name) {
+ super(wms, type, name);
+ }
+
+ @Override
+ SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+ return new MockSurfaceControlBuilder();
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 1637370..2ea00ce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -139,11 +139,11 @@
mAppWindow,
mChildAppWindowAbove,
mDockedDividerWindow,
+ mImeWindow,
+ mImeDialogWindow,
mStatusBarWindow,
mNotificationShadeWindow,
- mNavBarWindow,
- mImeWindow,
- mImeDialogWindow));
+ mNavBarWindow));
}
@Test
@@ -232,11 +232,11 @@
mChildAppWindowAbove,
mDockedDividerWindow,
voiceInteractionWindow,
+ mImeWindow,
+ mImeDialogWindow,
mStatusBarWindow,
mNotificationShadeWindow,
- mNavBarWindow,
- mImeWindow,
- mImeDialogWindow));
+ mNavBarWindow));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 9e80cf2..078347e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -16,45 +16,50 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
-import android.graphics.Point;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityManager.StackInfo;
+import android.content.res.Configuration;
import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
import android.os.Binder;
import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.view.Display;
import android.view.ITaskOrganizer;
+import android.view.IWindowContainer;
import android.view.SurfaceControl;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import android.view.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.List;
+
/**
- * Test class for {@link TaskOrganizer}.
+ * Test class for {@link ITaskOrganizer} and {@link android.app.ITaskOrganizerController}.
*
* Build/Install/Run:
* atest WmTests:TaskOrganizerTests
@@ -67,7 +72,8 @@
final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
when(organizer.asBinder()).thenReturn(new Binder());
- mWm.mAtmService.registerTaskOrganizer(organizer, windowingMode);
+ mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
+ organizer, windowingMode);
return organizer;
}
@@ -83,7 +89,7 @@
final ITaskOrganizer organizer = registerMockOrganizer();
task.setTaskOrganizer(organizer);
- verify(organizer).taskAppeared(any(), any());
+ verify(organizer).taskAppeared(any());
task.removeImmediately();
verify(organizer).taskVanished(any());
@@ -97,10 +103,10 @@
final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
task.setTaskOrganizer(organizer);
- verify(organizer).taskAppeared(any(), any());
+ verify(organizer).taskAppeared(any());
task.setTaskOrganizer(organizer2);
verify(organizer).taskVanished(any());
- verify(organizer2).taskAppeared(any(), any());
+ verify(organizer2).taskAppeared(any());
}
@Test
@@ -111,10 +117,10 @@
final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
- verify(organizer).taskAppeared(any(), any());
+ verify(organizer).taskAppeared(any());
stack.setWindowingMode(WINDOWING_MODE_PINNED);
verify(organizer).taskVanished(any());
- verify(organizer2).taskAppeared(any(), any());
+ verify(organizer2).taskAppeared(any());
}
@Test
@@ -124,7 +130,7 @@
final ITaskOrganizer organizer = registerMockOrganizer();
stack.setTaskOrganizer(organizer);
- verify(organizer).taskAppeared(any(), any());
+ verify(organizer).taskAppeared(any());
assertTrue(stack.isControlledByTaskOrganizer());
stack.setTaskOrganizer(null);
@@ -140,9 +146,176 @@
final Task task = createTaskInStack(stack, 0 /* userId */);
final Task task2 = createTaskInStack(stack, 0 /* userId */);
stack.setWindowingMode(WINDOWING_MODE_PINNED);
- verify(organizer, times(1)).taskAppeared(any(), any());
+ verify(organizer, times(1)).taskAppeared(any());
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
verify(organizer, times(1)).taskVanished(any());
}
+
+ @Test
+ public void testTaskTransaction() {
+ removeGlobalMinSizeRestriction();
+ final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+ final Task task = stack.getTopMostTask();
+ WindowContainerTransaction t = new WindowContainerTransaction();
+ Rect newBounds = new Rect(10, 10, 100, 100);
+ t.setBounds(task.mRemoteToken, new Rect(10, 10, 100, 100));
+ mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t);
+ assertEquals(newBounds, task.getBounds());
+ }
+
+ @Test
+ public void testStackTransaction() {
+ removeGlobalMinSizeRestriction();
+ final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+ StackInfo info =
+ mWm.mAtmService.getStackInfo(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+ WindowContainerTransaction t = new WindowContainerTransaction();
+ assertEquals(stack.mRemoteToken, info.stackToken);
+ Rect newBounds = new Rect(10, 10, 100, 100);
+ t.setBounds(info.stackToken, new Rect(10, 10, 100, 100));
+ mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t);
+ assertEquals(newBounds, stack.getBounds());
+ }
+
+ @Test
+ public void testContainerChanges() {
+ removeGlobalMinSizeRestriction();
+ final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+ final Task task = stack.getTopMostTask();
+ WindowContainerTransaction t = new WindowContainerTransaction();
+ assertTrue(task.isFocusable());
+ t.setFocusable(stack.mRemoteToken, false);
+ mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t);
+ assertFalse(task.isFocusable());
+ }
+
+ @Test
+ public void testCreateDeleteRootTasks() {
+ RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ Display.DEFAULT_DISPLAY,
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+ assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+ info1.configuration.windowConfiguration.getWindowingMode());
+ assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
+
+ RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ Display.DEFAULT_DISPLAY,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+ info2.configuration.windowConfiguration.getWindowingMode());
+ assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType);
+
+ DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
+ List<TaskTile> infos = getTaskTiles(dc);
+ assertEquals(2, infos.size());
+
+ assertTrue(mWm.mAtmService.mTaskOrganizerController.deleteRootTask(info1.token));
+ infos = getTaskTiles(dc);
+ assertEquals(1, infos.size());
+ assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, infos.get(0).getWindowingMode());
+ }
+
+ @Test
+ public void testTileAddRemoveChild() {
+ RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+
+ final ActivityStack stack = createTaskStackOnDisplay(
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode());
+ TaskTile tile1 = TaskTile.forToken(info1.token.asBinder());
+ tile1.addChild(stack, 0 /* index */);
+ assertEquals(info1.configuration.windowConfiguration.getWindowingMode(),
+ stack.getWindowingMode());
+
+ // Info should reflect new membership
+ List<TaskTile> tiles = getTaskTiles(mDisplayContent);
+ info1 = new RunningTaskInfo();
+ tiles.get(0).fillTaskInfo(info1);
+ assertEquals(ACTIVITY_TYPE_STANDARD, info1.topActivityType);
+
+ // Children inherit configuration
+ Rect newSize = new Rect(10, 10, 300, 300);
+ Configuration c = new Configuration(tile1.getRequestedOverrideConfiguration());
+ c.windowConfiguration.setBounds(newSize);
+ tile1.onRequestedOverrideConfigurationChanged(c);
+ assertEquals(newSize, stack.getBounds());
+
+ tile1.removeChild(stack);
+ assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode());
+ info1 = new RunningTaskInfo();
+ tiles = getTaskTiles(mDisplayContent);
+ tiles.get(0).fillTaskInfo(info1);
+ assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
+ }
+
+ @Test
+ public void testTaskInfoCallback() {
+ final ArrayList<RunningTaskInfo> lastReportedTiles = new ArrayList<>();
+ final boolean[] called = {false};
+ ITaskOrganizer listener = new ITaskOrganizer.Stub() {
+ @Override
+ public void taskAppeared(RunningTaskInfo taskInfo) { }
+
+ @Override
+ public void taskVanished(IWindowContainer container) { }
+
+ @Override
+ public void transactionReady(int id, SurfaceControl.Transaction t) { }
+
+ @Override
+ public void onTaskInfoChanged(RunningTaskInfo info) throws RemoteException {
+ lastReportedTiles.add(info);
+ called[0] = true;
+ }
+ };
+ mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+ mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ lastReportedTiles.clear();
+ called[0] = false;
+
+ final ActivityStack stack = createTaskStackOnDisplay(
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+ TaskTile tile1 = TaskTile.forToken(info1.token.asBinder());
+ tile1.addChild(stack, 0 /* index */);
+ assertTrue(called[0]);
+ assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
+
+ lastReportedTiles.clear();
+ called[0] = false;
+ final ActivityStack stack2 = createTaskStackOnDisplay(
+ WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent);
+ tile1.addChild(stack2, 0 /* index */);
+ assertTrue(called[0]);
+ assertEquals(ACTIVITY_TYPE_HOME, lastReportedTiles.get(0).topActivityType);
+
+ lastReportedTiles.clear();
+ called[0] = false;
+ mDisplayContent.positionStackAtTop(stack, false /* includingParents */);
+ assertTrue(called[0]);
+ assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
+
+ lastReportedTiles.clear();
+ called[0] = false;
+ tile1.removeAllChildren();
+ assertTrue(called[0]);
+ assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType);
+ }
+
+ private List<TaskTile> getTaskTiles(DisplayContent dc) {
+ ArrayList<TaskTile> out = new ArrayList<>();
+ for (int i = dc.getStackCount() - 1; i >= 0; --i) {
+ final Task t = dc.getStackAt(i);
+ if (t instanceof TaskTile) {
+ out.add((TaskTile) t);
+ }
+ }
+ return out;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 8e362ae..1ca2e318 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -40,6 +40,7 @@
import static org.mockito.Mockito.mock;
import android.content.Context;
+import android.content.Intent;
import android.util.Log;
import android.view.Display;
import android.view.DisplayInfo;
@@ -340,6 +341,7 @@
.setWindowingMode(windowingMode)
.setActivityType(activityType)
.setCreateActivity(false)
+ .setIntent(new Intent())
.build();
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/testing/Assert.java b/services/tests/wmtests/src/com/android/server/wm/testing/Assert.java
new file mode 100644
index 0000000..1e98277
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/testing/Assert.java
@@ -0,0 +1,48 @@
+/*
+ * 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.wm.testing;
+
+/**
+ * Assertions for WM tests.
+ */
+public class Assert {
+
+ /**
+ * Runs {@code r} and asserts that an exception of type {@code expectedThrowable} is thrown.
+ * @param expectedThrowable the type of throwable that is expected to be thrown
+ * @param r the {@link Runnable} which is run and expected to throw.
+ * @throws AssertionError if {@code r} does not throw, or throws a runnable that is not an
+ * instance of {@code expectedThrowable}.
+ */
+ // TODO: remove once Android migrates to JUnit 4.13, which provides assertThrows
+ public static void assertThrows(Class<? extends Throwable> expectedThrowable, Runnable r) {
+ try {
+ r.run();
+ } catch (Throwable t) {
+ if (expectedThrowable.isInstance(t)) {
+ return;
+ } else if (t instanceof Exception) {
+ throw new AssertionError("Expected " + expectedThrowable
+ + ", but got " + t.getClass(), t);
+ } else {
+ // Re-throw Errors and other non-Exception throwables.
+ throw t;
+ }
+ }
+ throw new AssertionError("Expected " + expectedThrowable + ", but nothing was thrown");
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/testing/AssertTest.java b/services/tests/wmtests/src/com/android/server/wm/testing/AssertTest.java
new file mode 100644
index 0000000..df12761
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/testing/AssertTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.testing;
+
+import static com.android.server.wm.testing.Assert.assertThrows;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class AssertTest {
+
+ @Rule
+ public ExpectedException mExpectedException = ExpectedException.none();
+
+ @Test
+ public void assertThrows_runsRunnable() {
+ boolean[] ran = new boolean[] { false };
+ assertThrows(TestException.class, () -> {
+ ran[0] = true;
+ throw new TestException();
+ });
+ assertTrue(ran[0]);
+ }
+
+ @Test
+ public void assertThrows_failsIfNothingThrown() {
+ mExpectedException.expect(AssertionError.class);
+ assertThrows(TestException.class, () -> {
+ });
+ }
+
+ @Test
+ public void assertThrows_failsIfWrongExceptionThrown() {
+ mExpectedException.expect(AssertionError.class);
+ assertThrows(TestException.class, () -> {
+ throw new RuntimeException();
+ });
+ }
+
+ @Test
+ public void assertThrows_succeedsIfGivenExceptionThrown() {
+ assertThrows(TestException.class, () -> {
+ throw new TestException();
+ });
+ }
+
+ @Test
+ public void assertThrows_succeedsIfSubExceptionThrown() {
+ assertThrows(RuntimeException.class, () -> {
+ throw new TestException();
+ });
+ }
+
+ @Test
+ public void assertThrows_rethrowsUnexpectedErrors() {
+ mExpectedException.expect(TestError.class);
+ assertThrows(TestException.class, () -> {
+ throw new TestError();
+ });
+ }
+
+ static class TestException extends RuntimeException {
+ }
+
+ static class TestError extends Error {
+ }
+
+}
diff --git a/telephony/java/android/telephony/BarringInfo.java b/telephony/java/android/telephony/BarringInfo.java
index 5419c3c..9baa66f 100644
--- a/telephony/java/android/telephony/BarringInfo.java
+++ b/telephony/java/android/telephony/BarringInfo.java
@@ -238,6 +238,12 @@
}
}
+ private static final BarringServiceInfo BARRING_SERVICE_INFO_UNKNOWN =
+ new BarringServiceInfo(BarringServiceInfo.BARRING_TYPE_UNKNOWN);
+
+ private static final BarringServiceInfo BARRING_SERVICE_INFO_UNBARRED =
+ new BarringServiceInfo(BarringServiceInfo.BARRING_TYPE_NONE);
+
private CellIdentity mCellIdentity;
// A SparseArray potentially mapping each BarringService type to a BarringServiceInfo config
@@ -298,17 +304,6 @@
}
/**
- * Return whether a service is currently barred based on the BarringInfo
- *
- * @param service the service to be checked.
- * @return true if the service is currently being barred, otherwise false
- */
- public boolean isServiceBarred(@BarringServiceType int service) {
- BarringServiceInfo bsi = mBarringServiceInfos.get(service);
- return bsi != null && (bsi.isBarred());
- }
-
- /**
* Get the BarringServiceInfo for a specified service.
*
* @return a BarringServiceInfo struct describing the current barring status for a service
@@ -319,9 +314,8 @@
// type as UNKNOWN; if the modem reports barring info but doesn't report for a particular
// service then we can safely assume that the service isn't barred (for instance because
// that particular service isn't applicable to the current RAN).
- return (bsi != null) ? bsi : new BarringServiceInfo(
- mBarringServiceInfos.size() > 0 ? BarringServiceInfo.BARRING_TYPE_NONE :
- BarringServiceInfo.BARRING_TYPE_UNKNOWN);
+ return (bsi != null) ? bsi : mBarringServiceInfos.size() > 0
+ ? BARRING_SERVICE_INFO_UNBARRED : BARRING_SERVICE_INFO_UNKNOWN;
}
/** @hide */
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 5b09cd9..a5a1ebc 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1925,6 +1925,8 @@
/**
* Get the network registration state for the transport type and network domain.
+ * If multiple domains are in the input bitmask, only the first one from
+ * networkRegistrationInfo.getDomain() will be returned.
*
* @param domain The network {@link NetworkRegistrationInfo.Domain domain}
* @param transportType The transport type
@@ -2072,11 +2074,18 @@
public boolean isIwlanPreferred() {
return mIsIwlanPreferred;
}
- /**
- * @return {@code true}Returns True whenever the modem is searching for service.
- * To check both CS and PS domain
- */
+ /**
+ * This indicates whether the device is searching for service.
+ *
+ * This API reports the modem searching status for
+ * {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN} (cellular) service in either
+ * {@link NetworkRegistrationInfo#DOMAIN_CS} or {@link NetworkRegistrationInfo#DOMAIN_PS}.
+ * This API will not report searching status for
+ * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}.
+ *
+ * @return {@code true} whenever the modem is searching for service.
+ */
public boolean isSearching() {
NetworkRegistrationInfo psRegState = getNetworkRegistrationInfo(
NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
diff --git a/tests/PlatformCompatGating/Android.bp b/tests/PlatformCompatGating/Android.bp
index 609896e..5e9ef8e 100644
--- a/tests/PlatformCompatGating/Android.bp
+++ b/tests/PlatformCompatGating/Android.bp
@@ -18,6 +18,7 @@
name: "PlatformCompatGating",
// Only compile source java files in this apk.
srcs: ["src/**/*.java"],
+ certificate: "platform",
libs: [
"android.test.runner",
"android.test.base",
diff --git a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
index c00aa2a..932ec64 100644
--- a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
+++ b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
@@ -16,9 +16,7 @@
package android.compat.testing;
-import android.Manifest;
import android.app.Instrumentation;
-import android.app.UiAutomation;
import android.compat.Compatibility;
import android.compat.Compatibility.ChangeConfig;
import android.content.Context;
@@ -85,16 +83,12 @@
@Override
public void evaluate() throws Throwable {
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- UiAutomation uiAutomation = instrumentation.getUiAutomation();
String packageName = instrumentation.getTargetContext().getPackageName();
IPlatformCompat platformCompat = IPlatformCompat.Stub
.asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
if (platformCompat == null) {
throw new IllegalStateException("Could not get IPlatformCompat service!");
}
- uiAutomation.adoptShellPermissionIdentity(
- Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
- Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG);
Compatibility.setOverrides(mConfig);
try {
platformCompat.setOverridesForTest(new CompatibilityChangeConfig(mConfig),
@@ -107,7 +101,6 @@
} catch (RemoteException e) {
throw new RuntimeException("Could not call IPlatformCompat binder method!", e);
} finally {
- uiAutomation.dropShellPermissionIdentity();
Compatibility.clearOverrides();
}
}
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
index bc8d3c3..3fb0050 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
@@ -25,6 +25,7 @@
import android.content.Intent;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.RemoteException;
import android.view.ITaskOrganizer;
import android.view.IWindowContainer;
import android.view.SurfaceControl;
@@ -47,13 +48,16 @@
class Organizer extends ITaskOrganizer.Stub {
@Override
- public void taskAppeared(IWindowContainer wc, ActivityManager.RunningTaskInfo ti) {
- mView.reparentTask(wc);
+ public void taskAppeared(ActivityManager.RunningTaskInfo ti) {
+ mView.reparentTask(ti.token);
}
public void taskVanished(IWindowContainer wc) {
}
public void transactionReady(int id, SurfaceControl.Transaction t) {
}
+ @Override
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
+ }
}
Organizer mOrganizer = new Organizer();
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
index fc1be28..8f3cb34 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
@@ -21,18 +21,14 @@
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.Service;
-import android.app.WindowConfiguration;
-import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.ITaskOrganizer;
import android.view.IWindowContainer;
-import android.view.WindowContainerTransaction;
import android.view.SurfaceControl;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
import android.view.ViewGroup;
+import android.view.WindowContainerTransaction;
import android.view.WindowManager;
import android.widget.FrameLayout;
@@ -43,13 +39,13 @@
TaskView mTaskView;
class Organizer extends ITaskOrganizer.Stub {
- public void taskAppeared(IWindowContainer wc, ActivityManager.RunningTaskInfo ti) {
- mTaskView.reparentTask(wc);
+ public void taskAppeared(ActivityManager.RunningTaskInfo ti) {
+ mTaskView.reparentTask(ti.token);
final WindowContainerTransaction wct = new WindowContainerTransaction();
- wct.scheduleFinishEnterPip(wc, new Rect(0, 0, PIP_WIDTH, PIP_HEIGHT));
+ wct.scheduleFinishEnterPip(ti.token, new Rect(0, 0, PIP_WIDTH, PIP_HEIGHT));
try {
- ActivityTaskManager.getService().applyContainerTransaction(wct);
+ ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct);
} catch (Exception e) {
}
}
@@ -57,6 +53,8 @@
}
public void transactionReady(int id, SurfaceControl.Transaction t) {
}
+ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
+ }
}
Organizer mOrganizer = new Organizer();
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java
index ff73340..9f32bb8 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java
@@ -44,7 +44,7 @@
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
- ActivityTaskManager.getService().registerTaskOrganizer(mTaskOrganizer,
+ ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer(mTaskOrganizer,
mWindowingMode);
} catch (Exception e) {
}
diff --git a/tests/net/TEST_MAPPING b/tests/net/TEST_MAPPING
index a7853b6..005cbe9 100644
--- a/tests/net/TEST_MAPPING
+++ b/tests/net/TEST_MAPPING
@@ -1,7 +1,12 @@
{
- "postsubmit": [
+ "presubmit": [
{
"name": "FrameworksNetIntegrationTests"
}
+ ],
+ "postsubmit": [
+ {
+ "name": "FrameworksNetDeflakeTest"
+ }
]
}
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/NetworkScoreTest.kt b/tests/net/common/java/android/net/NetworkScoreTest.kt
index 30836b7..a63d58d 100644
--- a/tests/net/common/java/android/net/NetworkScoreTest.kt
+++ b/tests/net/common/java/android/net/NetworkScoreTest.kt
@@ -16,7 +16,7 @@
package android.net
-import android.os.Parcelable
+import android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN
import androidx.test.filters.SmallTest
import androidx.test.runner.AndroidJUnit4
import com.android.testutils.assertParcelSane
@@ -28,77 +28,49 @@
import org.junit.runner.RunWith
private const val TEST_SCORE = 80
-private const val KEY_DEFAULT_CAPABILITIES = "DEFAULT_CAPABILITIES"
@RunWith(AndroidJUnit4::class)
@SmallTest
class NetworkScoreTest {
@Test
fun testParcelNetworkScore() {
- val networkScore = NetworkScore()
val defaultCap = NetworkCapabilities()
- networkScore.putExtension(KEY_DEFAULT_CAPABILITIES, defaultCap)
- assertEquals(defaultCap, networkScore.getExtension(KEY_DEFAULT_CAPABILITIES))
- networkScore.putIntExtension(NetworkScore.LEGACY_SCORE, TEST_SCORE)
- assertEquals(TEST_SCORE, networkScore.getIntExtension(NetworkScore.LEGACY_SCORE))
- assertParcelSane(networkScore, 1)
- }
+ val builder = NetworkScore.Builder().setLegacyScore(TEST_SCORE)
+ assertEquals(TEST_SCORE, builder.build().getLegacyScore())
+ assertParcelSane(builder.build(), 7)
- @Test
- fun testNullKeyAndValue() {
- val networkScore = NetworkScore()
- val defaultCap = NetworkCapabilities()
- networkScore.putIntExtension(null, TEST_SCORE)
- assertEquals(TEST_SCORE, networkScore.getIntExtension(null))
- networkScore.putExtension(null, defaultCap)
- assertEquals(defaultCap, networkScore.getExtension(null))
- networkScore.putExtension(null, null)
- val result: Parcelable? = networkScore.getExtension(null)
- assertEquals(null, result)
- }
+ builder.addPolicy(NetworkScore.POLICY_IGNORE_ON_WIFI)
+ .addPolicy(NetworkScore.POLICY_DEFAULT_SUBSCRIPTION)
+ .setLinkLayerMetrics(NetworkScore.Metrics(44 /* latency */,
+ 380 /* downlinkBandwidth */, BANDWIDTH_UNKNOWN /* uplinkBandwidth */))
+ .setEndToEndMetrics(NetworkScore.Metrics(11 /* latency */,
+ BANDWIDTH_UNKNOWN /* downlinkBandwidth */, 100_000 /* uplinkBandwidth */))
+ .setRange(NetworkScore.RANGE_MEDIUM)
+ assertParcelSane(builder.build(), 7)
+ builder.clearPolicy(NetworkScore.POLICY_IGNORE_ON_WIFI)
+ val ns = builder.build()
+ assertParcelSane(ns, 7)
+ assertFalse(ns.hasPolicy(NetworkScore.POLICY_IGNORE_ON_WIFI))
+ assertTrue(ns.hasPolicy(NetworkScore.POLICY_DEFAULT_SUBSCRIPTION))
- @Test
- fun testRemoveExtension() {
- val networkScore = NetworkScore()
- val defaultCap = NetworkCapabilities()
- networkScore.putExtension(KEY_DEFAULT_CAPABILITIES, defaultCap)
- networkScore.putIntExtension(NetworkScore.LEGACY_SCORE, TEST_SCORE)
- assertEquals(defaultCap, networkScore.getExtension(KEY_DEFAULT_CAPABILITIES))
- assertEquals(TEST_SCORE, networkScore.getIntExtension(NetworkScore.LEGACY_SCORE))
- networkScore.removeExtension(KEY_DEFAULT_CAPABILITIES)
- networkScore.removeExtension(NetworkScore.LEGACY_SCORE)
- val result: Parcelable? = networkScore.getExtension(KEY_DEFAULT_CAPABILITIES)
- assertEquals(null, result)
- assertEquals(0, networkScore.getIntExtension(NetworkScore.LEGACY_SCORE))
+ val exitingNs = ns.withExiting(true)
+ assertNotEquals(ns.isExiting, exitingNs.isExiting)
+ assertNotEquals(ns, exitingNs)
+ assertParcelSane(exitingNs, 7)
}
@Test
fun testEqualsNetworkScore() {
- val ns1 = NetworkScore()
- val ns2 = NetworkScore()
- assertTrue(ns1.equals(ns2))
- assertEquals(ns1.hashCode(), ns2.hashCode())
+ val builder1 = NetworkScore.Builder()
+ val builder2 = NetworkScore.Builder()
+ assertTrue(builder1.build().equals(builder2.build()))
+ assertEquals(builder1.build().hashCode(), builder2.build().hashCode())
- ns1.putIntExtension(NetworkScore.LEGACY_SCORE, TEST_SCORE)
- assertFalse(ns1.equals(ns2))
- assertNotEquals(ns1.hashCode(), ns2.hashCode())
- ns2.putIntExtension(NetworkScore.LEGACY_SCORE, TEST_SCORE)
- assertTrue(ns1.equals(ns2))
- assertEquals(ns1.hashCode(), ns2.hashCode())
-
- val defaultCap = NetworkCapabilities()
- ns1.putExtension(KEY_DEFAULT_CAPABILITIES, defaultCap)
- assertFalse(ns1.equals(ns2))
- assertNotEquals(ns1.hashCode(), ns2.hashCode())
- ns2.putExtension(KEY_DEFAULT_CAPABILITIES, defaultCap)
- assertTrue(ns1.equals(ns2))
- assertEquals(ns1.hashCode(), ns2.hashCode())
-
- ns1.putIntExtension(null, 10)
- assertFalse(ns1.equals(ns2))
- assertNotEquals(ns1.hashCode(), ns2.hashCode())
- ns2.putIntExtension(null, 10)
- assertTrue(ns1.equals(ns2))
- assertEquals(ns1.hashCode(), ns2.hashCode())
+ builder1.setLegacyScore(TEST_SCORE)
+ assertFalse(builder1.build().equals(builder2.build()))
+ assertNotEquals(builder1.hashCode(), builder2.hashCode())
+ builder2.setLegacyScore(TEST_SCORE)
+ assertTrue(builder1.build().equals(builder2.build()))
+ assertEquals(builder1.build().hashCode(), builder2.build().hashCode())
}
}
diff --git a/tests/net/deflake/Android.bp b/tests/net/deflake/Android.bp
index 1c48c74..b1b0171 100644
--- a/tests/net/deflake/Android.bp
+++ b/tests/net/deflake/Android.bp
@@ -26,4 +26,5 @@
"net-host-tests-utils",
],
data: [":FrameworksNetTests"],
-}
\ No newline at end of file
+ test_suites: ["device-tests"],
+}
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index a35fb40..7ae9e95 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -40,6 +40,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkProvider;
+import android.net.NetworkScore;
import android.net.NetworkSpecifier;
import android.net.SocketKeepalive;
import android.net.UidRange;
@@ -155,9 +156,13 @@
}
}
+ private NetworkScore makeNetworkScore(final int legacyScore) {
+ return new NetworkScore.Builder().setLegacyScore(legacyScore).build();
+ }
+
public void adjustScore(int change) {
mScore += change;
- mNetworkAgent.sendNetworkScore(mScore);
+ mNetworkAgent.sendNetworkScore(makeNetworkScore(mScore));
}
public int getScore() {
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index e863266..25e9057 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -353,8 +353,7 @@
NetworkCapabilities caps = new NetworkCapabilities();
caps.addCapability(0);
caps.addTransportType(transport);
- NetworkScore ns = new NetworkScore();
- ns.putIntExtension(NetworkScore.LEGACY_SCORE, 50);
+ NetworkScore ns = new NetworkScore.Builder().setLegacyScore(50).build();
NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null,
caps, ns, mCtx, null, null /* config */, mConnService, mNetd, mDnsResolver, mNMS,
NetworkProvider.ID_NONE);
diff --git a/tools/hiddenapi/Android.bp b/tools/hiddenapi/Android.bp
new file mode 100644
index 0000000..e0eb06cb
--- /dev/null
+++ b/tools/hiddenapi/Android.bp
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+python_binary_host {
+ name: "merge_csv",
+ main: "merge_csv.py",
+ srcs: ["merge_csv.py"],
+ version: {
+ py2: {
+ enabled: false,
+ },
+ py3: {
+ enabled: true,
+ embedded_launcher: true
+ },
+ },
+}
diff --git a/tools/hiddenapi/merge_csv.py b/tools/hiddenapi/merge_csv.py
index 48c0755..9661927 100755
--- a/tools/hiddenapi/merge_csv.py
+++ b/tools/hiddenapi/merge_csv.py
@@ -21,20 +21,19 @@
import sys
csv_readers = [
- csv.DictReader(open(csv_file, 'rb'), delimiter=',', quotechar='|')
+ csv.DictReader(open(csv_file, 'r'), delimiter=',', quotechar='|')
for csv_file in sys.argv[1:]
]
# Build union of all columns from source files:
headers = set()
for reader in csv_readers:
- headers = headers.union(reader.fieldnames)
+ headers = headers.union(reader.fieldnames)
# Concatenate all files to output:
-out = csv.DictWriter(sys.stdout, delimiter=',', quotechar='|', fieldnames = sorted(headers))
+out = csv.DictWriter(sys.stdout, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
+ dialect='unix', fieldnames=sorted(headers))
out.writeheader()
for reader in csv_readers:
- for row in reader:
- out.writerow(row)
-
-
+ for row in reader:
+ out.writerow(row)
diff --git a/tools/stats_log_api_gen/java_writer_q.cpp b/tools/stats_log_api_gen/java_writer_q.cpp
index 1259a68..a68c3a2 100644
--- a/tools/stats_log_api_gen/java_writer_q.cpp
+++ b/tools/stats_log_api_gen/java_writer_q.cpp
@@ -577,6 +577,50 @@
}
}
+// This method is called in main.cpp to generate StatsLog for modules that's compatible with
+// Q at compile-time.
+int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms,
+ const AtomDecl &attributionDecl, const string& moduleName,
+ const string& javaClass, const string& javaPackage,
+ const bool supportWorkSource) {
+ // Print prelude
+ fprintf(out, "// This file is autogenerated\n");
+ fprintf(out, "\n");
+ fprintf(out, "package %s;\n", javaPackage.c_str());
+ fprintf(out, "\n");
+ fprintf(out, "import static java.nio.charset.StandardCharsets.UTF_8;\n");
+ fprintf(out, "\n");
+ fprintf(out, "import android.util.StatsLog;\n");
+ fprintf(out, "import android.os.SystemClock;\n");
+ fprintf(out, "\n");
+ fprintf(out, "\n");
+ fprintf(out, "/**\n");
+ fprintf(out, " * Utility class for logging statistics events.\n");
+ fprintf(out, " */\n");
+ fprintf(out, "public class %s {\n", javaClass.c_str());
+
+ write_java_q_logging_constants(out, " ");
+
+ write_java_atom_codes(out, atoms, moduleName);
+
+ write_java_enum_values(out, atoms, moduleName);
+
+ int errors = 0;
+ // Print write methods
+ fprintf(out, " // Write methods\n");
+ errors += write_java_methods_q_schema(out, atoms.signatures_to_modules, attributionDecl,
+ moduleName, " ");
+ errors += write_java_non_chained_methods(out, atoms.non_chained_signatures_to_modules,
+ moduleName);
+ if (supportWorkSource) {
+ errors += write_java_work_source_methods(out, atoms.signatures_to_modules, moduleName);
+ }
+
+ fprintf(out, "}\n");
+
+ return errors;
+}
+
#if defined(STATS_SCHEMA_LEGACY)
static void write_java_method(
FILE* out,
@@ -639,48 +683,6 @@
return 0;
}
-
-int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms,
- const AtomDecl &attributionDecl, const string& moduleName,
- const string& javaClass, const string& javaPackage,
- const bool supportWorkSource) {
- // Print prelude
- fprintf(out, "// This file is autogenerated\n");
- fprintf(out, "\n");
- fprintf(out, "package %s;\n", javaPackage.c_str());
- fprintf(out, "\n");
- fprintf(out, "import static java.nio.charset.StandardCharsets.UTF_8;\n");
- fprintf(out, "\n");
- fprintf(out, "import android.util.StatsLog;\n");
- fprintf(out, "import android.os.SystemClock;\n");
- fprintf(out, "\n");
- fprintf(out, "\n");
- fprintf(out, "/**\n");
- fprintf(out, " * Utility class for logging statistics events.\n");
- fprintf(out, " */\n");
- fprintf(out, "public class %s {\n", javaClass.c_str());
-
- write_java_q_logging_constants(out, " ");
-
- write_java_atom_codes(out, atoms, moduleName);
-
- write_java_enum_values(out, atoms, moduleName);
-
- int errors = 0;
- // Print write methods
- fprintf(out, " // Write methods\n");
- errors += write_java_methods_q_schema(out, atoms.signatures_to_modules, attributionDecl,
- moduleName, " ");
- errors += write_java_non_chained_methods(out, atoms.non_chained_signatures_to_modules,
- moduleName);
- if (supportWorkSource) {
- errors += write_java_work_source_methods(out, atoms.signatures_to_modules, moduleName);
- }
-
- fprintf(out, "}\n");
-
- return errors;
-}
#endif
} // namespace stats_log_api_gen
diff --git a/tools/stats_log_api_gen/java_writer_q.h b/tools/stats_log_api_gen/java_writer_q.h
index 36df1d8..7d734df 100644
--- a/tools/stats_log_api_gen/java_writer_q.h
+++ b/tools/stats_log_api_gen/java_writer_q.h
@@ -45,13 +45,13 @@
const int requiredHelpers,
const string& indent);
-#if defined(STATS_SCHEMA_LEGACY)
-int write_stats_log_java_q(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
- const bool supportWorkSource);
-
int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms,
const AtomDecl &attributionDecl, const string& moduleName, const string& javaClass,
const string& javaPackage, const bool supportWorkSource);
+
+#if defined(STATS_SCHEMA_LEGACY)
+int write_stats_log_java_q(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
+ const bool supportWorkSource);
#endif
} // namespace stats_log_api_gen
} // namespace android
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 6089532..ddbf22c 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -511,8 +511,10 @@
fprintf(stderr, " --javaClass CLASS the class name of the java class.\n");
fprintf(stderr, " Optional for Java with module.\n");
fprintf(stderr, " Default is \"StatsLogInternal\"\n");
- fprintf(stderr, " --supportQ Include support for Android Q.\n");
+ fprintf(stderr, " --supportQ Include runtime support for Android Q.\n");
fprintf(stderr, " --worksource Include support for logging WorkSource objects.\n");
+ fprintf(stderr, " --compileQ Include compile-time support for Android Q "
+ "(Java only).\n");
}
/**
@@ -536,6 +538,7 @@
string javaClass = DEFAULT_JAVA_CLASS;
bool supportQ = false;
bool supportWorkSource = false;
+ bool compileQ = false;
int index = 1;
while (index < argc) {
@@ -630,6 +633,8 @@
supportQ = true;
} else if (0 == strcmp("--worksource", argv[index])) {
supportWorkSource = true;
+ } else if (0 == strcmp("--compileQ", argv[index])) {
+ compileQ = true;
}
index++;
@@ -645,12 +650,18 @@
return 1;
}
- if (DEFAULT_MODULE_NAME == moduleName && supportQ) {
+ if (DEFAULT_MODULE_NAME == moduleName && (supportQ || compileQ)) {
// Support for Q schema is not needed for default module.
fprintf(stderr, "%s cannot support Q schema\n", moduleName.c_str());
return 1;
}
+ if (supportQ && compileQ) {
+ // Runtime Q support is redundant if compile-time Q support is required.
+ fprintf(stderr, "Cannot specify compileQ and supportQ simultaneously.\n");
+ return 1;
+ }
+
// Collate the parameters
Atoms atoms;
int errorCount = collate_atoms(Atom::descriptor(), &atoms);
@@ -748,9 +759,15 @@
javaClass = "StatsLogInternal";
javaPackage = "android.util";
}
- errorCount = android::stats_log_api_gen::write_stats_log_java(
- out, atoms, attributionDecl, moduleName, javaClass, javaPackage, supportQ,
- supportWorkSource);
+ if (compileQ) {
+ errorCount = android::stats_log_api_gen::write_stats_log_java_q_for_module(
+ out, atoms, attributionDecl, moduleName, javaClass, javaPackage,
+ supportWorkSource);
+ } else {
+ errorCount = android::stats_log_api_gen::write_stats_log_java(
+ out, atoms, attributionDecl, moduleName, javaClass, javaPackage, supportQ,
+ supportWorkSource);
+ }
#endif
fclose(out);
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 0ef224a..8d95cb0 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -373,6 +373,7 @@
* ECDHE_ECDSA
* ECDHE_RSA
* </pre>
+ * @hide
*/
public static class SuiteBCipher {
private SuiteBCipher() { }
@@ -715,8 +716,8 @@
public BitSet allowedGroupManagementCiphers;
/**
* The set of SuiteB ciphers supported by this configuration.
- * To be used for WPA3-Enterprise mode.
- * See {@link SuiteBCipher} for descriptions of the values.
+ * To be used for WPA3-Enterprise mode. Set automatically by the framework based on the
+ * certificate type that is used in this configuration.
*/
@NonNull
public BitSet allowedSuiteBCiphers;
@@ -1929,54 +1930,38 @@
private NetworkSelectionStatus mNetworkSelectionStatus = new NetworkSelectionStatus();
/**
- * @hide
* This class is intended to store extra failure reason information for the most recent
* connection attempt, so that it may be surfaced to the settings UI
+ * @hide
*/
- @SystemApi
+ // TODO(b/148626966): called by SUW via reflection, remove once SUW is updated
public static class RecentFailure {
private RecentFailure() {}
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {NONE, STATUS_AP_UNABLE_TO_HANDLE_NEW_STA})
- public @interface AssociationStatus {}
-
- /**
- * No recent failure, or no specific reason given for the recent connection failure
- */
- public static final int NONE = 0;
- /**
- * Connection to this network recently failed due to Association Rejection Status 17
- * (AP is full)
- */
- public static final int STATUS_AP_UNABLE_TO_HANDLE_NEW_STA = 17;
/**
* Association Rejection Status code (NONE for success/non-association-rejection-fail)
*/
- @AssociationStatus
- private int mAssociationStatus = NONE;
+ @RecentFailureReason
+ private int mAssociationStatus = RECENT_FAILURE_NONE;
/**
* @param status the association status code for the recent failure
- * @hide
*/
- public void setAssociationStatus(@AssociationStatus int status) {
+ public void setAssociationStatus(@RecentFailureReason int status) {
mAssociationStatus = status;
}
/**
* Sets the RecentFailure to NONE
- * @hide
*/
public void clear() {
- mAssociationStatus = NONE;
+ mAssociationStatus = RECENT_FAILURE_NONE;
}
/**
- * Get the recent failure code. One of {@link #NONE} or
- * {@link #STATUS_AP_UNABLE_TO_HANDLE_NEW_STA}.
+ * Get the recent failure code. One of {@link #RECENT_FAILURE_NONE} or
+ * {@link #RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA}.
*/
- @AssociationStatus
+ @RecentFailureReason
public int getAssociationStatus() {
return mAssociationStatus;
}
@@ -1986,10 +1971,47 @@
* RecentFailure member
* @hide
*/
+ // TODO(b/148626966): called by SUW via reflection, once SUW is updated, make private and
+ // rename to mRecentFailure
@NonNull
- @SystemApi
public final RecentFailure recentFailure = new RecentFailure();
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "RECENT_FAILURE_", value = {
+ RECENT_FAILURE_NONE,
+ RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA})
+ public @interface RecentFailureReason {}
+
+ /**
+ * No recent failure, or no specific reason given for the recent connection failure
+ * @hide
+ */
+ @SystemApi
+ public static final int RECENT_FAILURE_NONE = 0;
+ /**
+ * Connection to this network recently failed due to Association Rejection Status 17
+ * (AP is full)
+ * @hide
+ */
+ @SystemApi
+ public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17;
+
+ /**
+ * Get the failure reason for the most recent connection attempt, or
+ * {@link #RECENT_FAILURE_NONE} if there was no failure.
+ *
+ * Failure reasons include:
+ * {@link #RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA}
+ *
+ * @hide
+ */
+ @RecentFailureReason
+ @SystemApi
+ public int getRecentFailureReason() {
+ return recentFailure.getAssociationStatus();
+ }
+
/**
* Get the network selection status.
* @hide
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 6f01350..8250a95 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -836,6 +836,8 @@
* Enable/Disable wifi scanning.
*
* @param enable set to true to enable scanning, set to false to disable all types of scanning.
+ *
+ * @see WifiManager#ACTION_WIFI_SCAN_AVAILABLE
* {@hide}
*/
@SystemApi
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 3a0d080..756d679 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -19,6 +19,7 @@
import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NONE;
import static android.net.wifi.WifiConfiguration.MeteredOverride;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.wifi.hotspot2.pps.Credential;
@@ -895,4 +896,18 @@
public boolean isOsuProvisioned() {
return getUpdateIdentifier() != Integer.MIN_VALUE;
}
+
+ /**
+ * Get a unique identifier for a PasspointConfiguration object.
+ *
+ * @return A unique identifier
+ * @throws IllegalStateException if Credential or HomeSP nodes are not initialized
+ */
+ public @NonNull String getUniqueId() throws IllegalStateException {
+ if (mCredential == null || mHomeSp == null || TextUtils.isEmpty(mHomeSp.getFqdn())) {
+ throw new IllegalStateException("Credential or HomeSP are not initialized");
+ }
+
+ return mHomeSp.getFqdn();
+ }
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index 9562f95..36c7213 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -137,12 +137,12 @@
/** @hide */
@UnsupportedAppUsage
- public int netId = WifiP2pGroup.PERSISTENT_NET_ID;
+ public int netId = WifiP2pGroup.NETWORK_ID_PERSISTENT;
/**
* Get the network ID of this P2P configuration.
- * @return either a non-negative network ID, or one of {@link WifiP2pGroup#PERSISTENT_NET_ID} or
- * {@link WifiP2pGroup#TEMPORARY_NET_ID}.
+ * @return either a non-negative network ID, or one of
+ * {@link WifiP2pGroup#NETWORK_ID_PERSISTENT} or {@link WifiP2pGroup#NETWORK_ID_TEMPORARY}.
*/
public int getNetworkId() {
return netId;
@@ -280,7 +280,7 @@
private String mPassphrase = "";
private int mGroupOperatingBand = GROUP_OWNER_BAND_AUTO;
private int mGroupOperatingFrequency = GROUP_OWNER_BAND_AUTO;
- private int mNetId = WifiP2pGroup.TEMPORARY_NET_ID;
+ private int mNetId = WifiP2pGroup.NETWORK_ID_TEMPORARY;
/**
* Specify the peer's MAC address. If not set, the device will
@@ -460,9 +460,9 @@
*/
public @NonNull Builder enablePersistentMode(boolean persistent) {
if (persistent) {
- mNetId = WifiP2pGroup.PERSISTENT_NET_ID;
+ mNetId = WifiP2pGroup.NETWORK_ID_PERSISTENT;
} else {
- mNetId = WifiP2pGroup.TEMPORARY_NET_ID;
+ mNetId = WifiP2pGroup.NETWORK_ID_TEMPORARY;
}
return this;
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
index 21f6704..e497b22 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -41,7 +41,15 @@
* The temporary network id.
* @see #getNetworkId()
*/
- public static final int TEMPORARY_NET_ID = -1;
+ public static final int NETWORK_ID_TEMPORARY = -1;
+
+ /**
+ * The temporary network id.
+ *
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public static final int TEMPORARY_NET_ID = NETWORK_ID_TEMPORARY;
/**
* The persistent network id.
@@ -49,7 +57,7 @@
* Otherwise, create a new persistent profile.
* @see #getNetworkId()
*/
- public static final int PERSISTENT_NET_ID = -2;
+ public static final int NETWORK_ID_PERSISTENT = -2;
/** The network name */
private String mNetworkName;
@@ -130,13 +138,13 @@
mPassphrase = match.group(4);
mOwner = new WifiP2pDevice(match.group(5));
if (match.group(6) != null) {
- mNetId = PERSISTENT_NET_ID;
+ mNetId = NETWORK_ID_PERSISTENT;
} else {
- mNetId = TEMPORARY_NET_ID;
+ mNetId = NETWORK_ID_TEMPORARY;
}
} else if (tokens[0].equals("P2P-INVITATION-RECEIVED")) {
String sa = null;
- mNetId = PERSISTENT_NET_ID;
+ mNetId = NETWORK_ID_PERSISTENT;
for (String token : tokens) {
String[] nameValue = token.split("=");
if (nameValue.length != 2) continue;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 3459c94..0fe0675 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -1293,7 +1293,7 @@
@RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
public void createGroup(Channel c, ActionListener listener) {
checkChannel(c);
- c.mAsyncChannel.sendMessage(CREATE_GROUP, WifiP2pGroup.PERSISTENT_NET_ID,
+ c.mAsyncChannel.sendMessage(CREATE_GROUP, WifiP2pGroup.NETWORK_ID_PERSISTENT,
c.putListener(listener));
}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index 654154d..e78c5bf 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -18,6 +18,7 @@
import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NONE;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -364,4 +365,54 @@
assertTrue(config.validateForR2());
assertTrue(config.isOsuProvisioned());
}
+
+ /**
+ * Verify that the unique identifier generated is correct.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUniqueId() throws Exception {
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
+ String uniqueId;
+ uniqueId = config.getUniqueId();
+ assertEquals(uniqueId, config.getHomeSp().getFqdn());
+ }
+
+ /**
+ * Verify that the unique identifier API generates an exception if HomeSP is not initialized.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUniqueIdExceptionWithEmptyHomeSp() throws Exception {
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
+ config.setHomeSp(null);
+ boolean exceptionCaught = false;
+ try {
+ String uniqueId = config.getUniqueId();
+ } catch (IllegalStateException e) {
+ exceptionCaught = true;
+ }
+ assertTrue(exceptionCaught);
+ }
+
+ /**
+ * Verify that the unique identifier API generates an exception if Credential is not
+ * initialized.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUniqueIdExceptionWithEmptyCredential() throws Exception {
+ PasspointConfiguration config = PasspointTestUtils.createConfig();
+ config.setCredential(null);
+ boolean exceptionCaught = false;
+ try {
+ String uniqueId = config.getUniqueId();
+ } catch (IllegalStateException e) {
+ exceptionCaught = true;
+ }
+ assertTrue(exceptionCaught);
+ }
}