Merge "AudioService: Fix cross deadlock with AudioDeviceBroker" into rvc-dev
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
index 483d2cc..9c1acaf 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobStoreManager.java
@@ -184,9 +184,8 @@
* @throws SecurityException when the caller is not allowed to create a session, such
* as when called from an Instant app.
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
- * @throws IllegalStateException when a new session could not be created, such as when the
- * caller is trying to create too many sessions or when the
- * device is running low on space.
+ * @throws LimitExceededException when a new session could not be created, such as when the
+ * caller is trying to create too many sessions.
*/
public @IntRange(from = 1) long createSession(@NonNull BlobHandle blobHandle)
throws IOException {
@@ -194,6 +193,7 @@
return mService.createSession(blobHandle, mContext.getOpPackageName());
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
+ e.maybeRethrow(LimitExceededException.class);
throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -302,8 +302,9 @@
* if the {@code leaseExpiryTimeMillis} is greater than the
* {@link BlobHandle#getExpiryTimeMillis()}.
* @throws LimitExceededException when a lease could not be acquired, such as when the
- * caller is trying to acquire leases on too much data. Apps
- * can avoid this by checking the remaining quota using
+ * caller is trying to acquire too many leases or acquire
+ * leases on too much data. Apps can avoid this by checking
+ * the remaining quota using
* {@link #getRemainingLeaseQuotaBytes()} before trying to
* acquire a lease.
*
@@ -362,8 +363,9 @@
* if the {@code leaseExpiryTimeMillis} is greater than the
* {@link BlobHandle#getExpiryTimeMillis()}.
* @throws LimitExceededException when a lease could not be acquired, such as when the
- * caller is trying to acquire leases on too much data. Apps
- * can avoid this by checking the remaining quota using
+ * caller is trying to acquire too many leases or acquire
+ * leases on too much data. Apps can avoid this by checking
+ * the remaining quota using
* {@link #getRemainingLeaseQuotaBytes()} before trying to
* acquire a lease.
*
@@ -415,8 +417,9 @@
* exist or the caller does not have access to it.
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
* @throws LimitExceededException when a lease could not be acquired, such as when the
- * caller is trying to acquire leases on too much data. Apps
- * can avoid this by checking the remaining quota using
+ * caller is trying to acquire too many leases or acquire
+ * leases on too much data. Apps can avoid this by checking
+ * the remaining quota using
* {@link #getRemainingLeaseQuotaBytes()} before trying to
* acquire a lease.
*
@@ -462,8 +465,9 @@
* exist or the caller does not have access to it.
* @throws IllegalArgumentException when {@code blobHandle} is invalid.
* @throws LimitExceededException when a lease could not be acquired, such as when the
- * caller is trying to acquire leases on too much data. Apps
- * can avoid this by checking the remaining quota using
+ * caller is trying to acquire too many leases or acquire
+ * leases on too much data. Apps can avoid this by checking
+ * the remaining quota using
* {@link #getRemainingLeaseQuotaBytes()} before trying to
* acquire a lease.
*
@@ -757,6 +761,8 @@
* @throws SecurityException when the caller is not the owner of the session.
* @throws IllegalStateException when the caller tries to change access for a blob which is
* already committed.
+ * @throws LimitExceededException when the caller tries to explicitly allow too
+ * many packages using this API.
*/
public void allowPackageAccess(@NonNull String packageName, @NonNull byte[] certificate)
throws IOException {
@@ -764,6 +770,7 @@
mSession.allowPackageAccess(packageName, certificate);
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
+ e.maybeRethrow(LimitExceededException.class);
throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
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 4d29045..3d4154a2 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -398,6 +398,26 @@
return revocableFd.getRevocableFileDescriptor();
}
+ void destroy() {
+ revokeAllFds();
+ getBlobFile().delete();
+ }
+
+ private void revokeAllFds() {
+ synchronized (mRevocableFds) {
+ for (int i = 0, pkgCount = mRevocableFds.size(); i < pkgCount; ++i) {
+ final ArraySet<RevocableFileDescriptor> packageFds =
+ mRevocableFds.valueAt(i);
+ if (packageFds == null) {
+ continue;
+ }
+ for (int j = 0, fdCount = packageFds.size(); j < fdCount; ++j) {
+ packageFds.valueAt(j).revoke();
+ }
+ }
+ }
+ }
+
boolean shouldBeDeleted(boolean respectLeaseWaitTime) {
// Expired data blobs
if (getBlobHandle().isExpired()) {
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
index 265479f..79cd1b1 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreConfig.java
@@ -141,6 +141,36 @@
public static long DELETE_ON_LAST_LEASE_DELAY_MS =
DEFAULT_DELETE_ON_LAST_LEASE_DELAY_MS;
+ /**
+ * Denotes the maximum number of active sessions per app at any time.
+ */
+ public static final String KEY_MAX_ACTIVE_SESSIONS = "max_active_sessions";
+ public static int DEFAULT_MAX_ACTIVE_SESSIONS = 250;
+ public static int MAX_ACTIVE_SESSIONS = DEFAULT_MAX_ACTIVE_SESSIONS;
+
+ /**
+ * Denotes the maximum number of committed blobs per app at any time.
+ */
+ public static final String KEY_MAX_COMMITTED_BLOBS = "max_committed_blobs";
+ public static int DEFAULT_MAX_COMMITTED_BLOBS = 1000;
+ public static int MAX_COMMITTED_BLOBS = DEFAULT_MAX_COMMITTED_BLOBS;
+
+ /**
+ * Denotes the maximum number of leased blobs per app at any time.
+ */
+ public static final String KEY_MAX_LEASED_BLOBS = "max_leased_blobs";
+ public static int DEFAULT_MAX_LEASED_BLOBS = 500;
+ public static int MAX_LEASED_BLOBS = DEFAULT_MAX_LEASED_BLOBS;
+
+ /**
+ * Denotes the maximum number of packages explicitly permitted to access a blob
+ * (permitted as part of creating a {@link BlobAccessMode}).
+ */
+ public static final String KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES = "max_permitted_pks";
+ public static int DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES = 300;
+ public static int MAX_BLOB_ACCESS_PERMITTED_PACKAGES =
+ DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES;
+
static void refresh(Properties properties) {
if (!NAMESPACE_BLOBSTORE.equals(properties.getNamespace())) {
return;
@@ -178,6 +208,19 @@
DELETE_ON_LAST_LEASE_DELAY_MS = properties.getLong(key,
DEFAULT_DELETE_ON_LAST_LEASE_DELAY_MS);
break;
+ case KEY_MAX_ACTIVE_SESSIONS:
+ MAX_ACTIVE_SESSIONS = properties.getInt(key, DEFAULT_MAX_ACTIVE_SESSIONS);
+ break;
+ case KEY_MAX_COMMITTED_BLOBS:
+ MAX_COMMITTED_BLOBS = properties.getInt(key, DEFAULT_MAX_COMMITTED_BLOBS);
+ break;
+ case KEY_MAX_LEASED_BLOBS:
+ MAX_LEASED_BLOBS = properties.getInt(key, DEFAULT_MAX_LEASED_BLOBS);
+ break;
+ case KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES:
+ MAX_BLOB_ACCESS_PERMITTED_PACKAGES = properties.getInt(key,
+ DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES);
+ break;
default:
Slog.wtf(TAG, "Unknown key in device config properties: " + key);
}
@@ -210,6 +253,15 @@
fout.println(String.format(dumpFormat, KEY_DELETE_ON_LAST_LEASE_DELAY_MS,
TimeUtils.formatDuration(DELETE_ON_LAST_LEASE_DELAY_MS),
TimeUtils.formatDuration(DEFAULT_DELETE_ON_LAST_LEASE_DELAY_MS)));
+ fout.println(String.format(dumpFormat, KEY_MAX_ACTIVE_SESSIONS,
+ MAX_ACTIVE_SESSIONS, DEFAULT_MAX_ACTIVE_SESSIONS));
+ fout.println(String.format(dumpFormat, KEY_MAX_COMMITTED_BLOBS,
+ MAX_COMMITTED_BLOBS, DEFAULT_MAX_COMMITTED_BLOBS));
+ fout.println(String.format(dumpFormat, KEY_MAX_LEASED_BLOBS,
+ MAX_LEASED_BLOBS, DEFAULT_MAX_LEASED_BLOBS));
+ fout.println(String.format(dumpFormat, KEY_MAX_BLOB_ACCESS_PERMITTED_PACKAGES,
+ MAX_BLOB_ACCESS_PERMITTED_PACKAGES,
+ DEFAULT_MAX_BLOB_ACCESS_PERMITTED_PACKAGES));
}
}
@@ -288,6 +340,34 @@
return DeviceConfigProperties.DELETE_ON_LAST_LEASE_DELAY_MS;
}
+ /**
+ * Returns the maximum number of active sessions per app.
+ */
+ public static int getMaxActiveSessions() {
+ return DeviceConfigProperties.MAX_ACTIVE_SESSIONS;
+ }
+
+ /**
+ * Returns the maximum number of committed blobs per app.
+ */
+ public static int getMaxCommittedBlobs() {
+ return DeviceConfigProperties.MAX_COMMITTED_BLOBS;
+ }
+
+ /**
+ * Returns the maximum number of leased blobs per app.
+ */
+ public static int getMaxLeasedBlobs() {
+ return DeviceConfigProperties.MAX_LEASED_BLOBS;
+ }
+
+ /**
+ * Returns the maximum number of packages explicitly permitted to access a blob.
+ */
+ public static int getMaxPermittedPackages() {
+ return DeviceConfigProperties.MAX_BLOB_ACCESS_PERMITTED_PACKAGES;
+ }
+
@Nullable
public static File prepareBlobFile(long sessionId) {
final File blobsDir = prepareBlobsDir();
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 a90536fe..f7468d8 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -35,6 +35,9 @@
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_CURRENT;
import static com.android.server.blob.BlobStoreConfig.getAdjustedCommitTimeMs;
import static com.android.server.blob.BlobStoreConfig.getDeletionOnLastLeaseDelayMs;
+import static com.android.server.blob.BlobStoreConfig.getMaxActiveSessions;
+import static com.android.server.blob.BlobStoreConfig.getMaxCommittedBlobs;
+import static com.android.server.blob.BlobStoreConfig.getMaxLeasedBlobs;
import static com.android.server.blob.BlobStoreSession.STATE_ABANDONED;
import static com.android.server.blob.BlobStoreSession.STATE_COMMITTED;
import static com.android.server.blob.BlobStoreSession.STATE_VERIFIED_INVALID;
@@ -124,6 +127,7 @@
import java.util.Objects;
import java.util.Random;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -332,9 +336,26 @@
mKnownBlobIds.add(id);
}
+ @GuardedBy("mBlobsLock")
+ private int getSessionsCountLocked(int uid, String packageName) {
+ // TODO: Maintain a counter instead of traversing all the sessions
+ final AtomicInteger sessionsCount = new AtomicInteger(0);
+ forEachSessionInUser(session -> {
+ if (session.getOwnerUid() == uid && session.getOwnerPackageName().equals(packageName)) {
+ sessionsCount.getAndIncrement();
+ }
+ }, UserHandle.getUserId(uid));
+ return sessionsCount.get();
+ }
+
private long createSessionInternal(BlobHandle blobHandle,
int callingUid, String callingPackage) {
synchronized (mBlobsLock) {
+ final int sessionsCount = getSessionsCountLocked(callingUid, callingPackage);
+ if (sessionsCount >= getMaxActiveSessions()) {
+ throw new LimitExceededException("Too many active sessions for the caller: "
+ + sessionsCount);
+ }
// TODO: throw if there is already an active session associated with blobHandle.
final long sessionId = generateNextSessionIdLocked();
final BlobStoreSession session = new BlobStoreSession(mContext,
@@ -408,10 +429,39 @@
}
}
+ @GuardedBy("mBlobsLock")
+ private int getCommittedBlobsCountLocked(int uid, String packageName) {
+ // TODO: Maintain a counter instead of traversing all the blobs
+ final AtomicInteger blobsCount = new AtomicInteger(0);
+ forEachBlobInUser((blobMetadata) -> {
+ if (blobMetadata.isACommitter(packageName, uid)) {
+ blobsCount.getAndIncrement();
+ }
+ }, UserHandle.getUserId(uid));
+ return blobsCount.get();
+ }
+
+ @GuardedBy("mBlobsLock")
+ private int getLeasedBlobsCountLocked(int uid, String packageName) {
+ // TODO: Maintain a counter instead of traversing all the blobs
+ final AtomicInteger blobsCount = new AtomicInteger(0);
+ forEachBlobInUser((blobMetadata) -> {
+ if (blobMetadata.isALeasee(packageName, uid)) {
+ blobsCount.getAndIncrement();
+ }
+ }, UserHandle.getUserId(uid));
+ return blobsCount.get();
+ }
+
private void acquireLeaseInternal(BlobHandle blobHandle, int descriptionResId,
CharSequence description, long leaseExpiryTimeMillis,
int callingUid, String callingPackage) {
synchronized (mBlobsLock) {
+ final int leasesCount = getLeasedBlobsCountLocked(callingUid, callingPackage);
+ if (leasesCount >= getMaxLeasedBlobs()) {
+ throw new LimitExceededException("Too many leased blobs for the caller: "
+ + leasesCount);
+ }
final BlobMetadata blobMetadata = getUserBlobsLocked(UserHandle.getUserId(callingUid))
.get(blobHandle);
if (blobMetadata == null || !blobMetadata.isAccessAllowedForCaller(
@@ -556,7 +606,11 @@
UserHandle.getUserId(callingUid));
userBlobs.entrySet().removeIf(entry -> {
final BlobMetadata blobMetadata = entry.getValue();
- return blobMetadata.getBlobId() == blobId;
+ if (blobMetadata.getBlobId() == blobId) {
+ deleteBlobLocked(blobMetadata);
+ return true;
+ }
+ return false;
});
writeBlobsInfoAsync();
}
@@ -607,11 +661,10 @@
switch (session.getState()) {
case STATE_ABANDONED:
case STATE_VERIFIED_INVALID:
- session.getSessionFile().delete();
synchronized (mBlobsLock) {
+ deleteSessionLocked(session);
getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
.remove(session.getSessionId());
- mActiveBlobIds.remove(session.getSessionId());
if (LOGV) {
Slog.v(TAG, "Session is invalid; deleted " + session);
}
@@ -626,6 +679,17 @@
break;
case STATE_VERIFIED_VALID:
synchronized (mBlobsLock) {
+ final int committedBlobsCount = getCommittedBlobsCountLocked(
+ session.getOwnerUid(), session.getOwnerPackageName());
+ if (committedBlobsCount >= getMaxCommittedBlobs()) {
+ Slog.d(TAG, "Failed to commit: too many committed blobs. count: "
+ + committedBlobsCount + "; blob: " + session);
+ session.sendCommitCallbackResult(COMMIT_RESULT_ERROR);
+ deleteSessionLocked(session);
+ getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
+ .remove(session.getSessionId());
+ break;
+ }
final int userId = UserHandle.getUserId(session.getOwnerUid());
final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(
userId);
@@ -656,7 +720,7 @@
} else {
blob.addOrReplaceCommitter(existingCommitter);
}
- Slog.d(TAG, "Error committing the blob", e);
+ Slog.d(TAG, "Error committing the blob: " + session, e);
FrameworkStatsLog.write(FrameworkStatsLog.BLOB_COMMITTED,
session.getOwnerUid(), blob.getBlobId(), blob.getSize(),
FrameworkStatsLog.BLOB_COMMITTED__RESULT__ERROR_DURING_COMMIT);
@@ -670,8 +734,7 @@
}
// Delete redundant data from recommits.
if (session.getSessionId() != blob.getBlobId()) {
- session.getSessionFile().delete();
- mActiveBlobIds.remove(session.getSessionId());
+ deleteSessionLocked(session);
}
getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
.remove(session.getSessionId());
@@ -957,8 +1020,7 @@
userSessions.removeIf((sessionId, blobStoreSession) -> {
if (blobStoreSession.getOwnerUid() == uid
&& blobStoreSession.getOwnerPackageName().equals(packageName)) {
- blobStoreSession.getSessionFile().delete();
- mActiveBlobIds.remove(blobStoreSession.getSessionId());
+ deleteSessionLocked(blobStoreSession);
return true;
}
return false;
@@ -999,8 +1061,7 @@
if (userSessions != null) {
for (int i = 0, count = userSessions.size(); i < count; ++i) {
final BlobStoreSession session = userSessions.valueAt(i);
- session.getSessionFile().delete();
- mActiveBlobIds.remove(session.getSessionId());
+ deleteSessionLocked(session);
}
}
@@ -1076,8 +1137,7 @@
}
if (shouldRemove) {
- blobStoreSession.getSessionFile().delete();
- mActiveBlobIds.remove(blobStoreSession.getSessionId());
+ deleteSessionLocked(blobStoreSession);
deletedBlobIds.add(blobStoreSession.getSessionId());
}
return shouldRemove;
@@ -1089,13 +1149,29 @@
}
@GuardedBy("mBlobsLock")
+ private void deleteSessionLocked(BlobStoreSession blobStoreSession) {
+ blobStoreSession.destroy();
+ mActiveBlobIds.remove(blobStoreSession.getSessionId());
+ }
+
+ @GuardedBy("mBlobsLock")
private void deleteBlobLocked(BlobMetadata blobMetadata) {
- blobMetadata.getBlobFile().delete();
+ blobMetadata.destroy();
mActiveBlobIds.remove(blobMetadata.getBlobId());
}
void runClearAllSessions(@UserIdInt int userId) {
synchronized (mBlobsLock) {
+ for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
+ final int sessionUserId = mSessions.keyAt(i);
+ if (userId != UserHandle.USER_ALL && userId != sessionUserId) {
+ continue;
+ }
+ final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i);
+ for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) {
+ mActiveBlobIds.remove(userSessions.valueAt(j).getSessionId());
+ }
+ }
if (userId == UserHandle.USER_ALL) {
mSessions.clear();
} else {
@@ -1107,6 +1183,16 @@
void runClearAllBlobs(@UserIdInt int userId) {
synchronized (mBlobsLock) {
+ for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
+ final int blobUserId = mBlobsMap.keyAt(i);
+ if (userId != UserHandle.USER_ALL && userId != blobUserId) {
+ continue;
+ }
+ final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
+ for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
+ mActiveBlobIds.remove(userBlobs.valueAt(j).getBlobId());
+ }
+ }
if (userId == UserHandle.USER_ALL) {
mBlobsMap.clear();
} else {
@@ -1331,8 +1417,11 @@
+ "callingUid=" + callingUid + ", callingPackage=" + packageName);
}
- // TODO: Verify caller request is within limits (no. of calls/blob sessions/blobs)
- return createSessionInternal(blobHandle, callingUid, packageName);
+ try {
+ return createSessionInternal(blobHandle, callingUid, packageName);
+ } catch (LimitExceededException e) {
+ throw new ParcelableException(e);
+ }
}
@Override
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 baafff5..2f83be1 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -32,6 +32,7 @@
import static com.android.server.blob.BlobStoreConfig.TAG;
import static com.android.server.blob.BlobStoreConfig.XML_VERSION_ADD_SESSION_CREATION_TIME;
+import static com.android.server.blob.BlobStoreConfig.getMaxPermittedPackages;
import static com.android.server.blob.BlobStoreConfig.hasSessionExpired;
import android.annotation.BytesLong;
@@ -43,7 +44,9 @@
import android.content.Context;
import android.os.Binder;
import android.os.FileUtils;
+import android.os.LimitExceededException;
import android.os.ParcelFileDescriptor;
+import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.RevocableFileDescriptor;
import android.os.Trace;
@@ -76,7 +79,10 @@
import java.util.Arrays;
import java.util.Objects;
-/** TODO: add doc */
+/**
+ * Class to represent the state corresponding to an ongoing
+ * {@link android.app.blob.BlobStoreManager.Session}
+ */
@VisibleForTesting
class BlobStoreSession extends IBlobStoreSession.Stub {
@@ -326,6 +332,11 @@
throw new IllegalStateException("Not allowed to change access type in state: "
+ stateToString(mState));
}
+ if (mBlobAccessMode.getNumWhitelistedPackages() >= getMaxPermittedPackages()) {
+ throw new ParcelableException(new LimitExceededException(
+ "Too many packages permitted to access the blob: "
+ + mBlobAccessMode.getNumWhitelistedPackages()));
+ }
mBlobAccessMode.allowPackageAccess(packageName, certificate);
}
}
@@ -468,6 +479,11 @@
}
}
+ void destroy() {
+ revokeAllFds();
+ getSessionFile().delete();
+ }
+
private void revokeAllFds() {
synchronized (mRevocableFds) {
for (int i = mRevocableFds.size() - 1; i >= 0; --i) {
diff --git a/apex/media/framework/java/android/media/MediaParser.java b/apex/media/framework/java/android/media/MediaParser.java
index cc3017a..0c8c9a9 100644
--- a/apex/media/framework/java/android/media/MediaParser.java
+++ b/apex/media/framework/java/android/media/MediaParser.java
@@ -870,6 +870,14 @@
*/
public static final String PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS =
"android.media.mediaParser.overrideInBandCaptionDeclarations";
+ /**
+ * Sets whether a track for EMSG events should be exposed in case of parsing a container that
+ * supports them. {@code boolean} expected. Default value is {@link false}.
+ *
+ * @hide
+ */
+ public static final String PARAMETER_EXPOSE_EMSG_TRACK =
+ "android.media.mediaParser.exposeEmsgTrack";
// Private constants.
@@ -880,6 +888,7 @@
private static final String TS_MODE_MULTI_PMT = "multi_pmt";
private static final String TS_MODE_HLS = "hls";
private static final int BYTES_PER_SUBSAMPLE_ENCRYPTION_ENTRY = 6;
+ private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
@IntDef(
value = {
@@ -1309,6 +1318,10 @@
return new MatroskaExtractor(flags);
case PARSER_NAME_FMP4:
flags |=
+ getBooleanParameter(PARAMETER_EXPOSE_EMSG_TRACK)
+ ? FragmentedMp4Extractor.FLAG_ENABLE_EMSG_TRACK
+ : 0;
+ flags |=
getBooleanParameter(PARAMETER_MP4_IGNORE_EDIT_LISTS)
? FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS
: 0;
@@ -1674,6 +1687,9 @@
if (cryptoData != mLastReceivedCryptoData) {
mLastOutputCryptoInfo =
createNewCryptoInfoAndPopulateWithCryptoData(cryptoData);
+ // We are using in-band crypto info, so the IV will be ignored. But we prevent
+ // it from being null because toString assumes it non-null.
+ mLastOutputCryptoInfo.iv = EMPTY_BYTE_ARRAY;
}
} else /* We must populate the full CryptoInfo. */ {
// CryptoInfo.pattern is not accessible to the user, so the user needs to feed
@@ -1916,8 +1932,10 @@
// format for convenient use from ExoPlayer.
result.setString("crypto-mode-fourcc", format.drmInitData.schemeType);
}
+ if (format.subsampleOffsetUs != Format.OFFSET_SAMPLE_RELATIVE) {
+ result.setLong("subsample-offset-us-long", format.subsampleOffsetUs);
+ }
// LACK OF SUPPORT FOR:
- // format.containerMimeType;
// format.id;
// format.metadata;
// format.stereoMode;
@@ -2102,6 +2120,7 @@
PARAMETER_EXPOSE_CHUNK_INDEX_AS_MEDIA_FORMAT, Boolean.class);
expectedTypeByParameterName.put(
PARAMETER_OVERRIDE_IN_BAND_CAPTION_DECLARATIONS, Boolean.class);
+ expectedTypeByParameterName.put(PARAMETER_EXPOSE_EMSG_TRACK, Boolean.class);
// We do not check PARAMETER_EXPOSE_CAPTION_FORMATS here, and we do it in setParameters
// instead. Checking that the value is a List is insufficient to catch wrong parameter
// value types.
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index a5f0ac9..ca03343 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -6131,6 +6131,10 @@
*/
message ProcStats {
optional ProcessStatsSectionProto proc_stats_section = 1;
+ // Data pulled from device into this is sometimes sharded across multiple atoms to work around
+ // a size limit. When this happens, this shard ID will contain an increasing 1-indexed integer
+ // with the number of this shard.
+ optional int32 shard_id = 2;
}
/**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 6f8233d..c9031b7 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -763,23 +763,24 @@
@Override
public void revokeRuntimePermission(String packageName, String permName, UserHandle user) {
- if (DEBUG_TRACE_PERMISSION_UPDATES
- && shouldTraceGrant(packageName, permName, user.getIdentifier())) {
- Log.i(TAG, "App " + mContext.getPackageName() + " is revoking " + packageName + " "
- + permName + " for user " + user.getIdentifier(), new RuntimeException());
- }
- try {
- mPermissionManager
- .revokeRuntimePermission(packageName, permName, user.getIdentifier());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ revokeRuntimePermission(packageName, permName, user, null);
}
@Override
public void revokeRuntimePermission(String packageName, String permName, UserHandle user,
String reason) {
- // TODO evanseverson: impl
+ if (DEBUG_TRACE_PERMISSION_UPDATES
+ && shouldTraceGrant(packageName, permName, user.getIdentifier())) {
+ Log.i(TAG, "App " + mContext.getPackageName() + " is revoking " + packageName + " "
+ + permName + " for user " + user.getIdentifier() + " with reason " + reason,
+ new RuntimeException());
+ }
+ try {
+ mPermissionManager
+ .revokeRuntimePermission(packageName, permName, user.getIdentifier(), reason);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
@Override
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index a828aac..86a3579 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1900,13 +1900,11 @@
@Override
public Object getSystemService(String name) {
- // We may override this API from outer context.
- final boolean isUiContext = isUiContext() || getOuterContext().isUiContext();
// Check incorrect Context usage.
- if (isUiComponent(name) && !isUiContext && vmIncorrectContextUseEnabled()) {
+ if (isUiComponent(name) && !isUiContext() && vmIncorrectContextUseEnabled()) {
final String errorMessage = "Tried to access visual service "
+ SystemServiceRegistry.getSystemServiceClassName(name)
- + " from a non-visual Context:" + getOuterContext();
+ + " from a non-visual Context. ";
final String message = "Visual services, such as WindowManager, WallpaperService or "
+ "LayoutInflater should be accessed from Activity or other visual Context. "
+ "Use an Activity or a Context created with "
@@ -2371,7 +2369,6 @@
context.setResources(createResources(mToken, mPackageInfo, mSplitName, displayId,
overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo(),
mResources.getLoaders()));
- context.mIsUiContext = isUiContext() || getOuterContext().isUiContext();
return context;
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index e84c5e5..9459577 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -677,4 +677,10 @@
* Return whether the app freezer is supported (true) or not (false) by this system.
*/
boolean isAppFreezerSupported();
+
+
+ /**
+ * Kills uid with the reason of permission change.
+ */
+ void killUidForPermissionChange(int appId, int userId, String reason);
}
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 82e9881..ce51dba 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -294,7 +294,7 @@
}
final long identity = Binder.clearCallingIdentity();
try {
- mPermissionManager.revokeRuntimePermission(packageName, permission, userId);
+ mPermissionManager.revokeRuntimePermission(packageName, permission, userId, null);
} finally {
Binder.restoreCallingIdentity(identity);
}
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 30ee326..15625cd 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -680,7 +680,7 @@
* </table><br>
* </p>
*
- *<p>Devices capable of streaming concurrently with other devices as described by
+ *<p>BACKWARD_COMPATIBLE devices capable of streaming concurrently with other devices as described by
* {@link android.hardware.camera2.CameraManager#getConcurrentCameraIds} have the
* following guaranteed streams (when streaming concurrently with other devices)</p>
*
@@ -696,10 +696,14 @@
* </table><br>
* </p>
*
+ * <p> Devices which are not backwards-compatible, support a mandatory single stream of size sVGA with image format {@code DEPTH16} during concurrent operation.
+ *
* <p> For guaranteed concurrent stream configurations:</p>
- * <p> s720p refers to the camera device's resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
+ * <p> sVGA refers to the camera device's maximum resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
+ * VGA resolution (640X480) whichever is lower. </p>
+ * <p> s720p refers to the camera device's maximum resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
* 720p(1280X720) whichever is lower. </p>
- * <p> s1440p refers to the camera device's resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
+ * <p> s1440p refers to the camera device's maximum resolution for that format from {@link StreamConfigurationMap#getOutputSizes} or
* 1440p(1920X1440) whichever is lower. </p>
* <p>MONOCHROME-capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES}
* includes {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}) devices
@@ -707,6 +711,7 @@
* streams with {@code Y8} in all guaranteed stream combinations for the device's hardware level
* and capabilities.</p>
*
+ *
* <p>Devices capable of outputting HEIC formats ({@link StreamConfigurationMap#getOutputFormats}
* contains {@link android.graphics.ImageFormat#HEIC}) will support substituting {@code JPEG}
* streams with {@code HEIC} in all guaranteed stream combinations for the device's hardware
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 20d9c30..776d155 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -685,6 +685,12 @@
"Standard still image capture"),
};
+ private static StreamCombinationTemplate sConcurrentDepthOnlyStreamCombinations[] = {
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.DEPTH16, SizeThreshold.VGA) },
+ "Depth capture for mesh based object rendering"),
+ };
+
/**
* Helper builder class to generate a list of available mandatory stream combinations.
* @hide
@@ -729,19 +735,21 @@
getAvailableMandatoryConcurrentStreamCombinations() {
// Since concurrent streaming support is optional, we mandate these stream
// combinations regardless of camera device capabilities.
+
+ StreamCombinationTemplate []chosenStreamCombinations = sConcurrentStreamCombinations;
if (!isColorOutputSupported()) {
- Log.v(TAG, "Device is not backward compatible!");
- throw new IllegalArgumentException("Camera device which is not BACKWARD_COMPATIBLE"
- + " cannot have mandatory concurrent streams");
+ Log.v(TAG, "Device is not backward compatible, depth streams are mandatory!");
+ chosenStreamCombinations = sConcurrentDepthOnlyStreamCombinations;
}
+ Size sizeVGAp = new Size(640, 480);
Size size720p = new Size(1280, 720);
Size size1440p = new Size(1920, 1440);
ArrayList<MandatoryStreamCombination> availableConcurrentStreamCombinations =
new ArrayList<MandatoryStreamCombination>();
availableConcurrentStreamCombinations.ensureCapacity(
- sConcurrentStreamCombinations.length);
- for (StreamCombinationTemplate combTemplate : sConcurrentStreamCombinations) {
+ chosenStreamCombinations.length);
+ for (StreamCombinationTemplate combTemplate : chosenStreamCombinations) {
ArrayList<MandatoryStreamInformation> streamsInfo =
new ArrayList<MandatoryStreamInformation>();
streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
@@ -753,6 +761,9 @@
case s1440p:
formatSize = size1440p;
break;
+ case VGA:
+ formatSize = sizeVGAp;
+ break;
default:
formatSize = size720p;
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index ef2a8a1..b36aeb8 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -133,12 +133,23 @@
* <a href="/training/articles/security-key-attestation.html">key attestation</a> to obtain
* proof of the device's original identifiers.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
- * privileges (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). The profile
- * owner is an app that owns a managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
- * access is deprecated and will be removed in a future release.
+ * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+ * restrictions, and apps are recommended to use resettable identifiers (see <a
+ * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
+ * the following requirements is met:
+ * <ul>
+ * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+ * is a privileged permission that can only be granted to apps preloaded on the device.
+ * <li>If the calling app is the device or profile owner and has been granted the
+ * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
+ * owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app has carrier privileges (see {@link
+ * android.telephony.TelephonyManager#hasCarrierPrivileges}) on any active subscription.
+ * <li>If the calling app is the default SMS role holder (see {@link
+ * android.app.role.RoleManager#isRoleHeld(String)}).
+ * </ul>
*
* <p>If the calling app does not meet one of these requirements then this method will behave
* as follows:
@@ -150,7 +161,7 @@
* the READ_PHONE_STATE permission, or if the calling app is targeting API level 29 or
* higher, then a SecurityException is thrown.</li>
* </ul>
- * *
+ *
* @return The serial number if specified.
*/
@SuppressAutoDoc // No support for device / profile owner.
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 235b083..e231021 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -71,7 +71,7 @@
void grantRuntimePermission(String packageName, String permName, int userId);
- void revokeRuntimePermission(String packageName, String permName, int userId);
+ void revokeRuntimePermission(String packageName, String permName, int userId, String reason);
void resetRuntimePermissions();
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 9f7c5e4..daeb1c9 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -229,7 +229,7 @@
int transformHint);
@Nullable
- @GuardedBy("sLock")
+ @GuardedBy("mLock")
private ArrayList<OnReparentListener> mReparentListeners;
/**
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 5b6e5c1..0d21eb5 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1231,7 +1231,7 @@
float postScaleX, float postScaleY) {
transaction.setPosition(surface, positionLeft, positionTop);
transaction.setMatrix(surface, postScaleX /*dsdx*/, 0f /*dtdx*/,
- 0f /*dsdy*/, postScaleY /*dtdy*/);
+ 0f /*dtdy*/, postScaleY /*dsdy*/);
}
/** @hide */
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index ffeeb80..0d2d4d1 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -500,13 +500,12 @@
*/
public static ViewConfiguration get(Context context) {
if (!context.isUiContext() && vmIncorrectContextUseEnabled()) {
- final String errorMessage = "Tried to access UI constants from a non-visual Context:"
- + context;
+ final String errorMessage = "Tried to access UI constants from a non-visual Context.";
final String message = "UI constants, such as display metrics or window metrics, "
+ "must be accessed from Activity or other visual Context. "
+ "Use an Activity or a Context created with "
+ "Context#createWindowContext(int, Bundle), which are adjusted to the "
- + "configuration and visual bounds of an area on screen";
+ + "configuration and visual bounds of an area on screen.";
final Exception exception = new IllegalArgumentException(errorMessage);
StrictMode.onIncorrectContextUsed(message, exception);
Log.e(TAG, errorMessage + message, exception);
diff --git a/core/java/android/widget/inline/InlineContentView.java b/core/java/android/widget/inline/InlineContentView.java
index be7c696..1b666aa 100644
--- a/core/java/android/widget/inline/InlineContentView.java
+++ b/core/java/android/widget/inline/InlineContentView.java
@@ -156,8 +156,7 @@
@Override
public void onDraw() {
computeParentPositionAndScale();
- final int visibility = InlineContentView.this.isShown() ? VISIBLE : GONE;
- mSurfaceView.setVisibility(visibility);
+ mSurfaceView.setVisibility(VISIBLE);
}
};
diff --git a/core/res/res/layout/notification_template_material_conversation.xml b/core/res/res/layout/notification_template_material_conversation.xml
index 82e99e6..861a056 100644
--- a/core/res/res/layout/notification_template_material_conversation.xml
+++ b/core/res/res/layout/notification_template_material_conversation.xml
@@ -128,6 +128,9 @@
android:layout_weight="1">
<!-- Header -->
+
+ <!-- Use layout_marginStart instead of paddingStart to work around strange
+ measurement behavior on lower display densities. -->
<LinearLayout
android:id="@+id/conversation_header"
android:layout_width="wrap_content"
@@ -135,11 +138,11 @@
android:orientation="horizontal"
android:paddingTop="16dp"
android:layout_marginBottom="2dp"
- android:paddingStart="@dimen/conversation_content_start"
+ android:layout_marginStart="@dimen/conversation_content_start"
>
<TextView
android:id="@+id/conversation_text"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/notification_conversation_header_separating_margin"
android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index 493c85a..4eb6e42 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -41,19 +41,18 @@
/**
* Create an ephemeral key pair to use to establish a secure channel with a reader.
*
- * <p>Most applications will use only the public key, and only to send it to the reader,
- * allowing the private key to be used internally for {@link #encryptMessageToReader(byte[])}
- * and {@link #decryptMessageFromReader(byte[])}. The private key is also provided for
- * applications that wish to use a cipher suite that is not supported by
- * {@link IdentityCredentialStore}.
+ * <p>Applications should use this key-pair for the communications channel with the reader
+ * using a protocol / cipher-suite appropriate for the application. One example of such a
+ * protocol is the one used for Mobile Driving Licenses, see ISO 18013-5 section 9.2.1 "Session
+ * encryption".
*
* @return ephemeral key pair to use to establish a secure channel with a reader.
*/
public @NonNull abstract KeyPair createEphemeralKeyPair();
/**
- * Set the ephemeral public key provided by the reader. This must be called before
- * {@link #encryptMessageToReader} or {@link #decryptMessageFromReader} can be called.
+ * Set the ephemeral public key provided by the reader. If called, this must be called before
+ * {@link #getEntries(byte[], Map, byte[], byte[])} is called.
*
* @param readerEphemeralPublicKey The ephemeral public key provided by the reader to
* establish a secure session.
@@ -65,6 +64,11 @@
/**
* Encrypt a message for transmission to the reader.
*
+ * <p>Do not use. In this version of the API, this method produces an incorrect
+ * result. Instead, applications should implement message encryption/decryption themselves as
+ * detailed in the {@link #createEphemeralKeyPair()} method. In a future API-level, this
+ * method will be deprecated.
+ *
* @param messagePlaintext unencrypted message to encrypt.
* @return encrypted message.
*/
@@ -73,6 +77,11 @@
/**
* Decrypt a message received from the reader.
*
+ * <p>Do not use. In this version of the API, this method produces an incorrect
+ * result. Instead, applications should implement message encryption/decryption themselves as
+ * detailed in the {@link #createEphemeralKeyPair()} method. In a future API-level, this
+ * method will be deprecated.
+ *
* @param messageCiphertext encrypted message to decrypt.
* @return decrypted message.
* @throws MessageDecryptionException if the ciphertext couldn't be decrypted.
@@ -178,7 +187,7 @@
*
* <p>If {@code readerAuth} is not {@code null} it must be the bytes of a {@code COSE_Sign1}
* structure as defined in RFC 8152. For the payload nil shall be used and the
- * detached payload is the ReaderAuthentication CBOR described below.
+ * detached payload is the ReaderAuthenticationBytes CBOR described below.
* <pre>
* ReaderAuthentication = [
* "ReaderAuthentication",
@@ -186,7 +195,9 @@
* ItemsRequestBytes
* ]
*
- * ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest) ; Bytes of ItemsRequest
+ * ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
+ *
+ * ReaderAuthenticationBytes = #6.24(bstr .cbor ReaderAuthentication)
* </pre>
*
* <p>where {@code ItemsRequestBytes} are the bytes in the {@code requestMessage} parameter.
diff --git a/identity/java/android/security/identity/ResultData.java b/identity/java/android/security/identity/ResultData.java
index 37de2c4..71860d2 100644
--- a/identity/java/android/security/identity/ResultData.java
+++ b/identity/java/android/security/identity/ResultData.java
@@ -68,8 +68,8 @@
* {@link #getMessageAuthenticationCode()} can be used to get a MAC.
*
* <p>The CBOR structure which is cryptographically authenticated is the
- * {@code DeviceAuthentication} structure according to the following
- * <a href="https://tools.ietf.org/html/draft-ietf-cbor-cddl-06">CDDL</a> schema:
+ * {@code DeviceAuthenticationBytes} structure according to the following
+ * <a href="https://tools.ietf.org/html/rfc8610">CDDL</a> schema:
*
* <pre>
* DeviceAuthentication = [
@@ -80,15 +80,9 @@
* ]
*
* DocType = tstr
- *
- * SessionTranscript = [
- * DeviceEngagementBytes,
- * EReaderKeyBytes
- * ]
- *
- * DeviceEngagementBytes = #6.24(bstr .cbor DeviceEngagement)
- * EReaderKeyBytes = #6.24(bstr .cbor EReaderKey.Pub)
+ * SessionTranscript = any
* DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
+ * DeviceAuthenticationBytes = #6.24(bstr .cbor DeviceAuthentication)
* </pre>
*
* <p>where
@@ -115,7 +109,7 @@
public abstract @NonNull byte[] getAuthenticatedData();
/**
- * Returns a message authentication code over the {@code DeviceAuthentication} CBOR
+ * Returns a message authentication code over the {@code DeviceAuthenticationBytes} CBOR
* specified in {@link #getAuthenticatedData()}, to prove to the reader that the data
* is from a trusted credential.
*
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 62d76c0..0780c68 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2762,7 +2762,7 @@
builder.append(hexdigits.charAt(key[i] & 0x0f));
}
builder.append("], iv [");
- for (int i = 0; i < key.length; i++) {
+ for (int i = 0; i < iv.length; i++) {
builder.append(hexdigits.charAt((iv[i] & 0xf0) >> 4));
builder.append(hexdigits.charAt(iv[i] & 0x0f));
}
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index e8f18a5..515d610 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -335,7 +335,7 @@
if (mLinearBlockObj != NULL) {
return mLinearBlockObj;
}
- mIonHandle = new C2HandleIon(mAvHandle->data[0], mDataLength);
+ mIonHandle = new C2HandleIon(dup(mAvHandle->data[0]), mDataLength);
std::shared_ptr<C2LinearBlock> block = _C2BlockFactory::CreateLinearBlock(mIonHandle);
JNIEnv *env = AndroidRuntime::getJNIEnv();
diff --git a/packages/SystemUI/res/layout/controls_management.xml b/packages/SystemUI/res/layout/controls_management.xml
index 46f79de..ae7f44d 100644
--- a/packages/SystemUI/res/layout/controls_management.xml
+++ b/packages/SystemUI/res/layout/controls_management.xml
@@ -69,7 +69,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
- android:text="See other apps"
+ android:text="@string/controls_favorite_see_other_apps"
style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 0e3fa1e..db45a60 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2744,6 +2744,9 @@
<!-- Controls management favorites screen, See other apps with changes made [CHAR LIMIT=NONE] -->
<string name="controls_favorite_toast_no_changes">Changes not saved</string>
+ <!-- Controls management favorites screen. See other apps button [CHAR LIMIT=30] -->
+ <string name="controls_favorite_see_other_apps">See other apps</string>
+
<!-- Controls management controls screen error on load message [CHAR LIMIT=NONE] -->
<string name="controls_favorite_load_error">Controls could not be loaded. Check the <xliff:g id="app" example="System UI">%s</xliff:g> app to make sure that the app settings haven\u2019t changed.</string>
<!-- Controls management controls screen no controls found on load message [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index b615885..6dc8322 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -137,6 +137,7 @@
final int desiredHeight, final int desiredHeightResId, @Nullable final String title) {
Objects.requireNonNull(key);
Objects.requireNonNull(shortcutInfo);
+ mMetadataShortcutId = shortcutInfo.getId();
mShortcutInfo = shortcutInfo;
mKey = key;
mFlags = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 7020f1c..acbde9f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -366,11 +366,15 @@
validShortcutIds.add(info.getId());
}
- final Predicate<Bubble> invalidBubblesFromPackage = bubble ->
- packageName.equals(bubble.getPackageName())
- && (bubble.getShortcutInfo() == null
- || !bubble.getShortcutInfo().isEnabled()
- || !validShortcutIds.contains(bubble.getShortcutInfo().getId()));
+ final Predicate<Bubble> invalidBubblesFromPackage = bubble -> {
+ final boolean bubbleIsFromPackage = packageName.equals(bubble.getPackageName());
+ final boolean hasShortcutIdAndValidShortcut =
+ bubble.hasMetadataShortcutId()
+ && bubble.getShortcutInfo() != null
+ && bubble.getShortcutInfo().isEnabled()
+ && validShortcutIds.contains(bubble.getShortcutInfo().getId());
+ return bubbleIsFromPackage && !hasShortcutIdAndValidShortcut;
+ };
final Consumer<Bubble> removeBubble = bubble ->
dismissBubbleWithKey(bubble.getKey(), reason);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
index a888bd5..ffb650d6 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
@@ -70,9 +70,6 @@
private static final String WHITELISTED_AUTO_BUBBLE_APPS = "whitelisted_auto_bubble_apps";
- private static final String ALLOW_BUBBLE_OVERFLOW = "allow_bubble_overflow";
- private static final boolean ALLOW_BUBBLE_OVERFLOW_DEFAULT = true;
-
/**
* When true, if a notification has the information necessary to bubble (i.e. valid
* contentIntent and an icon or image), then a {@link android.app.Notification.BubbleMetadata}
@@ -87,15 +84,6 @@
}
/**
- * When true, show a menu with dismissed and aged-out bubbles.
- */
- static boolean allowBubbleOverflow(Context context) {
- return Settings.Secure.getInt(context.getContentResolver(),
- ALLOW_BUBBLE_OVERFLOW,
- ALLOW_BUBBLE_OVERFLOW_DEFAULT ? 1 : 0) != 0;
- }
-
- /**
* Same as {@link #allowAnyNotifToBubble(Context)} except it filters for notifications that
* are using {@link Notification.MessagingStyle} and have remote input.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index a389e2b..09e8799 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -1152,9 +1152,6 @@
}
private void setUpOverflow() {
- if (!BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
- return;
- }
int overflowBtnIndex = 0;
if (mBubbleOverflow == null) {
mBubbleOverflow = new BubbleOverflow(getContext());
@@ -1520,8 +1517,7 @@
}
private void updateOverflowVisibility() {
- if (!BubbleExperimentConfig.allowBubbleOverflow(mContext)
- || mBubbleOverflow == null) {
+ if (mBubbleOverflow == null) {
return;
}
mBubbleOverflow.setVisible(mIsExpanded ? VISIBLE : GONE);
@@ -2778,11 +2774,8 @@
* @return the number of bubbles in the stack view.
*/
public int getBubbleCount() {
- if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
- // Subtract 1 for the overflow button that is always in the bubble container.
- return mBubbleContainer.getChildCount() - 1;
- }
- return mBubbleContainer.getChildCount();
+ // Subtract 1 for the overflow button that is always in the bubble container.
+ return mBubbleContainer.getChildCount() - 1;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index e3fbdbc..468b9b1 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -281,8 +281,10 @@
Intent.createChooser(sharingIntent, null, chooserAction.getIntentSender())
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, requestCode,
- sharingChooserIntent, 0, null, UserHandle.CURRENT);
+
+ // cancel current pending intent (if any) since clipData isn't used for matching
+ PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, 0,
+ sharingChooserIntent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT);
// Create a share action for the notification
PendingIntent shareAction = PendingIntent.getBroadcastAsUser(context, requestCode,
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
index 6794a2a..52d4647 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -253,7 +253,7 @@
private final AtomicBoolean mRegistered = new AtomicBoolean();
@Inject
- public ProximityCheck(ProximitySensor sensor, DelayableExecutor delayableExecutor) {
+ public ProximityCheck(ProximitySensor sensor, @Main DelayableExecutor delayableExecutor) {
mSensor = sensor;
mSensor.setTag("prox_check");
mDelayableExecutor = delayableExecutor;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 65e98ac..4bba0d8 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -95,7 +95,6 @@
"android.hardware.light-V2.0-java",
"android.hardware.power-java",
"android.hardware.power-V1.0-java",
- "android.hardware.tv.cec-V1.0-java",
"android.hardware.vibrator-java",
"android.net.ipsec.ike.stubs.module_lib",
"app-compat-annotations",
@@ -117,6 +116,7 @@
"android.hardware.health-V2.0-java",
"android.hardware.health-V2.1-java",
"android.hardware.light-java",
+ "android.hardware.tv.cec-V1.0-java",
"android.hardware.weaver-V1.0-java",
"android.hardware.biometrics.face-V1.0-java",
"android.hardware.biometrics.fingerprint-V2.2-java",
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index be85906..a5d99e0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -82,8 +82,6 @@
import static android.os.Process.sendSignal;
import static android.os.Process.setThreadPriority;
import static android.os.Process.setThreadScheduler;
-import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED;
-import static android.permission.PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED;
import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
import static android.provider.Settings.Global.DEBUG_APP;
import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
@@ -9202,16 +9200,31 @@
synchronized (this) {
final long identity = Binder.clearCallingIdentity();
try {
- boolean permissionChange = KILL_APP_REASON_PERMISSIONS_REVOKED.equals(reason)
- || KILL_APP_REASON_GIDS_CHANGED.equals(reason);
mProcessList.killPackageProcessesLocked(null /* packageName */, appId, userId,
ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
true /* callerWillRestart */, true /* doit */, true /* evenPersistent */,
false /* setRemoved */,
- permissionChange ? ApplicationExitInfo.REASON_PERMISSION_CHANGE
- : ApplicationExitInfo.REASON_OTHER,
- permissionChange ? ApplicationExitInfo.SUBREASON_UNKNOWN
- : ApplicationExitInfo.SUBREASON_KILL_UID,
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_KILL_UID,
+ reason != null ? reason : "kill uid");
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ @Override
+ public void killUidForPermissionChange(int appId, int userId, String reason) {
+ enforceCallingPermission(Manifest.permission.KILL_UID, "killUid");
+ synchronized (this) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mProcessList.killPackageProcessesLocked(null /* packageName */, appId, userId,
+ ProcessList.PERSISTENT_PROC_ADJ, false /* callerWillRestart */,
+ true /* callerWillRestart */, true /* doit */, true /* evenPersistent */,
+ false /* setRemoved */,
+ ApplicationExitInfo.REASON_PERMISSION_CHANGE,
+ ApplicationExitInfo.SUBREASON_UNKNOWN,
reason != null ? reason : "kill uid");
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index b84d322..75ab33d 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -17,11 +17,18 @@
package com.android.server.hdmi;
import android.hardware.hdmi.HdmiPortInfo;
+import android.hardware.tv.cec.V1_0.CecMessage;
+import android.hardware.tv.cec.V1_0.HotplugEvent;
+import android.hardware.tv.cec.V1_0.IHdmiCec;
+import android.hardware.tv.cec.V1_0.IHdmiCec.getPhysicalAddressCallback;
+import android.hardware.tv.cec.V1_0.IHdmiCecCallback;
import android.hardware.tv.cec.V1_0.Result;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Handler;
+import android.os.IHwBinder;
import android.os.Looper;
import android.os.MessageQueue;
+import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.Slog;
import android.util.SparseArray;
@@ -79,6 +86,11 @@
private static final int MAX_HDMI_MESSAGE_HISTORY = 250;
+ private static final int INVALID_PHYSICAL_ADDRESS = 0xFFFF;
+
+ /** Cookie for matching the right end point. */
+ protected static final int HDMI_CEC_HAL_DEATH_COOKIE = 353;
+
// Predicate for whether the given logical address is remote device's one or not.
private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() {
@Override
@@ -102,10 +114,6 @@
// device or issued by internal state change.
private Handler mControlHandler;
- // Stores the pointer to the native implementation of the service that
- // interacts with HAL.
- private volatile long mNativePtr;
-
private final HdmiControlService mService;
// Stores the local CEC devices in the system. Device type is used for key.
@@ -149,23 +157,21 @@
* A factory method with injection of native methods for testing.
*/
static HdmiCecController createWithNativeWrapper(
- HdmiControlService service, NativeWrapper nativeWrapper) {
- HdmiCecController controller = new HdmiCecController(service, nativeWrapper);
- long nativePtr = nativeWrapper
- .nativeInit(controller, service.getServiceLooper().getQueue());
- if (nativePtr == 0L) {
- controller = null;
- return null;
- }
-
- controller.init(nativePtr);
- return controller;
+ HdmiControlService service, NativeWrapper nativeWrapper) {
+ HdmiCecController controller = new HdmiCecController(service, nativeWrapper);
+ String nativePtr = nativeWrapper.nativeInit();
+ if (nativePtr == null) {
+ HdmiLogger.warning("Couldn't get tv.cec service.");
+ return null;
+ }
+ controller.init(nativeWrapper);
+ return controller;
}
- private void init(long nativePtr) {
+ private void init(NativeWrapper nativeWrapper) {
mIoHandler = new Handler(mService.getIoLooper());
mControlHandler = new Handler(mService.getServiceLooper());
- mNativePtr = nativePtr;
+ nativeWrapper.setCallback(new HdmiCecCallback());
}
@ServiceThreadOnly
@@ -261,7 +267,7 @@
HdmiPortInfo[] getPortInfos() {
- return mNativeWrapperImpl.nativeGetPortInfos(mNativePtr);
+ return mNativeWrapperImpl.nativeGetPortInfos();
}
/**
@@ -289,7 +295,7 @@
int addLogicalAddress(int newLogicalAddress) {
assertRunOnServiceThread();
if (HdmiUtils.isValidAddress(newLogicalAddress)) {
- return mNativeWrapperImpl.nativeAddLogicalAddress(mNativePtr, newLogicalAddress);
+ return mNativeWrapperImpl.nativeAddLogicalAddress(newLogicalAddress);
} else {
return Result.FAILURE_INVALID_ARGS;
}
@@ -306,7 +312,7 @@
for (int i = 0; i < mLocalDevices.size(); ++i) {
mLocalDevices.valueAt(i).clearAddress();
}
- mNativeWrapperImpl.nativeClearLogicalAddress(mNativePtr);
+ mNativeWrapperImpl.nativeClearLogicalAddress();
}
@ServiceThreadOnly
@@ -326,7 +332,7 @@
@ServiceThreadOnly
int getPhysicalAddress() {
assertRunOnServiceThread();
- return mNativeWrapperImpl.nativeGetPhysicalAddress(mNativePtr);
+ return mNativeWrapperImpl.nativeGetPhysicalAddress();
}
/**
@@ -337,7 +343,7 @@
@ServiceThreadOnly
int getVersion() {
assertRunOnServiceThread();
- return mNativeWrapperImpl.nativeGetVersion(mNativePtr);
+ return mNativeWrapperImpl.nativeGetVersion();
}
/**
@@ -348,7 +354,7 @@
@ServiceThreadOnly
int getVendorId() {
assertRunOnServiceThread();
- return mNativeWrapperImpl.nativeGetVendorId(mNativePtr);
+ return mNativeWrapperImpl.nativeGetVendorId();
}
/**
@@ -361,7 +367,7 @@
void setOption(int flag, boolean enabled) {
assertRunOnServiceThread();
HdmiLogger.debug("setOption: [flag:%d, enabled:%b]", flag, enabled);
- mNativeWrapperImpl.nativeSetOption(mNativePtr, flag, enabled);
+ mNativeWrapperImpl.nativeSetOption(flag, enabled);
}
/**
@@ -375,7 +381,7 @@
if (!LanguageTag.isLanguage(language)) {
return;
}
- mNativeWrapperImpl.nativeSetLanguage(mNativePtr, language);
+ mNativeWrapperImpl.nativeSetLanguage(language);
}
/**
@@ -387,7 +393,7 @@
@ServiceThreadOnly
void enableAudioReturnChannel(int port, boolean enabled) {
assertRunOnServiceThread();
- mNativeWrapperImpl.nativeEnableAudioReturnChannel(mNativePtr, port, enabled);
+ mNativeWrapperImpl.nativeEnableAudioReturnChannel(port, enabled);
}
/**
@@ -399,7 +405,7 @@
@ServiceThreadOnly
boolean isConnected(int port) {
assertRunOnServiceThread();
- return mNativeWrapperImpl.nativeIsConnected(mNativePtr, port);
+ return mNativeWrapperImpl.nativeIsConnected(port);
}
/**
@@ -521,7 +527,7 @@
// <Polling Message> is a message which has empty body.
int ret =
mNativeWrapperImpl.nativeSendCecCommand(
- mNativePtr, sourceAddress, destinationAddress, EMPTY_BODY);
+ sourceAddress, destinationAddress, EMPTY_BODY);
if (ret == SendMessageResult.SUCCESS) {
return true;
} else if (ret != SendMessageResult.NACK) {
@@ -627,7 +633,7 @@
int i = 0;
int errorCode = SendMessageResult.SUCCESS;
do {
- errorCode = mNativeWrapperImpl.nativeSendCecCommand(mNativePtr,
+ errorCode = mNativeWrapperImpl.nativeSendCecCommand(
cecMessage.getSource(), cecMessage.getDestination(), body);
if (errorCode == SendMessageResult.SUCCESS) {
break;
@@ -651,7 +657,7 @@
}
/**
- * Called by native when incoming CEC message arrived.
+ * Called when incoming CEC message arrived.
*/
@ServiceThreadOnly
private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
@@ -663,7 +669,7 @@
}
/**
- * Called by native when a hotplug event issues.
+ * Called when a hotplug event issues.
*/
@ServiceThreadOnly
private void handleHotplug(int port, boolean connected) {
@@ -710,18 +716,19 @@
}
protected interface NativeWrapper {
- long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
- int nativeSendCecCommand(long controllerPtr, int srcAddress, int dstAddress, byte[] body);
- int nativeAddLogicalAddress(long controllerPtr, int logicalAddress);
- void nativeClearLogicalAddress(long controllerPtr);
- int nativeGetPhysicalAddress(long controllerPtr);
- int nativeGetVersion(long controllerPtr);
- int nativeGetVendorId(long controllerPtr);
- HdmiPortInfo[] nativeGetPortInfos(long controllerPtr);
- void nativeSetOption(long controllerPtr, int flag, boolean enabled);
- void nativeSetLanguage(long controllerPtr, String language);
- void nativeEnableAudioReturnChannel(long controllerPtr, int port, boolean flag);
- boolean nativeIsConnected(long controllerPtr, int port);
+ String nativeInit();
+ void setCallback(HdmiCecCallback callback);
+ int nativeSendCecCommand(int srcAddress, int dstAddress, byte[] body);
+ int nativeAddLogicalAddress(int logicalAddress);
+ void nativeClearLogicalAddress();
+ int nativeGetPhysicalAddress();
+ int nativeGetVersion();
+ int nativeGetVendorId();
+ HdmiPortInfo[] nativeGetPortInfos();
+ void nativeSetOption(int flag, boolean enabled);
+ void nativeSetLanguage(String language);
+ void nativeEnableAudioReturnChannel(int port, boolean flag);
+ boolean nativeIsConnected(int port);
}
private static native long nativeInit(HdmiCecController handler, MessageQueue messageQueue);
@@ -739,67 +746,200 @@
int port, boolean flag);
private static native boolean nativeIsConnected(long controllerPtr, int port);
- private static final class NativeWrapperImpl implements NativeWrapper {
+ private static final class NativeWrapperImpl implements NativeWrapper,
+ IHwBinder.DeathRecipient, getPhysicalAddressCallback {
+ private IHdmiCec mHdmiCec;
+ private final Object mLock = new Object();
+ private int mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
@Override
- public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) {
- return HdmiCecController.nativeInit(handler, messageQueue);
+ public String nativeInit() {
+ return (connectToHal() ? mHdmiCec.toString() : null);
+ }
+
+ boolean connectToHal() {
+ try {
+ mHdmiCec = IHdmiCec.getService();
+ try {
+ mHdmiCec.linkToDeath(this, HDMI_CEC_HAL_DEATH_COOKIE);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Couldn't link to death : ", e);
+ }
+ } catch (RemoteException e) {
+ HdmiLogger.error("Couldn't get tv.cec service : ", e);
+ return false;
+ }
+ return true;
}
@Override
- public int nativeSendCecCommand(long controllerPtr, int srcAddress, int dstAddress,
- byte[] body) {
- return HdmiCecController.nativeSendCecCommand(controllerPtr, srcAddress, dstAddress, body);
+ public void setCallback(HdmiCecCallback callback) {
+ try {
+ mHdmiCec.setCallback(callback);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Couldn't initialise tv.cec callback : ", e);
+ }
}
@Override
- public int nativeAddLogicalAddress(long controllerPtr, int logicalAddress) {
- return HdmiCecController.nativeAddLogicalAddress(controllerPtr, logicalAddress);
+ public int nativeSendCecCommand(int srcAddress, int dstAddress, byte[] body) {
+ CecMessage message = new CecMessage();
+ message.initiator = srcAddress;
+ message.destination = dstAddress;
+ message.body = new ArrayList<>(body.length);
+ for (byte b : body) {
+ message.body.add(b);
+ }
+ try {
+ return mHdmiCec.sendMessage(message);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to send CEC message : ", e);
+ return SendMessageResult.FAIL;
+ }
}
@Override
- public void nativeClearLogicalAddress(long controllerPtr) {
- HdmiCecController.nativeClearLogicalAddress(controllerPtr);
+ public int nativeAddLogicalAddress(int logicalAddress) {
+ try {
+ return mHdmiCec.addLogicalAddress(logicalAddress);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to add a logical address : ", e);
+ return Result.FAILURE_INVALID_ARGS;
+ }
}
@Override
- public int nativeGetPhysicalAddress(long controllerPtr) {
- return HdmiCecController.nativeGetPhysicalAddress(controllerPtr);
+ public void nativeClearLogicalAddress() {
+ try {
+ mHdmiCec.clearLogicalAddress();
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to clear logical address : ", e);
+ }
}
@Override
- public int nativeGetVersion(long controllerPtr) {
- return HdmiCecController.nativeGetVersion(controllerPtr);
+ public int nativeGetPhysicalAddress() {
+ try {
+ mHdmiCec.getPhysicalAddress(this);
+ return mPhysicalAddress;
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get physical address : ", e);
+ return INVALID_PHYSICAL_ADDRESS;
+ }
}
@Override
- public int nativeGetVendorId(long controllerPtr) {
- return HdmiCecController.nativeGetVendorId(controllerPtr);
+ public int nativeGetVersion() {
+ try {
+ return mHdmiCec.getCecVersion();
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get cec version : ", e);
+ return Result.FAILURE_UNKNOWN;
+ }
}
@Override
- public HdmiPortInfo[] nativeGetPortInfos(long controllerPtr) {
- return HdmiCecController.nativeGetPortInfos(controllerPtr);
+ public int nativeGetVendorId() {
+ try {
+ return mHdmiCec.getVendorId();
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get vendor id : ", e);
+ return Result.FAILURE_UNKNOWN;
+ }
}
@Override
- public void nativeSetOption(long controllerPtr, int flag, boolean enabled) {
- HdmiCecController.nativeSetOption(controllerPtr, flag, enabled);
+ public HdmiPortInfo[] nativeGetPortInfos() {
+ try {
+ ArrayList<android.hardware.tv.cec.V1_0.HdmiPortInfo> hdmiPortInfos =
+ mHdmiCec.getPortInfo();
+ HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[hdmiPortInfos.size()];
+ int i = 0;
+ for (android.hardware.tv.cec.V1_0.HdmiPortInfo portInfo : hdmiPortInfos) {
+ hdmiPortInfo[i] = new HdmiPortInfo(portInfo.portId,
+ portInfo.type,
+ portInfo.physicalAddress,
+ portInfo.cecSupported,
+ false,
+ portInfo.arcSupported);
+ i++;
+ }
+ return hdmiPortInfo;
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get port information : ", e);
+ return null;
+ }
}
@Override
- public void nativeSetLanguage(long controllerPtr, String language) {
- HdmiCecController.nativeSetLanguage(controllerPtr, language);
+ public void nativeSetOption(int flag, boolean enabled) {
+ try {
+ mHdmiCec.setOption(flag, enabled);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to set option : ", e);
+ }
}
@Override
- public void nativeEnableAudioReturnChannel(long controllerPtr, int port, boolean flag) {
- HdmiCecController.nativeEnableAudioReturnChannel(controllerPtr, port, flag);
+ public void nativeSetLanguage(String language) {
+ try {
+ mHdmiCec.setLanguage(language);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to set language : ", e);
+ }
}
@Override
- public boolean nativeIsConnected(long controllerPtr, int port) {
- return HdmiCecController.nativeIsConnected(controllerPtr, port);
+ public void nativeEnableAudioReturnChannel(int port, boolean flag) {
+ try {
+ mHdmiCec.enableAudioReturnChannel(port, flag);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to enable/disable ARC : ", e);
+ }
+ }
+
+ @Override
+ public boolean nativeIsConnected(int port) {
+ try {
+ return mHdmiCec.isConnected(port);
+ } catch (RemoteException e) {
+ HdmiLogger.error("Failed to get connection info : ", e);
+ return false;
+ }
+ }
+
+ @Override
+ public void serviceDied(long cookie) {
+ if (cookie == HDMI_CEC_HAL_DEATH_COOKIE) {
+ HdmiLogger.error(TAG, "Service died cokkie : " + cookie + "; reconnecting");
+ connectToHal();
+ }
+ }
+
+ @Override
+ public void onValues(int result, short addr) {
+ if (result == Result.SUCCESS) {
+ synchronized (mLock) {
+ mPhysicalAddress = new Short(addr).intValue();
+ }
+ }
+ }
+ }
+
+ final class HdmiCecCallback extends IHdmiCecCallback.Stub {
+ @Override
+ public void onCecMessage(CecMessage message) throws RemoteException {
+ byte[] body = new byte[message.body.size()];
+ for (int i = 0; i < message.body.size(); i++) {
+ body[i] = message.body.get(i);
+ }
+ runOnServiceThread(
+ () -> handleIncomingCecCommand(message.initiator, message.destination, body));
+ }
+
+ @Override
+ public void onHotplugEvent(HotplugEvent event) throws RemoteException {
+ runOnServiceThread(() -> handleHotplug(event.portId, event.connected));
}
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiLogger.java b/services/core/java/com/android/server/hdmi/HdmiLogger.java
index 2309293..8da3c93 100644
--- a/services/core/java/com/android/server/hdmi/HdmiLogger.java
+++ b/services/core/java/com/android/server/hdmi/HdmiLogger.java
@@ -18,9 +18,9 @@
import android.annotation.Nullable;
import android.os.SystemClock;
+import android.util.Log;
import android.util.Pair;
import android.util.Slog;
-import android.util.Log;
import java.util.HashMap;
@@ -71,6 +71,10 @@
getLogger().errorInternal(toLogString(logMessage, objs));
}
+ static void error(String logMessage, Exception e, Object... objs) {
+ getLogger().errorInternal(toLogString(logMessage + e, objs));
+ }
+
private void errorInternal(String logMessage) {
String log = updateLog(mErrorTimingCache, logMessage);
if (!log.isEmpty()) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index d8acf0e..85544d0 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -678,8 +678,6 @@
mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context,
GnssLocationProvider.this::onNetworkAvailable, mLooper, mNIHandler);
- sendMessage(INITIALIZE_HANDLER, 0, null);
-
mGnssStatusListenerHelper = new GnssStatusListenerHelper(mContext, mHandler) {
@Override
protected boolean isAvailableInPlatform() {
@@ -746,6 +744,8 @@
setProperties(PROPERTIES);
setAllowed(true);
+
+ sendMessage(INITIALIZE_HANDLER, 0, null);
}
/**
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index b219e26..3a203d5 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -280,11 +280,15 @@
@Override
public void onCompatChange(String packageName) {
- updateEnabledState(mPmInternal.getPackage(packageName));
+ AndroidPackage pkg = mPmInternal.getPackage(packageName);
+ if (pkg == null) {
+ return;
+ }
+ updateEnabledState(pkg);
mAppsFilter.updateShouldFilterCacheForPackage(packageName);
}
- private void updateEnabledState(AndroidPackage pkg) {
+ private void updateEnabledState(@NonNull AndroidPackage pkg) {
// TODO(b/135203078): Do not use toAppInfo
final boolean enabled = mInjector.getCompatibility().isChangeEnabledInternal(
PackageManager.FILTER_APPLICATION_QUERY, pkg.toAppInfoWithoutState());
@@ -297,12 +301,12 @@
@Override
public void updatePackageState(PackageSetting setting, boolean removed) {
- final boolean enableLogging =
+ final boolean enableLogging = setting.pkg != null &&
!removed && (setting.pkg.isTestOnly() || setting.pkg.isDebuggable());
enableLogging(setting.appId, enableLogging);
if (removed) {
- mDisabledPackages.remove(setting.pkg.getPackageName());
- } else {
+ mDisabledPackages.remove(setting.name);
+ } else if (setting.pkg != null) {
updateEnabledState(setting.pkg);
}
}
@@ -583,8 +587,9 @@
}
}
// if either package instruments the other, mark both as visible to one another
- if (pkgInstruments(newPkgSetting, existingSetting)
- || pkgInstruments(existingSetting, newPkgSetting)) {
+ if (newPkgSetting.pkg != null && existingSetting.pkg != null
+ && (pkgInstruments(newPkgSetting.pkg, existingSetting.pkg)
+ || pkgInstruments(existingSetting.pkg, newPkgSetting.pkg))) {
mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId);
mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId);
}
@@ -1106,16 +1111,14 @@
}
/** Returns {@code true} if the source package instruments the target package. */
- private static boolean pkgInstruments(PackageSetting source, PackageSetting target) {
+ private static boolean pkgInstruments(
+ @NonNull AndroidPackage source, @NonNull AndroidPackage target) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "pkgInstruments");
- final String packageName = target.pkg.getPackageName();
- final List<ParsedInstrumentation> inst = source.pkg.getInstrumentations();
+ final String packageName = target.getPackageName();
+ final List<ParsedInstrumentation> inst = source.getInstrumentations();
for (int i = ArrayUtils.size(inst) - 1; i >= 0; i--) {
if (Objects.equals(inst.get(i).getTargetPackage(), packageName)) {
- if (DEBUG_LOGGING) {
- log(source, target, "instrumentation");
- }
return true;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index de8ad6b..994cec2 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -401,6 +401,7 @@
private boolean mDataLoaderFinished = false;
+ // TODO(b/159663586): should be protected by mLock
private IncrementalFileStorages mIncrementalFileStorages;
private static final FileFilter sAddedApkFilter = new FileFilter() {
@@ -1353,7 +1354,7 @@
private boolean markAsSealed(@NonNull IntentSender statusReceiver, boolean forTransfer) {
Objects.requireNonNull(statusReceiver);
- List<PackageInstallerSession> childSessions = getChildSessions();
+ List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
@@ -1436,7 +1437,11 @@
*
* <p> This method is handy to prevent potential deadlocks (b/123391593)
*/
- private @Nullable List<PackageInstallerSession> getChildSessions() {
+ private @Nullable List<PackageInstallerSession> getChildSessionsNotLocked() {
+ if (Thread.holdsLock(mLock)) {
+ Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
+ + " is holding mLock", new Throwable());
+ }
List<PackageInstallerSession> childSessions = null;
if (isMultiPackage()) {
final int[] childSessionIds = getChildSessionIds();
@@ -1605,7 +1610,7 @@
return;
}
}
- List<PackageInstallerSession> childSessions = getChildSessions();
+ List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
synchronized (mLock) {
try {
sealLocked(childSessions);
@@ -1649,7 +1654,7 @@
throw new SecurityException("Can only transfer sessions that use public options");
}
- List<PackageInstallerSession> childSessions = getChildSessions();
+ List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
synchronized (mLock) {
assertCallerIsOwnerOrRootLocked();
@@ -1701,7 +1706,7 @@
// outside of the lock, because reading the child
// sessions with the lock held could lead to deadlock
// (b/123391593).
- List<PackageInstallerSession> childSessions = getChildSessions();
+ List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
try {
synchronized (mLock) {
@@ -2602,6 +2607,8 @@
"Session " + sessionId + " is a child of multi-package session "
+ mParentSessionId + " and may not be abandoned directly.");
}
+
+ List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
synchronized (mLock) {
if (params.isStaged && mDestroyed) {
// If a user abandons staged session in an unsafe state, then system will try to
@@ -2625,7 +2632,7 @@
mCallback.onStagedSessionChanged(this);
return;
}
- cleanStageDir();
+ cleanStageDir(childSessions);
}
if (mRelinquished) {
@@ -3055,7 +3062,7 @@
mStagedSessionErrorMessage = errorMessage;
Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage);
}
- cleanStageDir();
+ cleanStageDirNotLocked();
mCallback.onStagedSessionChanged(this);
}
@@ -3070,7 +3077,7 @@
mStagedSessionErrorMessage = "";
Slog.d(TAG, "Marking session " + sessionId + " as applied");
}
- cleanStageDir();
+ cleanStageDirNotLocked();
mCallback.onStagedSessionChanged(this);
}
@@ -3128,20 +3135,37 @@
}
}
- private void cleanStageDir() {
- if (isMultiPackage()) {
- for (int childSessionId : getChildSessionIds()) {
- mSessionProvider.getSession(childSessionId).cleanStageDir();
+ /**
+ * <b>must not hold {@link #mLock}</b>
+ */
+ private void cleanStageDirNotLocked() {
+ if (Thread.holdsLock(mLock)) {
+ Slog.wtf(TAG, "Calling thread " + Thread.currentThread().getName()
+ + " is holding mLock", new Throwable());
+ }
+ cleanStageDir(getChildSessionsNotLocked());
+ }
+
+ private void cleanStageDir(List<PackageInstallerSession> childSessions) {
+ if (childSessions != null) {
+ for (PackageInstallerSession childSession : childSessions) {
+ if (childSession != null) {
+ childSession.cleanStageDir();
+ }
}
} else {
- if (mIncrementalFileStorages != null) {
- mIncrementalFileStorages.cleanUp();
- mIncrementalFileStorages = null;
- }
- try {
- mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
- } catch (InstallerException ignored) {
- }
+ cleanStageDir();
+ }
+ }
+
+ private void cleanStageDir() {
+ if (mIncrementalFileStorages != null) {
+ mIncrementalFileStorages.cleanUp();
+ mIncrementalFileStorages = null;
+ }
+ try {
+ mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
+ } catch (InstallerException ignored) {
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 0dc4d13..1a7490e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -2281,7 +2281,7 @@
if (grant) {
mPermissionManager.grantRuntimePermission(pkg, perm, translatedUserId);
} else {
- mPermissionManager.revokeRuntimePermission(pkg, perm, translatedUserId);
+ mPermissionManager.revokeRuntimePermission(pkg, perm, translatedUserId, null);
}
return 0;
}
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 1b11e2d..4f0b689 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -29,6 +29,9 @@
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE;
import static android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME;
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
@@ -327,13 +330,17 @@
mPackageManagerInt.writeSettings(true);
}
@Override
- public void onPermissionRevoked(int uid, int userId) {
+ public void onPermissionRevoked(int uid, int userId, String reason) {
mOnPermissionChangeListeners.onPermissionsChanged(uid);
// Critical; after this call the application should never have the permission
mPackageManagerInt.writeSettings(false);
final int appId = UserHandle.getAppId(uid);
- mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED));
+ if (reason == null) {
+ mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED));
+ } else {
+ mHandler.post(() -> killUid(appId, userId, reason));
+ }
}
@Override
public void onInstallPermissionRevoked() {
@@ -470,7 +477,7 @@
IActivityManager am = ActivityManager.getService();
if (am != null) {
try {
- am.killUid(appId, userId, reason);
+ am.killUidForPermissionChange(appId, userId, reason);
} catch (RemoteException e) {
/* ignore - same process */
}
@@ -754,9 +761,9 @@
flagMask &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
flagValues &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
- flagValues &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
- flagValues &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
- flagValues &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+ flagValues &= ~FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+ flagValues &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+ flagValues &= ~FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
flagValues &= ~PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
}
@@ -1112,13 +1119,13 @@
int queryFlags = 0;
if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0) {
- queryFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+ queryFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
}
if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) {
- queryFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+ queryFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
}
if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) {
- queryFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+ queryFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
}
ArrayList<String> whitelistedPermissions = null;
@@ -1280,8 +1287,8 @@
final long identity = Binder.clearCallingIdentity();
try {
- setWhitelistedRestrictedPermissionsForUser(
- pkg, userId, permissions, Process.myUid(), flags, mDefaultPermissionCallback);
+ setWhitelistedRestrictedPermissionsForUsers(pkg, new int[]{ userId }, permissions,
+ Process.myUid(), flags, mDefaultPermissionCallback);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1526,19 +1533,21 @@
}
@Override
- public void revokeRuntimePermission(String packageName, String permName, int userId) {
+ public void revokeRuntimePermission(String packageName, String permName, int userId,
+ String reason) {
final int callingUid = Binder.getCallingUid();
final boolean overridePolicy =
checkUidPermission(ADJUST_RUNTIME_PERMISSIONS_POLICY, callingUid)
== PackageManager.PERMISSION_GRANTED;
revokeRuntimePermissionInternal(permName, packageName, overridePolicy, callingUid, userId,
- mDefaultPermissionCallback);
+ reason, mDefaultPermissionCallback);
}
// TODO swap permission name and package name
private void revokeRuntimePermissionInternal(String permName, String packageName,
- boolean overridePolicy, int callingUid, final int userId, PermissionCallback callback) {
+ boolean overridePolicy, int callingUid, final int userId, String reason,
+ PermissionCallback callback) {
if (ApplicationPackageManager.DEBUG_TRACE_PERMISSION_UPDATES
&& ApplicationPackageManager.shouldTraceGrant(packageName, permName, userId)) {
Log.i(TAG, "System is revoking " + packageName + " "
@@ -1629,7 +1638,7 @@
if (callback != null) {
callback.onPermissionRevoked(UserHandle.getUid(userId,
- UserHandle.getAppId(pkg.getUid())), userId);
+ UserHandle.getAppId(pkg.getUid())), userId, reason);
}
if (bp.isRuntime()) {
@@ -1703,7 +1712,7 @@
mDefaultPermissionCallback.onInstallPermissionGranted();
}
- public void onPermissionRevoked(int uid, int userId) {
+ public void onPermissionRevoked(int uid, int userId, String reason) {
revokedPermissions.add(IntPair.of(uid, userId));
syncUpdatedUsers.add(userId);
@@ -1816,7 +1825,7 @@
} else if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
// Otherwise, reset the permission.
revokeRuntimePermissionInternal(permName, packageName, false, Process.SYSTEM_UID,
- userId, delayingPermCallback);
+ userId, null, delayingPermCallback);
}
}
@@ -2297,7 +2306,7 @@
try {
revokeRuntimePermissionInternal(permissionName, packageName,
- false, callingUid, userId, permissionCallback);
+ false, callingUid, userId, null, permissionCallback);
} catch (IllegalArgumentException e) {
Slog.e(TAG, "Could not revoke " + permissionName + " from "
+ packageName, e);
@@ -2517,8 +2526,8 @@
if (permission.isHardOrSoftRestricted()
|| permission.isImmutablyRestricted()) {
permissionsState.updatePermissionFlags(permission, userId,
- PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
- PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT);
+ FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
+ FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT);
}
if (targetSdkVersion < Build.VERSION_CODES.M) {
permissionsState.updatePermissionFlags(permission, userId,
@@ -3756,8 +3765,8 @@
}
}
- private void setWhitelistedRestrictedPermissionsForUser(@NonNull AndroidPackage pkg,
- @UserIdInt int userId, @Nullable List<String> permissions, int callingUid,
+ private void setWhitelistedRestrictedPermissionsForUsers(@NonNull AndroidPackage pkg,
+ @UserIdInt int[] userIds, @Nullable List<String> permissions, int callingUid,
@PermissionWhitelistFlags int whitelistFlags, PermissionCallback callback) {
final PermissionsState permissionsState =
PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, pkg);
@@ -3765,95 +3774,102 @@
return;
}
- ArraySet<String> oldGrantedRestrictedPermissions = null;
+ SparseArray<ArraySet<String>> oldGrantedRestrictedPermissions = new SparseArray<>();
boolean updatePermissions = false;
-
final int permissionCount = pkg.getRequestedPermissions().size();
- for (int i = 0; i < permissionCount; i++) {
- final String permissionName = pkg.getRequestedPermissions().get(i);
- final BasePermission bp = mSettings.getPermissionLocked(permissionName);
+ for (int i = 0; i < userIds.length; i++) {
+ int userId = userIds[i];
+ for (int j = 0; j < permissionCount; j++) {
+ final String permissionName = pkg.getRequestedPermissions().get(j);
- if (bp == null || !bp.isHardOrSoftRestricted()) {
- continue;
- }
+ final BasePermission bp = mSettings.getPermissionLocked(permissionName);
- if (permissionsState.hasPermission(permissionName, userId)) {
- if (oldGrantedRestrictedPermissions == null) {
- oldGrantedRestrictedPermissions = new ArraySet<>();
+ if (bp == null || !bp.isHardOrSoftRestricted()) {
+ continue;
}
- oldGrantedRestrictedPermissions.add(permissionName);
- }
- final int oldFlags = permissionsState.getPermissionFlags(permissionName, userId);
-
- int newFlags = oldFlags;
- int mask = 0;
- int whitelistFlagsCopy = whitelistFlags;
- while (whitelistFlagsCopy != 0) {
- final int flag = 1 << Integer.numberOfTrailingZeros(whitelistFlagsCopy);
- whitelistFlagsCopy &= ~flag;
- switch (flag) {
- case FLAG_PERMISSION_WHITELIST_SYSTEM: {
- mask |= PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
- if (permissions != null && permissions.contains(permissionName)) {
- newFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
- } else {
- newFlags &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
- }
- } break;
- case FLAG_PERMISSION_WHITELIST_UPGRADE: {
- mask |= PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
- if (permissions != null && permissions.contains(permissionName)) {
- newFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
- } else {
- newFlags &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
- }
- } break;
- case FLAG_PERMISSION_WHITELIST_INSTALLER: {
- mask |= PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
- if (permissions != null && permissions.contains(permissionName)) {
- newFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
- } else {
- newFlags &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
- }
- } break;
+ if (permissionsState.hasPermission(permissionName, userId)) {
+ if (oldGrantedRestrictedPermissions.get(userId) == null) {
+ oldGrantedRestrictedPermissions.put(userId, new ArraySet<>());
+ }
+ oldGrantedRestrictedPermissions.get(userId).add(permissionName);
}
- }
- if (oldFlags == newFlags) {
- continue;
- }
+ final int oldFlags = permissionsState.getPermissionFlags(permissionName, userId);
- updatePermissions = true;
-
- final boolean wasWhitelisted = (oldFlags
- & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0;
- final boolean isWhitelisted = (newFlags
- & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0;
-
- // If the permission is policy fixed as granted but it is no longer
- // on any of the whitelists we need to clear the policy fixed flag
- // as whitelisting trumps policy i.e. policy cannot grant a non
- // grantable permission.
- if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
- final boolean isGranted = permissionsState.hasPermission(permissionName, userId);
- if (!isWhitelisted && isGranted) {
- mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
- newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+ int newFlags = oldFlags;
+ int mask = 0;
+ int whitelistFlagsCopy = whitelistFlags;
+ while (whitelistFlagsCopy != 0) {
+ final int flag = 1 << Integer.numberOfTrailingZeros(whitelistFlagsCopy);
+ whitelistFlagsCopy &= ~flag;
+ switch (flag) {
+ case FLAG_PERMISSION_WHITELIST_SYSTEM: {
+ mask |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+ if (permissions != null && permissions.contains(permissionName)) {
+ newFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+ } else {
+ newFlags &= ~FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
+ }
+ }
+ break;
+ case FLAG_PERMISSION_WHITELIST_UPGRADE: {
+ mask |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+ if (permissions != null && permissions.contains(permissionName)) {
+ newFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+ } else {
+ newFlags &= ~FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
+ }
+ }
+ break;
+ case FLAG_PERMISSION_WHITELIST_INSTALLER: {
+ mask |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+ if (permissions != null && permissions.contains(permissionName)) {
+ newFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+ } else {
+ newFlags &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
+ }
+ }
+ break;
+ }
}
- }
- // If we are whitelisting an app that does not support runtime permissions
- // we need to make sure it goes through the permission review UI at launch.
- if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M
- && !wasWhitelisted && isWhitelisted) {
- mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
- newFlags |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
- }
+ if (oldFlags == newFlags) {
+ continue;
+ }
- updatePermissionFlagsInternal(permissionName, pkg.getPackageName(), mask, newFlags,
- callingUid, userId, false, null /*callback*/);
+ updatePermissions = true;
+
+ final boolean wasWhitelisted = (oldFlags
+ & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0;
+ final boolean isWhitelisted = (newFlags
+ & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0;
+
+ // If the permission is policy fixed as granted but it is no longer
+ // on any of the whitelists we need to clear the policy fixed flag
+ // as whitelisting trumps policy i.e. policy cannot grant a non
+ // grantable permission.
+ if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
+ final boolean isGranted = permissionsState.hasPermission(permissionName,
+ userId);
+ if (!isWhitelisted && isGranted) {
+ mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+ newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+ }
+ }
+
+ // If we are whitelisting an app that does not support runtime permissions
+ // we need to make sure it goes through the permission review UI at launch.
+ if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M
+ && !wasWhitelisted && isWhitelisted) {
+ mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+ newFlags |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+ }
+
+ updatePermissionFlagsInternal(permissionName, pkg.getPackageName(), mask, newFlags,
+ callingUid, userId, false, null /*callback*/);
+ }
}
if (updatePermissions) {
@@ -3861,15 +3877,22 @@
restorePermissionState(pkg, false, pkg.getPackageName(), callback);
// If this resulted in losing a permission we need to kill the app.
- if (oldGrantedRestrictedPermissions != null) {
- final int oldGrantedCount = oldGrantedRestrictedPermissions.size();
- for (int i = 0; i < oldGrantedCount; i++) {
- final String permission = oldGrantedRestrictedPermissions.valueAt(i);
+ for (int i = 0; i < userIds.length; i++) {
+ int userId = userIds[i];
+ ArraySet<String> oldPermsForUser = oldGrantedRestrictedPermissions.get(userId);
+ if (oldPermsForUser == null) {
+ continue;
+ }
+
+ final int oldGrantedCount = oldPermsForUser.size();
+ for (int j = 0; j < oldGrantedCount; j++) {
+ final String permission = oldPermsForUser.valueAt(j);
// Sometimes we create a new permission state instance during update.
final PermissionsState newPermissionsState =
- PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt, pkg);
+ PackageManagerServiceUtils.getPermissionsState(mPackageManagerInt,
+ pkg);
if (!newPermissionsState.hasPermission(permission, userId)) {
- callback.onPermissionRevoked(pkg.getUid(), userId);
+ callback.onPermissionRevoked(pkg.getUid(), userId, null);
break;
}
}
@@ -4228,7 +4251,7 @@
overridePolicy,
Process.SYSTEM_UID,
userId,
- callback);
+ null, callback);
} catch (IllegalArgumentException e) {
Slog.e(TAG,
"Failed to revoke "
@@ -4624,10 +4647,8 @@
public void setWhitelistedRestrictedPermissions(@NonNull AndroidPackage pkg,
@NonNull int[] userIds, @Nullable List<String> permissions, int callingUid,
@PackageManager.PermissionWhitelistFlags int flags) {
- for (int userId : userIds) {
- setWhitelistedRestrictedPermissionsForUser(pkg, userId, permissions,
- callingUid, flags, mDefaultPermissionCallback);
- }
+ setWhitelistedRestrictedPermissionsForUsers(pkg, userIds, permissions,
+ callingUid, flags, mDefaultPermissionCallback);
}
@Override
public void setWhitelistedRestrictedPermissions(String packageName,
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 4412162..2e83b23 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -156,7 +156,7 @@
}
public void onInstallPermissionGranted() {
}
- public void onPermissionRevoked(int uid, @UserIdInt int userId) {
+ public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason) {
}
public void onInstallPermissionRevoked() {
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/README.md b/services/core/java/com/android/server/soundtrigger_middleware/README.md
new file mode 100644
index 0000000..416548d
--- /dev/null
+++ b/services/core/java/com/android/server/soundtrigger_middleware/README.md
@@ -0,0 +1,19 @@
+# Sound Trigger Middleware
+TODO: Add component description.
+
+## Notes about thread synchronization
+This component has some tricky thread synchronization considerations due to its layered design and
+due to the fact that it is involved in both in-bound and out-bound calls from / to
+external components. To avoid potential deadlocks, a strict locking order must be ensured whenever
+nesting locks. The order is:
+- `SoundTriggerMiddlewareValidation` lock.
+- Audio policy service lock. This one is external - it should be assumed to be held whenever we're
+ inside the `ExternalCaptureStateTracker.setCaptureState()` call stack *AND* to be acquired from
+ within our calls into `AudioSessionProvider.acquireSession()`.
+- `SoundTriggerModule` lock.
+
+This dictates careful consideration of callbacks going from `SoundTriggerModule` to
+`SoundTriggerMiddlewareValidation` and especially those coming from the `setCaptureState()` path.
+We always invoke those calls outside of the `SoundTriggerModule` lock, so we can lock
+`SoundTriggerMiddlewareValidation`. However, in the `setCaptureState()` case, we have to use atomics
+in `SoundTriggerMiddlewareValidation` and avoid the lock.
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index f4c77a0..5d25d2c 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -47,6 +47,9 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -328,7 +331,7 @@
}
/** Activity state. */
- Activity activityState = Activity.LOADED;
+ private AtomicInteger mActivityState = new AtomicInteger(Activity.LOADED.ordinal());
/** Human-readable description of the model. */
final String description;
@@ -383,6 +386,14 @@
void updateParameterSupport(int modelParam, @Nullable ModelParameterRange range) {
parameterSupport.put(modelParam, range);
}
+
+ Activity getActivityState() {
+ return Activity.values()[mActivityState.get()];
+ }
+
+ void setActivityState(Activity activity) {
+ mActivityState.set(activity.ordinal());
+ }
}
/**
@@ -393,7 +404,13 @@
IBinder.DeathRecipient {
private final ISoundTriggerCallback mCallback;
private ISoundTriggerModule mDelegate;
- private @NonNull Map<Integer, ModelState> mLoadedModels = new HashMap<>();
+ // While generally all the fields of this class must be changed under a lock, an exception
+ // is made for the specific case of changing a model state from ACTIVE to LOADED, which
+ // may happen as result of a recognition callback. This would happen atomically and is
+ // necessary in order to avoid deadlocks associated with locking from within callbacks
+ // possibly originating from the audio server.
+ private @NonNull
+ ConcurrentMap<Integer, ModelState> mLoadedModels = new ConcurrentHashMap<>();
private final int mHandle;
private ModuleStatus mState = ModuleStatus.ALIVE;
@@ -476,10 +493,9 @@
if (modelState == null) {
throw new IllegalStateException("Invalid handle: " + modelHandle);
}
- if (modelState.activityState
- != ModelState.Activity.LOADED) {
+ if (modelState.getActivityState() != ModelState.Activity.LOADED) {
throw new IllegalStateException("Model with handle: " + modelHandle
- + " has invalid state for unloading: " + modelState.activityState);
+ + " has invalid state for unloading: " + modelState.getActivityState());
}
// From here on, every exception isn't client's fault.
@@ -509,19 +525,21 @@
if (modelState == null) {
throw new IllegalStateException("Invalid handle: " + modelHandle);
}
- if (modelState.activityState
- != ModelState.Activity.LOADED) {
+ if (modelState.getActivityState() != ModelState.Activity.LOADED) {
throw new IllegalStateException("Model with handle: " + modelHandle
+ " has invalid state for starting recognition: "
- + modelState.activityState);
+ + modelState.getActivityState());
}
// From here on, every exception isn't client's fault.
try {
+ // Normally, we would set the state after the operation succeeds. However, since
+ // the activity state may be reset outside of the lock, we set it here first,
+ // and reset it in case of exception.
+ modelState.setActivityState(ModelState.Activity.ACTIVE);
mDelegate.startRecognition(modelHandle, config);
- modelState.activityState =
- ModelState.Activity.ACTIVE;
} catch (Exception e) {
+ modelState.setActivityState(ModelState.Activity.LOADED);
throw handleException(e);
}
}
@@ -548,8 +566,7 @@
// From here on, every exception isn't client's fault.
try {
mDelegate.stopRecognition(modelHandle);
- modelState.activityState =
- ModelState.Activity.LOADED;
+ modelState.setActivityState(ModelState.Activity.LOADED);
} catch (Exception e) {
throw handleException(e);
}
@@ -719,7 +736,7 @@
for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
pw.print(entry.getKey());
pw.print('\t');
- pw.print(entry.getValue().activityState.name());
+ pw.print(entry.getValue().getActivityState().name());
pw.print('\t');
pw.print(entry.getValue().description);
pw.println();
@@ -735,48 +752,61 @@
@Override
public void onRecognition(int modelHandle, @NonNull RecognitionEvent event) {
- synchronized (SoundTriggerMiddlewareValidation.this) {
- if (event.status != RecognitionStatus.FORCED) {
- mLoadedModels.get(modelHandle).activityState =
- ModelState.Activity.LOADED;
+ // We cannot obtain a lock on SoundTriggerMiddlewareValidation.this, since this call
+ // might be coming from the audio server (via setCaptureState()) while it is holding
+ // a lock that is also acquired while loading / unloading models. Thus, we require a
+ // strict locking order here, where obtaining our lock must always come first.
+ // To avoid this problem, we use an atomic model activity state. There is a risk of the
+ // model not being in the mLoadedModels map here, since it might have been stopped /
+ // unloaded while the event was in flight.
+ if (event.status != RecognitionStatus.FORCED) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (modelState != null) {
+ modelState.setActivityState(ModelState.Activity.LOADED);
}
- try {
- mCallback.onRecognition(modelHandle, event);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
- }
+ }
+ try {
+ mCallback.onRecognition(modelHandle, event);
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
}
}
@Override
public void onPhraseRecognition(int modelHandle, @NonNull PhraseRecognitionEvent event) {
- synchronized (SoundTriggerMiddlewareValidation.this) {
- if (event.common.status != RecognitionStatus.FORCED) {
- mLoadedModels.get(modelHandle).activityState =
- ModelState.Activity.LOADED;
+ // We cannot obtain a lock on SoundTriggerMiddlewareValidation.this, since this call
+ // might be coming from the audio server (via setCaptureState()) while it is holding
+ // a lock that is also acquired while loading / unloading models. Thus, we require a
+ // strict locking order here, where obtaining our lock must always come first.
+ // To avoid this problem, we use an atomic model activity state. There is a risk of the
+ // model not being in the mLoadedModels map here, since it might have been stopped /
+ // unloaded while the event was in flight.
+ if (event.common.status != RecognitionStatus.FORCED) {
+ ModelState modelState = mLoadedModels.get(modelHandle);
+ if (modelState != null) {
+ modelState.setActivityState(ModelState.Activity.LOADED);
}
- try {
- mCallback.onPhraseRecognition(modelHandle, event);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
- }
+ }
+ try {
+ mCallback.onPhraseRecognition(modelHandle, event);
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
}
}
@Override
public void onRecognitionAvailabilityChange(boolean available) {
- synchronized (SoundTriggerMiddlewareValidation.this) {
- try {
- mCallback.onRecognitionAvailabilityChange(available);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
- }
+ // Not locking to avoid deadlocks (not affecting any state).
+ try {
+ mCallback.onRecognitionAvailabilityChange(available);
+ } catch (RemoteException e) {
+ // Dead client will be handled by binderDied() - no need to handle here.
+ // In any case, client callbacks are considered best effort.
+ Log.e(TAG, "Client callback exception.", e);
}
}
@@ -804,10 +834,9 @@
// Gracefully stop all active recognitions and unload the models.
for (Map.Entry<Integer, ModelState> entry :
mLoadedModels.entrySet()) {
- if (entry.getValue().activityState
- == ModelState.Activity.ACTIVE) {
- mDelegate.stopRecognition(entry.getKey());
- }
+ // Idempotent call, no harm in calling even for models that are already
+ // stopped.
+ mDelegate.stopRecognition(entry.getKey());
mDelegate.unloadModel(entry.getKey());
}
// Detach.
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index 522e5e1..f809ed4 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -42,6 +42,7 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -153,18 +154,28 @@
*
* @param active true iff external capture is active.
*/
- synchronized void setExternalCaptureState(boolean active) {
- if (mProperties.concurrentCapture) {
- // If we support concurrent capture, we don't care about any of this.
- return;
- }
- mRecognitionAvailable = !active;
- if (!mRecognitionAvailable) {
- // Our module does not support recognition while a capture is active -
- // need to abort all active recognitions.
- for (Session session : mActiveSessions) {
- session.abortActiveRecognitions();
+ void setExternalCaptureState(boolean active) {
+ // We should never invoke callbacks while holding the lock, since this may deadlock with
+ // forward calls. Thus, we first gather all the callbacks we need to invoke while holding
+ // the lock, but invoke them after releasing it.
+ List<Runnable> callbacks = new LinkedList<>();
+
+ synchronized (this) {
+ if (mProperties.concurrentCapture) {
+ // If we support concurrent capture, we don't care about any of this.
+ return;
}
+ mRecognitionAvailable = !active;
+ if (!mRecognitionAvailable) {
+ // Our module does not support recognition while a capture is active -
+ // need to abort all active recognitions.
+ for (Session session : mActiveSessions) {
+ session.abortActiveRecognitions(callbacks);
+ }
+ }
+ }
+ for (Runnable callback : callbacks) {
+ callback.run();
}
for (Session session : mActiveSessions) {
session.notifyRecognitionAvailability();
@@ -329,9 +340,18 @@
@Override
public void startRecognition(int modelHandle, @NonNull RecognitionConfig config) {
+ // We should never invoke callbacks while holding the lock, since this may deadlock with
+ // forward calls. Thus, we first gather all the callbacks we need to invoke while holding
+ // the lock, but invoke them after releasing it.
+ List<Runnable> callbacks = new LinkedList<>();
+
synchronized (SoundTriggerModule.this) {
checkValid();
- mLoadedModels.get(modelHandle).startRecognition(config);
+ mLoadedModels.get(modelHandle).startRecognition(config, callbacks);
+ }
+
+ for (Runnable callback : callbacks) {
+ callback.run();
}
}
@@ -377,10 +397,12 @@
/**
* Abort all currently active recognitions.
+ * @param callbacks Will be appended with a list of callbacks that need to be invoked
+ * after this method returns, without holding the module lock.
*/
- private void abortActiveRecognitions() {
+ private void abortActiveRecognitions(@NonNull List<Runnable> callbacks) {
for (Model model : mLoadedModels.values()) {
- model.abortActiveRecognition();
+ model.abortActiveRecognition(callbacks);
}
}
@@ -475,10 +497,11 @@
return mSession.mSessionHandle;
}
- private void startRecognition(@NonNull RecognitionConfig config) {
+ private void startRecognition(@NonNull RecognitionConfig config,
+ @NonNull List<Runnable> callbacks) {
if (!mRecognitionAvailable) {
// Recognition is unavailable - send an abort event immediately.
- notifyAbort();
+ callbacks.add(this::notifyAbort);
return;
}
android.hardware.soundtrigger.V2_3.RecognitionConfig hidlConfig =
@@ -525,8 +548,12 @@
ConversionUtil.aidl2hidlModelParameter(modelParam)));
}
- /** Abort the recognition, if active. */
- private void abortActiveRecognition() {
+ /**
+ * Abort the recognition, if active.
+ * @param callbacks Will be appended with a list of callbacks that need to be invoked
+ * after this method returns, without holding the module lock.
+ */
+ private void abortActiveRecognition(List<Runnable> callbacks) {
// If we're inactive, do nothing.
if (getState() != ModelState.ACTIVE) {
return;
@@ -535,7 +562,7 @@
stopRecognition();
// Notify the client that recognition has been aborted.
- notifyAbort();
+ callbacks.add(this::notifyAbort);
}
/** Notify the client that recognition has been aborted. */
@@ -577,42 +604,44 @@
public void recognitionCallback(
@NonNull ISoundTriggerHwCallback.RecognitionEvent recognitionEvent,
int cookie) {
+ RecognitionEvent aidlEvent =
+ ConversionUtil.hidl2aidlRecognitionEvent(recognitionEvent);
+ aidlEvent.captureSession = mSession.mSessionHandle;
synchronized (SoundTriggerModule.this) {
- RecognitionEvent aidlEvent =
- ConversionUtil.hidl2aidlRecognitionEvent(recognitionEvent);
- aidlEvent.captureSession = mSession.mSessionHandle;
- try {
- mCallback.onRecognition(mHandle, aidlEvent);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback execption.", e);
- }
if (aidlEvent.status != RecognitionStatus.FORCED) {
setState(ModelState.LOADED);
}
}
+ // The callback must be invoked outside of the lock.
+ try {
+ mCallback.onRecognition(mHandle, aidlEvent);
+ } catch (RemoteException e) {
+ // We're not expecting any exceptions here.
+ throw e.rethrowAsRuntimeException();
+ }
}
@Override
public void phraseRecognitionCallback(
@NonNull ISoundTriggerHwCallback.PhraseRecognitionEvent phraseRecognitionEvent,
int cookie) {
+ PhraseRecognitionEvent aidlEvent =
+ ConversionUtil.hidl2aidlPhraseRecognitionEvent(phraseRecognitionEvent);
+ aidlEvent.common.captureSession = mSession.mSessionHandle;
+
synchronized (SoundTriggerModule.this) {
- PhraseRecognitionEvent aidlEvent =
- ConversionUtil.hidl2aidlPhraseRecognitionEvent(phraseRecognitionEvent);
- aidlEvent.common.captureSession = mSession.mSessionHandle;
- try {
- mCallback.onPhraseRecognition(mHandle, aidlEvent);
- } catch (RemoteException e) {
- // Dead client will be handled by binderDied() - no need to handle here.
- // In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback execption.", e);
- }
if (aidlEvent.common.status != RecognitionStatus.FORCED) {
setState(ModelState.LOADED);
}
}
+
+ // The callback must be invoked outside of the lock.
+ try {
+ mCallback.onPhraseRecognition(mHandle, aidlEvent);
+ } catch (RemoteException e) {
+ // We're not expecting any exceptions here.
+ throw e.rethrowAsRuntimeException();
+ }
}
}
}
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 09fd33d..dbdef23 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -2575,11 +2575,17 @@
lastHighWaterMark, section, true, statsFiles, procStats);
procStats.dumpAggregatedProtoForStatsd(protoStreams, MAX_PROCSTATS_RAW_SHARD_SIZE);
- for (ProtoOutputStream proto : protoStreams) {
- if (proto.getBytes().length > 0) {
+ for (int i = 0; i < protoStreams.length; i++) {
+ byte[] bytes = protoStreams[i].getBytes(); // cache the value
+ if (bytes.length > 0) {
StatsEvent e = StatsEvent.newBuilder()
.setAtomId(atomTag)
- .writeByteArray(proto.getBytes())
+ .writeByteArray(bytes)
+ // This is a shard ID, and is specified in the metric definition to be
+ // a dimension. This will result in statsd using RANDOM_ONE_SAMPLE to
+ // keep all the shards, as it thinks each shard is a different dimension
+ // of data.
+ .writeInt(i)
.build();
pulledData.add(e);
}
diff --git a/services/core/java/com/android/server/uri/UriGrantsManagerService.java b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
index c38d649..5f63233 100644
--- a/services/core/java/com/android/server/uri/UriGrantsManagerService.java
+++ b/services/core/java/com/android/server/uri/UriGrantsManagerService.java
@@ -115,7 +115,7 @@
private static final String TAG = "UriGrantsManagerService";
// Maximum number of persisted Uri grants a package is allowed
private static final int MAX_PERSISTED_URI_GRANTS = 128;
- private static final boolean ENABLE_DYNAMIC_PERMISSIONS = false;
+ private static final boolean ENABLE_DYNAMIC_PERMISSIONS = true;
private final Object mLock = new Object();
private final H mH;
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 6fbfa68..16ca60d 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -52,6 +52,7 @@
import com.android.internal.util.ArrayUtils;
import com.android.server.am.ActivityManagerService;
import com.android.server.am.PendingIntentRecord;
+import com.android.server.uri.NeededUriGrants;
import com.android.server.wm.ActivityStackSupervisor.PendingActivityLaunch;
import com.android.server.wm.ActivityStarter.DefaultFactory;
import com.android.server.wm.ActivityStarter.Factory;
@@ -402,6 +403,7 @@
// potentially acquire activity manager lock that leads to deadlock.
for (int i = 0; i < intents.length; i++) {
Intent intent = intents[i];
+ NeededUriGrants intentGrants = null;
// Refuse possible leaked file descriptors.
if (intent.hasFileDescriptors()) {
@@ -418,6 +420,14 @@
0 /* startFlags */, null /* profilerInfo */, userId, filterCallingUid);
aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId);
+ // Carefully collect grants without holding lock
+ if (aInfo != null) {
+ intentGrants = mSupervisor.mService.mUgmInternal
+ .checkGrantUriPermissionFromIntent(intent, filterCallingUid,
+ aInfo.applicationInfo.packageName,
+ UserHandle.getUserId(aInfo.applicationInfo.uid));
+ }
+
if (aInfo != null) {
if ((aInfo.applicationInfo.privateFlags
& ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {
@@ -433,6 +443,7 @@
? options
: null;
starters[i] = obtainStarter(intent, reason)
+ .setIntentGrants(intentGrants)
.setCaller(caller)
.setResolvedType(resolvedTypes[i])
.setActivityInfo(aInfo)
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index daa97b5..25842f5 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -639,8 +639,14 @@
mRequest.intent, caller);
}
- // Do not lock the resolving to avoid potential deadlock.
+ // If the caller hasn't already resolved the activity, we're willing
+ // to do so here, but because that may require acquiring the AM lock
+ // as part of calculating the NeededUriGrants, we must never hold
+ // the WM lock here to avoid deadlocking.
if (mRequest.activityInfo == null) {
+ if (Thread.holdsLock(mService.mGlobalLock)) {
+ Slog.wtf(TAG, new IllegalStateException("Caller must not hold WM lock"));
+ }
mRequest.resolveActivity(mSupervisor);
}
@@ -2632,6 +2638,11 @@
return mRequest.intent;
}
+ ActivityStarter setIntentGrants(NeededUriGrants intentGrants) {
+ mRequest.intentGrants = intentGrants;
+ return this;
+ }
+
ActivityStarter setReason(String reason) {
mRequest.reason = reason;
return this;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index cf453c7..205523b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -6174,12 +6174,10 @@
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
boolean allowBackgroundActivityStart) {
assertPackageMatchesCallingUid(callingPackage);
- synchronized (mGlobalLock) {
- return getActivityStartController().startActivitiesInPackage(uid, realCallingPid,
- realCallingUid, callingPackage, callingFeatureId, intents, resolvedTypes,
- resultTo, options, userId, validateIncomingUser, originatingPendingIntent,
- allowBackgroundActivityStart);
- }
+ return getActivityStartController().startActivitiesInPackage(uid, realCallingPid,
+ realCallingUid, callingPackage, callingFeatureId, intents, resolvedTypes,
+ resultTo, options, userId, validateIncomingUser, originatingPendingIntent,
+ allowBackgroundActivityStart);
}
@Override
@@ -6190,13 +6188,11 @@
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
boolean allowBackgroundActivityStart) {
assertPackageMatchesCallingUid(callingPackage);
- synchronized (mGlobalLock) {
- return getActivityStartController().startActivityInPackage(uid, realCallingPid,
- realCallingUid, callingPackage, callingFeatureId, intent, resolvedType,
- resultTo, resultWho, requestCode, startFlags, options, userId, inTask,
- reason, validateIncomingUser, originatingPendingIntent,
- allowBackgroundActivityStart);
- }
+ return getActivityStartController().startActivityInPackage(uid, realCallingPid,
+ realCallingUid, callingPackage, callingFeatureId, intent, resolvedType,
+ resultTo, resultWho, requestCode, startFlags, options, userId, inTask,
+ reason, validateIncomingUser, originatingPendingIntent,
+ allowBackgroundActivityStart);
}
@Override
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 1762b62..c8d9fe0 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -351,13 +351,9 @@
}
private void updateVisibility() {
- // TODO(b/159699383): remove the client controlled check when the insets visibility can be
- // driven by the system UI.
final boolean isClientControlled = mControlTarget != null
&& mControlTarget.isClientControlled();
- mSource.setVisible(mServerVisible
- && ((!isClientControlled && mDisplayContent.inMultiWindowMode())
- || mClientVisible));
+ mSource.setVisible(mServerVisible && (!isClientControlled || mClientVisible));
ProtoLog.d(WM_DEBUG_IME,
"InsetsSource updateVisibility serverVisible: %s clientVisible: %s",
mServerVisible, mClientVisible);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 52fb941..564eecf 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5142,8 +5142,8 @@
}
case WINDOW_STATE_BLAST_SYNC_TIMEOUT: {
synchronized (mGlobalLock) {
- final WindowState ws = (WindowState) msg.obj;
- ws.finishDrawing(null);
+ final WindowState ws = (WindowState) msg.obj;
+ ws.immediatelyNotifyBlastSync();
}
break;
}
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index fbc5afa..46e1bf0 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -428,6 +428,9 @@
try {
callback.onTransactionReady(mSyncId, mergedTransaction);
} catch (RemoteException e) {
+ // If there's an exception when trying to send the mergedTransaction to the client, we
+ // should immediately apply it here so the transactions aren't lost.
+ mergedTransaction.apply();
}
mTransactionCallbacksByPendingSyncId.remove(mSyncId);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 26a1fea..49ef4e4 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2192,7 +2192,7 @@
void removeIfPossible() {
super.removeIfPossible();
removeIfPossible(false /*keepVisibleDeadWindow*/);
- finishDrawing(null);
+ immediatelyNotifyBlastSync();
}
private void removeIfPossible(boolean keepVisibleDeadWindow) {
@@ -5806,7 +5806,7 @@
// client will not render when visibility is GONE. Therefore, call finishDrawing here to
// prevent system server from blocking on a window that will not draw.
if (viewVisibility == View.GONE && mUsingBLASTSyncTransaction) {
- finishDrawing(null);
+ immediatelyNotifyBlastSync();
}
}
@@ -5844,7 +5844,6 @@
return mWinAnimator.finishDrawingLocked(postDrawTransaction);
}
- mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
if (postDrawTransaction != null) {
mBLASTSyncTransaction.merge(postDrawTransaction);
}
@@ -5853,8 +5852,9 @@
return mWinAnimator.finishDrawingLocked(null);
}
- @VisibleForTesting
- void notifyBlastSyncTransaction() {
+ private void notifyBlastSyncTransaction() {
+ mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this);
+
if (!mNotifyBlastOnSurfacePlacement || mWaitingListener == null) {
mNotifyBlastOnSurfacePlacement = false;
return;
@@ -5877,6 +5877,11 @@
mNotifyBlastOnSurfacePlacement = false;
}
+ void immediatelyNotifyBlastSync() {
+ finishDrawing(null);
+ notifyBlastSyncTransaction();
+ }
+
private boolean requestResizeForBlastSync() {
return useBLASTSync() && !mResizeForBlastSyncReported;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
index 7446289..4e2f9a4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
@@ -174,10 +174,10 @@
mService.handlePackageRemoved(TEST_PKG1, TEST_UID1);
// Verify sessions are removed
- verify(sessionFile1).delete();
- verify(sessionFile2, never()).delete();
- verify(sessionFile3, never()).delete();
- verify(sessionFile4).delete();
+ verify(session1).destroy();
+ verify(session2, never()).destroy();
+ verify(session3, never()).destroy();
+ verify(session4).destroy();
assertThat(mUserSessions.size()).isEqualTo(2);
assertThat(mUserSessions.get(sessionId1)).isNull();
@@ -193,9 +193,9 @@
verify(blobMetadata3).removeCommitter(TEST_PKG1, TEST_UID1);
verify(blobMetadata3).removeLeasee(TEST_PKG1, TEST_UID1);
- verify(blobFile1, never()).delete();
- verify(blobFile2).delete();
- verify(blobFile3).delete();
+ verify(blobMetadata1, never()).destroy();
+ verify(blobMetadata2).destroy();
+ verify(blobMetadata3).destroy();
assertThat(mUserBlobs.size()).isEqualTo(1);
assertThat(mUserBlobs.get(blobHandle1)).isNotNull();
@@ -272,9 +272,9 @@
mService.handleIdleMaintenanceLocked();
// Verify stale sessions are removed
- verify(sessionFile1).delete();
- verify(sessionFile2, never()).delete();
- verify(sessionFile3).delete();
+ verify(session1).destroy();
+ verify(session2, never()).destroy();
+ verify(session3).destroy();
assertThat(mUserSessions.size()).isEqualTo(1);
assertThat(mUserSessions.get(sessionId2)).isNotNull();
@@ -317,9 +317,9 @@
mService.handleIdleMaintenanceLocked();
// Verify stale blobs are removed
- verify(blobFile1).delete();
- verify(blobFile2, never()).delete();
- verify(blobFile3).delete();
+ verify(blobMetadata1).destroy();
+ verify(blobMetadata2, never()).destroy();
+ verify(blobMetadata3).destroy();
assertThat(mUserBlobs.size()).isEqualTo(1);
assertThat(mUserBlobs.get(blobHandle2)).isNotNull();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 8607ec6..7538468 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -17,7 +17,6 @@
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
-import android.os.MessageQueue;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.hdmi.HdmiCecController.NativeWrapper;
@@ -53,13 +52,16 @@
private HdmiPortInfo[] mHdmiPortInfo = null;
@Override
- public long nativeInit(HdmiCecController handler, MessageQueue messageQueue) {
- return 1L;
+ public String nativeInit() {
+ return "[class or subclass of IHdmiCec]@Proxy";
}
@Override
+ public void setCallback(HdmiCecController.HdmiCecCallback callback) {}
+
+ @Override
public int nativeSendCecCommand(
- long controllerPtr, int srcAddress, int dstAddress, byte[] body) {
+ int srcAddress, int dstAddress, byte[] body) {
if (body.length == 0) {
return mPollAddressResponse[dstAddress];
} else {
@@ -69,30 +71,30 @@
}
@Override
- public int nativeAddLogicalAddress(long controllerPtr, int logicalAddress) {
+ public int nativeAddLogicalAddress(int logicalAddress) {
return 0;
}
@Override
- public void nativeClearLogicalAddress(long controllerPtr) {}
+ public void nativeClearLogicalAddress() {}
@Override
- public int nativeGetPhysicalAddress(long controllerPtr) {
+ public int nativeGetPhysicalAddress() {
return mMyPhysicalAddress;
}
@Override
- public int nativeGetVersion(long controllerPtr) {
+ public int nativeGetVersion() {
return 0;
}
@Override
- public int nativeGetVendorId(long controllerPtr) {
+ public int nativeGetVendorId() {
return 0;
}
@Override
- public HdmiPortInfo[] nativeGetPortInfos(long controllerPtr) {
+ public HdmiPortInfo[] nativeGetPortInfos() {
if (mHdmiPortInfo == null) {
mHdmiPortInfo = new HdmiPortInfo[1];
mHdmiPortInfo[0] = new HdmiPortInfo(1, 1, 0x1000, true, true, true);
@@ -101,16 +103,16 @@
}
@Override
- public void nativeSetOption(long controllerPtr, int flag, boolean enabled) {}
+ public void nativeSetOption(int flag, boolean enabled) {}
@Override
- public void nativeSetLanguage(long controllerPtr, String language) {}
+ public void nativeSetLanguage(String language) {}
@Override
- public void nativeEnableAudioReturnChannel(long controllerPtr, int port, boolean flag) {}
+ public void nativeEnableAudioReturnChannel(int port, boolean flag) {}
@Override
- public boolean nativeIsConnected(long controllerPtr, int port) {
+ public boolean nativeIsConnected(int port) {
return false;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
index 7ce0c1e..341e209 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java
@@ -729,7 +729,7 @@
// We should be rejected from the second sync since we are already
// in one.
assertEquals(false, bse.addToSyncSet(id2, task));
- finishAndNotifyDrawing(w);
+ w.immediatelyNotifyBlastSync();
assertEquals(true, bse.addToSyncSet(id2, task));
bse.setReady(id2);
}
@@ -753,7 +753,7 @@
// Since we have a window we have to wait for it to draw to finish sync.
verify(transactionListener, never())
.onTransactionReady(anyInt(), any());
- finishAndNotifyDrawing(w);
+ w.immediatelyNotifyBlastSync();
verify(transactionListener)
.onTransactionReady(anyInt(), any());
}
@@ -821,14 +821,14 @@
int id = bse.startSyncSet(transactionListener);
assertEquals(true, bse.addToSyncSet(id, task));
bse.setReady(id);
- finishAndNotifyDrawing(w);
+ w.immediatelyNotifyBlastSync();
// Since we have a child window we still shouldn't be done.
verify(transactionListener, never())
.onTransactionReady(anyInt(), any());
reset(transactionListener);
- finishAndNotifyDrawing(child);
+ child.immediatelyNotifyBlastSync();
// Ah finally! Done
verify(transactionListener)
.onTransactionReady(anyInt(), any());
@@ -1002,20 +1002,15 @@
verify(mockCallback, never()).onTransactionReady(anyInt(), any());
assertTrue(w1.useBLASTSync());
assertTrue(w2.useBLASTSync());
- finishAndNotifyDrawing(w1);
+ w1.immediatelyNotifyBlastSync();
// Even though one Window finished drawing, both windows should still be using blast sync
assertTrue(w1.useBLASTSync());
assertTrue(w2.useBLASTSync());
- finishAndNotifyDrawing(w2);
+ w2.immediatelyNotifyBlastSync();
verify(mockCallback).onTransactionReady(anyInt(), any());
assertFalse(w1.useBLASTSync());
assertFalse(w2.useBLASTSync());
}
-
- private void finishAndNotifyDrawing(WindowState ws) {
- ws.finishDrawing(null);
- ws.notifyBlastSyncTransaction();
- }
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ee14608..8ae1ee9 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -38,6 +38,7 @@
import android.annotation.TestApi;
import android.annotation.WorkerThread;
import android.app.PendingIntent;
+import android.app.role.RoleManager;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
@@ -1885,12 +1886,23 @@
* Returns the unique device ID, for example, the IMEI for GSM and the MEID
* or ESN for CDMA phones. Return null if device ID is not available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
- * privileges (see {@link #hasCarrierPrivileges}) on any active subscription. The profile owner
- * is an app that owns a managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
- * access is deprecated and will be removed in a future release.
+ * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+ * restrictions, and apps are recommended to use resettable identifiers (see <a
+ * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
+ * the following requirements is met:
+ * <ul>
+ * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+ * is a privileged permission that can only be granted to apps preloaded on the device.
+ * <li>If the calling app is the device or profile owner and has been granted the
+ * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
+ * owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any
+ * active subscription.
+ * <li>If the calling app is the default SMS role holder (see {@link
+ * RoleManager#isRoleHeld(String)}).
+ * </ul>
*
* <p>If the calling app does not meet one of these requirements then this method will behave
* as follows:
@@ -1927,12 +1939,23 @@
* Returns the unique device ID of a subscription, for example, the IMEI for
* GSM and the MEID for CDMA phones. Return null if device ID is not available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
- * privileges (see {@link #hasCarrierPrivileges}) on any active subscription. The profile owner
- * is an app that owns a managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
- * access is deprecated and will be removed in a future release.
+ * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+ * restrictions, and apps are recommended to use resettable identifiers (see <a
+ * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
+ * the following requirements is met:
+ * <ul>
+ * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+ * is a privileged permission that can only be granted to apps preloaded on the device.
+ * <li>If the calling app is the device or profile owner and has been granted the
+ * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
+ * owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any
+ * active subscription.
+ * <li>If the calling app is the default SMS role holder (see {@link
+ * RoleManager#isRoleHeld(String)}).
+ * </ul>
*
* <p>If the calling app does not meet one of these requirements then this method will behave
* as follows:
@@ -1985,18 +2008,23 @@
* Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not
* available.
*
- * <p>This API requires one of the following:
+ * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+ * restrictions, and apps are recommended to use resettable identifiers (see <a
+ * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
+ * the following requirements is met:
* <ul>
- * <li>The caller holds the READ_PRIVILEGED_PHONE_STATE permission.</li>
- * <li>If the caller is the device or profile owner, the caller holds the
- * {@link Manifest.permission#READ_PHONE_STATE} permission.</li>
- * <li>The caller has carrier privileges (see {@link #hasCarrierPrivileges()} on any
- * active subscription.</li>
- * <li>The caller is the default SMS app for the device.</li>
+ * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+ * is a privileged permission that can only be granted to apps preloaded on the device.
+ * <li>If the calling app is the device or profile owner and has been granted the
+ * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
+ * owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any
+ * active subscription.
+ * <li>If the calling app is the default SMS role holder (see {@link
+ * RoleManager#isRoleHeld(String)}).
* </ul>
- * <p>The profile owner is an app that owns a managed profile on the device; for more details
- * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
- * Access by profile owners is deprecated and will be removed in a future release.
*
* <p>If the calling app does not meet one of these requirements then this method will behave
* as follows:
@@ -2058,12 +2086,23 @@
/**
* Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
- * privileges (see {@link #hasCarrierPrivileges}) on any active subscription. The profile owner
- * is an app that owns a managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
- * access is deprecated and will be removed in a future release.
+ * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+ * restrictions, and apps are recommended to use resettable identifiers (see <a
+ * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
+ * the following requirements is met:
+ * <ul>
+ * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+ * is a privileged permission that can only be granted to apps preloaded on the device.
+ * <li>If the calling app is the device or profile owner and has been granted the
+ * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
+ * owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any
+ * active subscription.
+ * <li>If the calling app is the default SMS role holder (see {@link
+ * RoleManager#isRoleHeld(String)}).
+ * </ul>
*
* <p>If the calling app does not meet one of these requirements then this method will behave
* as follows:
@@ -2085,12 +2124,23 @@
/**
* Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
- * privileges (see {@link #hasCarrierPrivileges}) on any active subscription. The profile owner
- * is an app that owns a managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
- * access is deprecated and will be removed in a future release.
+ * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+ * restrictions, and apps are recommended to use resettable identifiers (see <a
+ * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
+ * the following requirements is met:
+ * <ul>
+ * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+ * is a privileged permission that can only be granted to apps preloaded on the device.
+ * <li>If the calling app is the device or profile owner and has been granted the
+ * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
+ * owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}) on any
+ * active subscription.
+ * <li>If the calling app is the default SMS role holder (see {@link
+ * RoleManager#isRoleHeld(String)}).
+ * </ul>
*
* <p>If the calling app does not meet one of these requirements then this method will behave
* as follows:
@@ -2158,12 +2208,25 @@
/**
* Returns the Network Access Identifier (NAI). Return null if NAI is not available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
- * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
- * managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
- * access is deprecated and will be removed in a future release.
+ * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+ * restrictions, and apps are recommended to use resettable identifiers (see <a
+ * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
+ * the following requirements is met:
+ * <ul>
+ * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+ * is a privileged permission that can only be granted to apps preloaded on the device.
+ * <li>If the calling app is the device or profile owner and has been granted the
+ * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
+ * owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * <li>If the calling app is the default SMS role holder (see {@link
+ * RoleManager#isRoleHeld(String)}).
+ * </ul>
+ *
+ * <p>If the calling app does not meet one of these requirements then this method will behave
+ * as follows:
*
* <ul>
* <li>If the calling app's target SDK is API level 28 or lower and the app has the
@@ -2182,12 +2245,25 @@
/**
* Returns the NAI. Return null if NAI is not available.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
- * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
- * managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
- * access is deprecated and will be removed in a future release.
+ * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+ * restrictions, and apps are recommended to use resettable identifiers (see <a
+ * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
+ * the following requirements is met:
+ * <ul>
+ * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+ * is a privileged permission that can only be granted to apps preloaded on the device.
+ * <li>If the calling app is the device or profile owner and has been granted the
+ * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
+ * owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * <li>If the calling app is the default SMS role holder (see {@link
+ * RoleManager#isRoleHeld(String)}).
+ * </ul>
+ *
+ * <p>If the calling app does not meet one of these requirements then this method will behave
+ * as follows:
*
* <ul>
* <li>If the calling app's target SDK is API level 28 or lower and the app has the
@@ -3775,12 +3851,22 @@
* Returns the serial number of the SIM, if applicable. Return null if it is
* unavailable.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
- * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
- * managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
- * access is deprecated and will be removed in a future release.
+ * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+ * restrictions, and apps are recommended to use resettable identifiers (see <a
+ * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
+ * the following requirements is met:
+ * <ul>
+ * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+ * is a privileged permission that can only be granted to apps preloaded on the device.
+ * <li>If the calling app is the device or profile owner and has been granted the
+ * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
+ * owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * <li>If the calling app is the default SMS role holder (see {@link
+ * RoleManager#isRoleHeld(String)}).
+ * </ul>
*
* <p>If the calling app does not meet one of these requirements then this method will behave
* as follows:
@@ -3803,12 +3889,22 @@
* Returns the serial number for the given subscription, if applicable. Return null if it is
* unavailable.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
- * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
- * managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
- * access is deprecated and will be removed in a future release.
+ * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+ * restrictions, and apps are recommended to use resettable identifiers (see <a
+ * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
+ * the following requirements is met:
+ * <ul>
+ * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+ * is a privileged permission that can only be granted to apps preloaded on the device.
+ * <li>If the calling app is the device or profile owner and has been granted the
+ * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
+ * owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * <li>If the calling app is the default SMS role holder (see {@link
+ * RoleManager#isRoleHeld(String)}).
+ * </ul>
*
* <p>If the calling app does not meet one of these requirements then this method will behave
* as follows:
@@ -4047,12 +4143,22 @@
* Returns the unique subscriber ID, for example, the IMSI for a GSM phone.
* Return null if it is unavailable.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
- * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
- * managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
- * access is deprecated and will be removed in a future release.
+ * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+ * restrictions, and apps are recommended to use resettable identifiers (see <a
+ * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
+ * the following requirements is met:
+ * <ul>
+ * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+ * is a privileged permission that can only be granted to apps preloaded on the device.
+ * <li>If the calling app is the device or profile owner and has been granted the
+ * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
+ * owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * <li>If the calling app is the default SMS role holder (see {@link
+ * RoleManager#isRoleHeld(String)}).
+ * </ul>
*
* <p>If the calling app does not meet one of these requirements then this method will behave
* as follows:
@@ -4076,12 +4182,22 @@
* for a subscription.
* Return null if it is unavailable.
*
- * <p>Requires Permission: READ_PRIVILEGED_PHONE_STATE, for the calling app to be the device or
- * profile owner and have the READ_PHONE_STATE permission, or that the calling app has carrier
- * privileges (see {@link #hasCarrierPrivileges}). The profile owner is an app that owns a
- * managed profile on the device; for more details see <a
- * href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile owner
- * access is deprecated and will be removed in a future release.
+ * <p>Starting with API level 29, persistent device identifiers are guarded behind additional
+ * restrictions, and apps are recommended to use resettable identifiers (see <a
+ * href="c"> Best practices for unique identifiers</a>). This method can be invoked if one of
+ * the following requirements is met:
+ * <ul>
+ * <li>If the calling app has been granted the READ_PRIVILEGED_PHONE_STATE permission; this
+ * is a privileged permission that can only be granted to apps preloaded on the device.
+ * <li>If the calling app is the device or profile owner and has been granted the
+ * {@link Manifest.permission#READ_PHONE_STATE} permission. The profile owner is an app that
+ * owns a managed profile on the device; for more details see <a
+ * href="https://developer.android.com/work/managed-profiles">Work profiles</a>.
+ * Profile owner access is deprecated and will be removed in a future release.
+ * <li>If the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * <li>If the calling app is the default SMS role holder (see {@link
+ * RoleManager#isRoleHeld(String)}).
+ * </ul>
*
* <p>If the calling app does not meet one of these requirements then this method will behave
* as follows: